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
23function _typeof(obj) {
24 "@babel/helpers - typeof";
25
26 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
27 return typeof obj;
28 } : function (obj) {
29 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
30 }, _typeof(obj);
31}
32function _classCallCheck(instance, Constructor) {
33 if (!(instance instanceof Constructor)) {
34 throw new TypeError("Cannot call a class as a function");
35 }
36}
37function _defineProperties(target, props) {
38 for (var i = 0; i < props.length; i++) {
39 var descriptor = props[i];
40 descriptor.enumerable = descriptor.enumerable || false;
41 descriptor.configurable = true;
42 if ("value" in descriptor) descriptor.writable = true;
43 Object.defineProperty(target, descriptor.key, descriptor);
44 }
45}
46function _createClass(Constructor, protoProps, staticProps) {
47 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
48 if (staticProps) _defineProperties(Constructor, staticProps);
49 Object.defineProperty(Constructor, "prototype", {
50 writable: false
51 });
52 return Constructor;
53}
54function _defineProperty$1(obj, key, value) {
55 if (key in obj) {
56 Object.defineProperty(obj, key, {
57 value: value,
58 enumerable: true,
59 configurable: true,
60 writable: true
61 });
62 } else {
63 obj[key] = value;
64 }
65 return obj;
66}
67function _slicedToArray(arr, i) {
68 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
69}
70function _arrayWithHoles(arr) {
71 if (Array.isArray(arr)) return arr;
72}
73function _iterableToArrayLimit(arr, i) {
74 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
75 if (_i == null) return;
76 var _arr = [];
77 var _n = true;
78 var _d = false;
79 var _s, _e;
80 try {
81 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
82 _arr.push(_s.value);
83 if (i && _arr.length === i) break;
84 }
85 } catch (err) {
86 _d = true;
87 _e = err;
88 } finally {
89 try {
90 if (!_n && _i["return"] != null) _i["return"]();
91 } finally {
92 if (_d) throw _e;
93 }
94 }
95 return _arr;
96}
97function _unsupportedIterableToArray(o, minLen) {
98 if (!o) return;
99 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
100 var n = Object.prototype.toString.call(o).slice(8, -1);
101 if (n === "Object" && o.constructor) n = o.constructor.name;
102 if (n === "Map" || n === "Set") return Array.from(o);
103 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
104}
105function _arrayLikeToArray(arr, len) {
106 if (len == null || len > arr.length) len = arr.length;
107 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
108 return arr2;
109}
110function _nonIterableRest() {
111 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
112}
113function _createForOfIteratorHelper(o, allowArrayLike) {
114 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
115 if (!it) {
116 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
117 if (it) o = it;
118 var i = 0;
119 var F = function () {};
120 return {
121 s: F,
122 n: function () {
123 if (i >= o.length) return {
124 done: true
125 };
126 return {
127 done: false,
128 value: o[i++]
129 };
130 },
131 e: function (e) {
132 throw e;
133 },
134 f: F
135 };
136 }
137 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
138 }
139 var normalCompletion = true,
140 didErr = false,
141 err;
142 return {
143 s: function () {
144 it = it.call(o);
145 },
146 n: function () {
147 var step = it.next();
148 normalCompletion = step.done;
149 return step;
150 },
151 e: function (e) {
152 didErr = true;
153 err = e;
154 },
155 f: function () {
156 try {
157 if (!normalCompletion && it.return != null) it.return();
158 } finally {
159 if (didErr) throw err;
160 }
161 }
162 };
163}
164
165var _window = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
166
167var navigator = _window ? _window.navigator : null;
168_window ? _window.document : null;
169var typeofstr = _typeof('');
170var typeofobj = _typeof({});
171var typeoffn = _typeof(function () {});
172var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
173var instanceStr = function instanceStr(obj) {
174 return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null;
175};
176
177var string = function string(obj) {
178 return obj != null && _typeof(obj) == typeofstr;
179};
180var fn$6 = function fn(obj) {
181 return obj != null && _typeof(obj) === typeoffn;
182};
183var array = function array(obj) {
184 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
185};
186var plainObject = function plainObject(obj) {
187 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
188};
189var object = function object(obj) {
190 return obj != null && _typeof(obj) === typeofobj;
191};
192var number$1 = function number(obj) {
193 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
194};
195var integer = function integer(obj) {
196 return number$1(obj) && Math.floor(obj) === obj;
197};
198var htmlElement = function htmlElement(obj) {
199 if ('undefined' === typeofhtmlele) {
200 return undefined;
201 } else {
202 return null != obj && obj instanceof HTMLElement;
203 }
204};
205var elementOrCollection = function elementOrCollection(obj) {
206 return element(obj) || collection(obj);
207};
208var element = function element(obj) {
209 return instanceStr(obj) === 'collection' && obj._private.single;
210};
211var collection = function collection(obj) {
212 return instanceStr(obj) === 'collection' && !obj._private.single;
213};
214var core = function core(obj) {
215 return instanceStr(obj) === 'core';
216};
217var stylesheet = function stylesheet(obj) {
218 return instanceStr(obj) === 'stylesheet';
219};
220var event = function event(obj) {
221 return instanceStr(obj) === 'event';
222};
223var emptyString = function emptyString(obj) {
224 if (obj === undefined || obj === null) {
225 // null is empty
226 return true;
227 } else if (obj === '' || obj.match(/^\s+$/)) {
228 return true; // empty string is empty
229 }
230
231 return false; // otherwise, we don't know what we've got
232};
233var domElement = function domElement(obj) {
234 if (typeof HTMLElement === 'undefined') {
235 return false; // we're not in a browser so it doesn't matter
236 } else {
237 return obj instanceof HTMLElement;
238 }
239};
240var boundingBox = function boundingBox(obj) {
241 return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2);
242};
243var promise = function promise(obj) {
244 return object(obj) && fn$6(obj.then);
245};
246var ms = function ms() {
247 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
248}; // probably a better way to detect this...
249
250var memoize$1 = function memoize(fn, keyFn) {
251 if (!keyFn) {
252 keyFn = function keyFn() {
253 if (arguments.length === 1) {
254 return arguments[0];
255 } else if (arguments.length === 0) {
256 return 'undefined';
257 }
258 var args = [];
259 for (var i = 0; i < arguments.length; i++) {
260 args.push(arguments[i]);
261 }
262 return args.join('$');
263 };
264 }
265 var memoizedFn = function memoizedFn() {
266 var self = this;
267 var args = arguments;
268 var ret;
269 var k = keyFn.apply(self, args);
270 var cache = memoizedFn.cache;
271 if (!(ret = cache[k])) {
272 ret = cache[k] = fn.apply(self, args);
273 }
274 return ret;
275 };
276 memoizedFn.cache = {};
277 return memoizedFn;
278};
279
280var camel2dash = memoize$1(function (str) {
281 return str.replace(/([A-Z])/g, function (v) {
282 return '-' + v.toLowerCase();
283 });
284});
285var dash2camel = memoize$1(function (str) {
286 return str.replace(/(-\w)/g, function (v) {
287 return v[1].toUpperCase();
288 });
289});
290var prependCamel = memoize$1(function (prefix, str) {
291 return prefix + str[0].toUpperCase() + str.substring(1);
292}, function (prefix, str) {
293 return prefix + '$' + str;
294});
295var capitalize = function capitalize(str) {
296 if (emptyString(str)) {
297 return str;
298 }
299 return str.charAt(0).toUpperCase() + str.substring(1);
300};
301
302var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
303var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)';
304var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)';
305var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)';
306var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)';
307var hex3 = '\\#[0-9a-fA-F]{3}';
308var hex6 = '\\#[0-9a-fA-F]{6}';
309
310var ascending = function ascending(a, b) {
311 if (a < b) {
312 return -1;
313 } else if (a > b) {
314 return 1;
315 } else {
316 return 0;
317 }
318};
319var descending = function descending(a, b) {
320 return -1 * ascending(a, b);
321};
322
323var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
324 var args = arguments;
325 for (var i = 1; i < args.length; i++) {
326 var obj = args[i];
327 if (obj == null) {
328 continue;
329 }
330 var keys = Object.keys(obj);
331 for (var j = 0; j < keys.length; j++) {
332 var k = keys[j];
333 tgt[k] = obj[k];
334 }
335 }
336 return tgt;
337};
338
339// get [r, g, b] from #abc or #aabbcc
340var hex2tuple = function hex2tuple(hex) {
341 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
342 return;
343 }
344 var shortHex = hex.length === 4;
345 var r, g, b;
346 var base = 16;
347 if (shortHex) {
348 r = parseInt(hex[1] + hex[1], base);
349 g = parseInt(hex[2] + hex[2], base);
350 b = parseInt(hex[3] + hex[3], base);
351 } else {
352 r = parseInt(hex[1] + hex[2], base);
353 g = parseInt(hex[3] + hex[4], base);
354 b = parseInt(hex[5] + hex[6], base);
355 }
356 return [r, g, b];
357};
358
359// get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
360var hsl2tuple = function hsl2tuple(hsl) {
361 var ret;
362 var h, s, l, a, r, g, b;
363 function hue2rgb(p, q, t) {
364 if (t < 0) t += 1;
365 if (t > 1) t -= 1;
366 if (t < 1 / 6) return p + (q - p) * 6 * t;
367 if (t < 1 / 2) return q;
368 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
369 return p;
370 }
371 var m = new RegExp('^' + hsla + '$').exec(hsl);
372 if (m) {
373 // get hue
374 h = parseInt(m[1]);
375 if (h < 0) {
376 h = (360 - -1 * h % 360) % 360;
377 } else if (h > 360) {
378 h = h % 360;
379 }
380 h /= 360; // normalise on [0, 1]
381
382 s = parseFloat(m[2]);
383 if (s < 0 || s > 100) {
384 return;
385 } // saturation is [0, 100]
386 s = s / 100; // normalise on [0, 1]
387
388 l = parseFloat(m[3]);
389 if (l < 0 || l > 100) {
390 return;
391 } // lightness is [0, 100]
392 l = l / 100; // normalise on [0, 1]
393
394 a = m[4];
395 if (a !== undefined) {
396 a = parseFloat(a);
397 if (a < 0 || a > 1) {
398 return;
399 } // alpha is [0, 1]
400 }
401
402 // now, convert to rgb
403 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
404 if (s === 0) {
405 r = g = b = Math.round(l * 255); // achromatic
406 } else {
407 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
408 var p = 2 * l - q;
409 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
410 g = Math.round(255 * hue2rgb(p, q, h));
411 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
412 }
413 ret = [r, g, b, a];
414 }
415 return ret;
416};
417
418// get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
419var rgb2tuple = function rgb2tuple(rgb) {
420 var ret;
421 var m = new RegExp('^' + rgba + '$').exec(rgb);
422 if (m) {
423 ret = [];
424 var isPct = [];
425 for (var i = 1; i <= 3; i++) {
426 var channel = m[i];
427 if (channel[channel.length - 1] === '%') {
428 isPct[i] = true;
429 }
430 channel = parseFloat(channel);
431 if (isPct[i]) {
432 channel = channel / 100 * 255; // normalise to [0, 255]
433 }
434
435 if (channel < 0 || channel > 255) {
436 return;
437 } // invalid channel value
438
439 ret.push(Math.floor(channel));
440 }
441 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
442 var allArePct = isPct[1] && isPct[2] && isPct[3];
443 if (atLeastOneIsPct && !allArePct) {
444 return;
445 } // must all be percent values if one is
446
447 var alpha = m[4];
448 if (alpha !== undefined) {
449 alpha = parseFloat(alpha);
450 if (alpha < 0 || alpha > 1) {
451 return;
452 } // invalid alpha value
453
454 ret.push(alpha);
455 }
456 }
457 return ret;
458};
459var colorname2tuple = function colorname2tuple(color) {
460 return colors[color.toLowerCase()];
461};
462var color2tuple = function color2tuple(color) {
463 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
464};
465var colors = {
466 // special colour names
467 transparent: [0, 0, 0, 0],
468 // NB alpha === 0
469
470 // regular colours
471 aliceblue: [240, 248, 255],
472 antiquewhite: [250, 235, 215],
473 aqua: [0, 255, 255],
474 aquamarine: [127, 255, 212],
475 azure: [240, 255, 255],
476 beige: [245, 245, 220],
477 bisque: [255, 228, 196],
478 black: [0, 0, 0],
479 blanchedalmond: [255, 235, 205],
480 blue: [0, 0, 255],
481 blueviolet: [138, 43, 226],
482 brown: [165, 42, 42],
483 burlywood: [222, 184, 135],
484 cadetblue: [95, 158, 160],
485 chartreuse: [127, 255, 0],
486 chocolate: [210, 105, 30],
487 coral: [255, 127, 80],
488 cornflowerblue: [100, 149, 237],
489 cornsilk: [255, 248, 220],
490 crimson: [220, 20, 60],
491 cyan: [0, 255, 255],
492 darkblue: [0, 0, 139],
493 darkcyan: [0, 139, 139],
494 darkgoldenrod: [184, 134, 11],
495 darkgray: [169, 169, 169],
496 darkgreen: [0, 100, 0],
497 darkgrey: [169, 169, 169],
498 darkkhaki: [189, 183, 107],
499 darkmagenta: [139, 0, 139],
500 darkolivegreen: [85, 107, 47],
501 darkorange: [255, 140, 0],
502 darkorchid: [153, 50, 204],
503 darkred: [139, 0, 0],
504 darksalmon: [233, 150, 122],
505 darkseagreen: [143, 188, 143],
506 darkslateblue: [72, 61, 139],
507 darkslategray: [47, 79, 79],
508 darkslategrey: [47, 79, 79],
509 darkturquoise: [0, 206, 209],
510 darkviolet: [148, 0, 211],
511 deeppink: [255, 20, 147],
512 deepskyblue: [0, 191, 255],
513 dimgray: [105, 105, 105],
514 dimgrey: [105, 105, 105],
515 dodgerblue: [30, 144, 255],
516 firebrick: [178, 34, 34],
517 floralwhite: [255, 250, 240],
518 forestgreen: [34, 139, 34],
519 fuchsia: [255, 0, 255],
520 gainsboro: [220, 220, 220],
521 ghostwhite: [248, 248, 255],
522 gold: [255, 215, 0],
523 goldenrod: [218, 165, 32],
524 gray: [128, 128, 128],
525 grey: [128, 128, 128],
526 green: [0, 128, 0],
527 greenyellow: [173, 255, 47],
528 honeydew: [240, 255, 240],
529 hotpink: [255, 105, 180],
530 indianred: [205, 92, 92],
531 indigo: [75, 0, 130],
532 ivory: [255, 255, 240],
533 khaki: [240, 230, 140],
534 lavender: [230, 230, 250],
535 lavenderblush: [255, 240, 245],
536 lawngreen: [124, 252, 0],
537 lemonchiffon: [255, 250, 205],
538 lightblue: [173, 216, 230],
539 lightcoral: [240, 128, 128],
540 lightcyan: [224, 255, 255],
541 lightgoldenrodyellow: [250, 250, 210],
542 lightgray: [211, 211, 211],
543 lightgreen: [144, 238, 144],
544 lightgrey: [211, 211, 211],
545 lightpink: [255, 182, 193],
546 lightsalmon: [255, 160, 122],
547 lightseagreen: [32, 178, 170],
548 lightskyblue: [135, 206, 250],
549 lightslategray: [119, 136, 153],
550 lightslategrey: [119, 136, 153],
551 lightsteelblue: [176, 196, 222],
552 lightyellow: [255, 255, 224],
553 lime: [0, 255, 0],
554 limegreen: [50, 205, 50],
555 linen: [250, 240, 230],
556 magenta: [255, 0, 255],
557 maroon: [128, 0, 0],
558 mediumaquamarine: [102, 205, 170],
559 mediumblue: [0, 0, 205],
560 mediumorchid: [186, 85, 211],
561 mediumpurple: [147, 112, 219],
562 mediumseagreen: [60, 179, 113],
563 mediumslateblue: [123, 104, 238],
564 mediumspringgreen: [0, 250, 154],
565 mediumturquoise: [72, 209, 204],
566 mediumvioletred: [199, 21, 133],
567 midnightblue: [25, 25, 112],
568 mintcream: [245, 255, 250],
569 mistyrose: [255, 228, 225],
570 moccasin: [255, 228, 181],
571 navajowhite: [255, 222, 173],
572 navy: [0, 0, 128],
573 oldlace: [253, 245, 230],
574 olive: [128, 128, 0],
575 olivedrab: [107, 142, 35],
576 orange: [255, 165, 0],
577 orangered: [255, 69, 0],
578 orchid: [218, 112, 214],
579 palegoldenrod: [238, 232, 170],
580 palegreen: [152, 251, 152],
581 paleturquoise: [175, 238, 238],
582 palevioletred: [219, 112, 147],
583 papayawhip: [255, 239, 213],
584 peachpuff: [255, 218, 185],
585 peru: [205, 133, 63],
586 pink: [255, 192, 203],
587 plum: [221, 160, 221],
588 powderblue: [176, 224, 230],
589 purple: [128, 0, 128],
590 red: [255, 0, 0],
591 rosybrown: [188, 143, 143],
592 royalblue: [65, 105, 225],
593 saddlebrown: [139, 69, 19],
594 salmon: [250, 128, 114],
595 sandybrown: [244, 164, 96],
596 seagreen: [46, 139, 87],
597 seashell: [255, 245, 238],
598 sienna: [160, 82, 45],
599 silver: [192, 192, 192],
600 skyblue: [135, 206, 235],
601 slateblue: [106, 90, 205],
602 slategray: [112, 128, 144],
603 slategrey: [112, 128, 144],
604 snow: [255, 250, 250],
605 springgreen: [0, 255, 127],
606 steelblue: [70, 130, 180],
607 tan: [210, 180, 140],
608 teal: [0, 128, 128],
609 thistle: [216, 191, 216],
610 tomato: [255, 99, 71],
611 turquoise: [64, 224, 208],
612 violet: [238, 130, 238],
613 wheat: [245, 222, 179],
614 white: [255, 255, 255],
615 whitesmoke: [245, 245, 245],
616 yellow: [255, 255, 0],
617 yellowgreen: [154, 205, 50]
618};
619
620// sets the value in a map (map may not be built)
621var setMap = function setMap(options) {
622 var obj = options.map;
623 var keys = options.keys;
624 var l = keys.length;
625 for (var i = 0; i < l; i++) {
626 var key = keys[i];
627 if (plainObject(key)) {
628 throw Error('Tried to set map with object key');
629 }
630 if (i < keys.length - 1) {
631 // extend the map if necessary
632 if (obj[key] == null) {
633 obj[key] = {};
634 }
635 obj = obj[key];
636 } else {
637 // set the value
638 obj[key] = options.value;
639 }
640 }
641};
642
643// gets the value in a map even if it's not built in places
644var getMap = function getMap(options) {
645 var obj = options.map;
646 var keys = options.keys;
647 var l = keys.length;
648 for (var i = 0; i < l; i++) {
649 var key = keys[i];
650 if (plainObject(key)) {
651 throw Error('Tried to get map with object key');
652 }
653 obj = obj[key];
654 if (obj == null) {
655 return obj;
656 }
657 }
658 return obj;
659};
660
661/**
662 * Checks if `value` is the
663 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
664 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
665 *
666 * @static
667 * @memberOf _
668 * @since 0.1.0
669 * @category Lang
670 * @param {*} value The value to check.
671 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
672 * @example
673 *
674 * _.isObject({});
675 * // => true
676 *
677 * _.isObject([1, 2, 3]);
678 * // => true
679 *
680 * _.isObject(_.noop);
681 * // => true
682 *
683 * _.isObject(null);
684 * // => false
685 */
686function isObject(value) {
687 var type = typeof value;
688 return value != null && (type == 'object' || type == 'function');
689}
690
691var isObject_1 = isObject;
692
693var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
694
695function createCommonjsModule(fn, module) {
696 return module = { exports: {} }, fn(module, module.exports), module.exports;
697}
698
699/** Detect free variable `global` from Node.js. */
700var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
701
702var _freeGlobal = freeGlobal;
703
704/** Detect free variable `self`. */
705var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
706
707/** Used as a reference to the global object. */
708var root = _freeGlobal || freeSelf || Function('return this')();
709
710var _root = root;
711
712/**
713 * Gets the timestamp of the number of milliseconds that have elapsed since
714 * the Unix epoch (1 January 1970 00:00:00 UTC).
715 *
716 * @static
717 * @memberOf _
718 * @since 2.4.0
719 * @category Date
720 * @returns {number} Returns the timestamp.
721 * @example
722 *
723 * _.defer(function(stamp) {
724 * console.log(_.now() - stamp);
725 * }, _.now());
726 * // => Logs the number of milliseconds it took for the deferred invocation.
727 */
728var now = function() {
729 return _root.Date.now();
730};
731
732var now_1 = now;
733
734/** Used to match a single whitespace character. */
735var reWhitespace = /\s/;
736
737/**
738 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
739 * character of `string`.
740 *
741 * @private
742 * @param {string} string The string to inspect.
743 * @returns {number} Returns the index of the last non-whitespace character.
744 */
745function trimmedEndIndex(string) {
746 var index = string.length;
747
748 while (index-- && reWhitespace.test(string.charAt(index))) {}
749 return index;
750}
751
752var _trimmedEndIndex = trimmedEndIndex;
753
754/** Used to match leading whitespace. */
755var reTrimStart = /^\s+/;
756
757/**
758 * The base implementation of `_.trim`.
759 *
760 * @private
761 * @param {string} string The string to trim.
762 * @returns {string} Returns the trimmed string.
763 */
764function baseTrim(string) {
765 return string
766 ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '')
767 : string;
768}
769
770var _baseTrim = baseTrim;
771
772/** Built-in value references. */
773var Symbol$1 = _root.Symbol;
774
775var _Symbol = Symbol$1;
776
777/** Used for built-in method references. */
778var objectProto$5 = Object.prototype;
779
780/** Used to check objects for own properties. */
781var hasOwnProperty$4 = objectProto$5.hasOwnProperty;
782
783/**
784 * Used to resolve the
785 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
786 * of values.
787 */
788var nativeObjectToString$1 = objectProto$5.toString;
789
790/** Built-in value references. */
791var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
792
793/**
794 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
795 *
796 * @private
797 * @param {*} value The value to query.
798 * @returns {string} Returns the raw `toStringTag`.
799 */
800function getRawTag(value) {
801 var isOwn = hasOwnProperty$4.call(value, symToStringTag$1),
802 tag = value[symToStringTag$1];
803
804 try {
805 value[symToStringTag$1] = undefined;
806 var unmasked = true;
807 } catch (e) {}
808
809 var result = nativeObjectToString$1.call(value);
810 if (unmasked) {
811 if (isOwn) {
812 value[symToStringTag$1] = tag;
813 } else {
814 delete value[symToStringTag$1];
815 }
816 }
817 return result;
818}
819
820var _getRawTag = getRawTag;
821
822/** Used for built-in method references. */
823var objectProto$4 = Object.prototype;
824
825/**
826 * Used to resolve the
827 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
828 * of values.
829 */
830var nativeObjectToString = objectProto$4.toString;
831
832/**
833 * Converts `value` to a string using `Object.prototype.toString`.
834 *
835 * @private
836 * @param {*} value The value to convert.
837 * @returns {string} Returns the converted string.
838 */
839function objectToString(value) {
840 return nativeObjectToString.call(value);
841}
842
843var _objectToString = objectToString;
844
845/** `Object#toString` result references. */
846var nullTag = '[object Null]',
847 undefinedTag = '[object Undefined]';
848
849/** Built-in value references. */
850var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
851
852/**
853 * The base implementation of `getTag` without fallbacks for buggy environments.
854 *
855 * @private
856 * @param {*} value The value to query.
857 * @returns {string} Returns the `toStringTag`.
858 */
859function baseGetTag(value) {
860 if (value == null) {
861 return value === undefined ? undefinedTag : nullTag;
862 }
863 return (symToStringTag && symToStringTag in Object(value))
864 ? _getRawTag(value)
865 : _objectToString(value);
866}
867
868var _baseGetTag = baseGetTag;
869
870/**
871 * Checks if `value` is object-like. A value is object-like if it's not `null`
872 * and has a `typeof` result of "object".
873 *
874 * @static
875 * @memberOf _
876 * @since 4.0.0
877 * @category Lang
878 * @param {*} value The value to check.
879 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
880 * @example
881 *
882 * _.isObjectLike({});
883 * // => true
884 *
885 * _.isObjectLike([1, 2, 3]);
886 * // => true
887 *
888 * _.isObjectLike(_.noop);
889 * // => false
890 *
891 * _.isObjectLike(null);
892 * // => false
893 */
894function isObjectLike(value) {
895 return value != null && typeof value == 'object';
896}
897
898var isObjectLike_1 = isObjectLike;
899
900/** `Object#toString` result references. */
901var symbolTag = '[object Symbol]';
902
903/**
904 * Checks if `value` is classified as a `Symbol` primitive or object.
905 *
906 * @static
907 * @memberOf _
908 * @since 4.0.0
909 * @category Lang
910 * @param {*} value The value to check.
911 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
912 * @example
913 *
914 * _.isSymbol(Symbol.iterator);
915 * // => true
916 *
917 * _.isSymbol('abc');
918 * // => false
919 */
920function isSymbol(value) {
921 return typeof value == 'symbol' ||
922 (isObjectLike_1(value) && _baseGetTag(value) == symbolTag);
923}
924
925var isSymbol_1 = isSymbol;
926
927/** Used as references for various `Number` constants. */
928var NAN = 0 / 0;
929
930/** Used to detect bad signed hexadecimal string values. */
931var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
932
933/** Used to detect binary string values. */
934var reIsBinary = /^0b[01]+$/i;
935
936/** Used to detect octal string values. */
937var reIsOctal = /^0o[0-7]+$/i;
938
939/** Built-in method references without a dependency on `root`. */
940var freeParseInt = parseInt;
941
942/**
943 * Converts `value` to a number.
944 *
945 * @static
946 * @memberOf _
947 * @since 4.0.0
948 * @category Lang
949 * @param {*} value The value to process.
950 * @returns {number} Returns the number.
951 * @example
952 *
953 * _.toNumber(3.2);
954 * // => 3.2
955 *
956 * _.toNumber(Number.MIN_VALUE);
957 * // => 5e-324
958 *
959 * _.toNumber(Infinity);
960 * // => Infinity
961 *
962 * _.toNumber('3.2');
963 * // => 3.2
964 */
965function toNumber(value) {
966 if (typeof value == 'number') {
967 return value;
968 }
969 if (isSymbol_1(value)) {
970 return NAN;
971 }
972 if (isObject_1(value)) {
973 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
974 value = isObject_1(other) ? (other + '') : other;
975 }
976 if (typeof value != 'string') {
977 return value === 0 ? value : +value;
978 }
979 value = _baseTrim(value);
980 var isBinary = reIsBinary.test(value);
981 return (isBinary || reIsOctal.test(value))
982 ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
983 : (reIsBadHex.test(value) ? NAN : +value);
984}
985
986var toNumber_1 = toNumber;
987
988/** Error message constants. */
989var FUNC_ERROR_TEXT$1 = 'Expected a function';
990
991/* Built-in method references for those with the same name as other `lodash` methods. */
992var nativeMax = Math.max,
993 nativeMin = Math.min;
994
995/**
996 * Creates a debounced function that delays invoking `func` until after `wait`
997 * milliseconds have elapsed since the last time the debounced function was
998 * invoked. The debounced function comes with a `cancel` method to cancel
999 * delayed `func` invocations and a `flush` method to immediately invoke them.
1000 * Provide `options` to indicate whether `func` should be invoked on the
1001 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
1002 * with the last arguments provided to the debounced function. Subsequent
1003 * calls to the debounced function return the result of the last `func`
1004 * invocation.
1005 *
1006 * **Note:** If `leading` and `trailing` options are `true`, `func` is
1007 * invoked on the trailing edge of the timeout only if the debounced function
1008 * is invoked more than once during the `wait` timeout.
1009 *
1010 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
1011 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
1012 *
1013 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
1014 * for details over the differences between `_.debounce` and `_.throttle`.
1015 *
1016 * @static
1017 * @memberOf _
1018 * @since 0.1.0
1019 * @category Function
1020 * @param {Function} func The function to debounce.
1021 * @param {number} [wait=0] The number of milliseconds to delay.
1022 * @param {Object} [options={}] The options object.
1023 * @param {boolean} [options.leading=false]
1024 * Specify invoking on the leading edge of the timeout.
1025 * @param {number} [options.maxWait]
1026 * The maximum time `func` is allowed to be delayed before it's invoked.
1027 * @param {boolean} [options.trailing=true]
1028 * Specify invoking on the trailing edge of the timeout.
1029 * @returns {Function} Returns the new debounced function.
1030 * @example
1031 *
1032 * // Avoid costly calculations while the window size is in flux.
1033 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
1034 *
1035 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
1036 * jQuery(element).on('click', _.debounce(sendMail, 300, {
1037 * 'leading': true,
1038 * 'trailing': false
1039 * }));
1040 *
1041 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
1042 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
1043 * var source = new EventSource('/stream');
1044 * jQuery(source).on('message', debounced);
1045 *
1046 * // Cancel the trailing debounced invocation.
1047 * jQuery(window).on('popstate', debounced.cancel);
1048 */
1049function debounce(func, wait, options) {
1050 var lastArgs,
1051 lastThis,
1052 maxWait,
1053 result,
1054 timerId,
1055 lastCallTime,
1056 lastInvokeTime = 0,
1057 leading = false,
1058 maxing = false,
1059 trailing = true;
1060
1061 if (typeof func != 'function') {
1062 throw new TypeError(FUNC_ERROR_TEXT$1);
1063 }
1064 wait = toNumber_1(wait) || 0;
1065 if (isObject_1(options)) {
1066 leading = !!options.leading;
1067 maxing = 'maxWait' in options;
1068 maxWait = maxing ? nativeMax(toNumber_1(options.maxWait) || 0, wait) : maxWait;
1069 trailing = 'trailing' in options ? !!options.trailing : trailing;
1070 }
1071
1072 function invokeFunc(time) {
1073 var args = lastArgs,
1074 thisArg = lastThis;
1075
1076 lastArgs = lastThis = undefined;
1077 lastInvokeTime = time;
1078 result = func.apply(thisArg, args);
1079 return result;
1080 }
1081
1082 function leadingEdge(time) {
1083 // Reset any `maxWait` timer.
1084 lastInvokeTime = time;
1085 // Start the timer for the trailing edge.
1086 timerId = setTimeout(timerExpired, wait);
1087 // Invoke the leading edge.
1088 return leading ? invokeFunc(time) : result;
1089 }
1090
1091 function remainingWait(time) {
1092 var timeSinceLastCall = time - lastCallTime,
1093 timeSinceLastInvoke = time - lastInvokeTime,
1094 timeWaiting = wait - timeSinceLastCall;
1095
1096 return maxing
1097 ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
1098 : timeWaiting;
1099 }
1100
1101 function shouldInvoke(time) {
1102 var timeSinceLastCall = time - lastCallTime,
1103 timeSinceLastInvoke = time - lastInvokeTime;
1104
1105 // Either this is the first call, activity has stopped and we're at the
1106 // trailing edge, the system time has gone backwards and we're treating
1107 // it as the trailing edge, or we've hit the `maxWait` limit.
1108 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
1109 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
1110 }
1111
1112 function timerExpired() {
1113 var time = now_1();
1114 if (shouldInvoke(time)) {
1115 return trailingEdge(time);
1116 }
1117 // Restart the timer.
1118 timerId = setTimeout(timerExpired, remainingWait(time));
1119 }
1120
1121 function trailingEdge(time) {
1122 timerId = undefined;
1123
1124 // Only invoke if we have `lastArgs` which means `func` has been
1125 // debounced at least once.
1126 if (trailing && lastArgs) {
1127 return invokeFunc(time);
1128 }
1129 lastArgs = lastThis = undefined;
1130 return result;
1131 }
1132
1133 function cancel() {
1134 if (timerId !== undefined) {
1135 clearTimeout(timerId);
1136 }
1137 lastInvokeTime = 0;
1138 lastArgs = lastCallTime = lastThis = timerId = undefined;
1139 }
1140
1141 function flush() {
1142 return timerId === undefined ? result : trailingEdge(now_1());
1143 }
1144
1145 function debounced() {
1146 var time = now_1(),
1147 isInvoking = shouldInvoke(time);
1148
1149 lastArgs = arguments;
1150 lastThis = this;
1151 lastCallTime = time;
1152
1153 if (isInvoking) {
1154 if (timerId === undefined) {
1155 return leadingEdge(lastCallTime);
1156 }
1157 if (maxing) {
1158 // Handle invocations in a tight loop.
1159 clearTimeout(timerId);
1160 timerId = setTimeout(timerExpired, wait);
1161 return invokeFunc(lastCallTime);
1162 }
1163 }
1164 if (timerId === undefined) {
1165 timerId = setTimeout(timerExpired, wait);
1166 }
1167 return result;
1168 }
1169 debounced.cancel = cancel;
1170 debounced.flush = flush;
1171 return debounced;
1172}
1173
1174var debounce_1 = debounce;
1175
1176var performance = _window ? _window.performance : null;
1177var pnow = performance && performance.now ? function () {
1178 return performance.now();
1179} : function () {
1180 return Date.now();
1181};
1182var raf = function () {
1183 if (_window) {
1184 if (_window.requestAnimationFrame) {
1185 return function (fn) {
1186 _window.requestAnimationFrame(fn);
1187 };
1188 } else if (_window.mozRequestAnimationFrame) {
1189 return function (fn) {
1190 _window.mozRequestAnimationFrame(fn);
1191 };
1192 } else if (_window.webkitRequestAnimationFrame) {
1193 return function (fn) {
1194 _window.webkitRequestAnimationFrame(fn);
1195 };
1196 } else if (_window.msRequestAnimationFrame) {
1197 return function (fn) {
1198 _window.msRequestAnimationFrame(fn);
1199 };
1200 }
1201 }
1202 return function (fn) {
1203 if (fn) {
1204 setTimeout(function () {
1205 fn(pnow());
1206 }, 1000 / 60);
1207 }
1208 };
1209}();
1210var requestAnimationFrame = function requestAnimationFrame(fn) {
1211 return raf(fn);
1212};
1213var performanceNow = pnow;
1214
1215var DEFAULT_HASH_SEED = 9261;
1216var K = 65599; // 37 also works pretty well
1217var DEFAULT_HASH_SEED_ALT = 5381;
1218var hashIterableInts = function hashIterableInts(iterator) {
1219 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1220 // sdbm/string-hash
1221 var hash = seed;
1222 var entry;
1223 for (;;) {
1224 entry = iterator.next();
1225 if (entry.done) {
1226 break;
1227 }
1228 hash = hash * K + entry.value | 0;
1229 }
1230 return hash;
1231};
1232var hashInt = function hashInt(num) {
1233 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1234 // sdbm/string-hash
1235 return seed * K + num | 0;
1236};
1237var hashIntAlt = function hashIntAlt(num) {
1238 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
1239 // djb2/string-hash
1240 return (seed << 5) + seed + num | 0;
1241};
1242var combineHashes = function combineHashes(hash1, hash2) {
1243 return hash1 * 0x200000 + hash2;
1244};
1245var combineHashesArray = function combineHashesArray(hashes) {
1246 return hashes[0] * 0x200000 + hashes[1];
1247};
1248var hashArrays = function hashArrays(hashes1, hashes2) {
1249 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
1250};
1251var hashIntsArray = function hashIntsArray(ints, seed) {
1252 var entry = {
1253 value: 0,
1254 done: false
1255 };
1256 var i = 0;
1257 var length = ints.length;
1258 var iterator = {
1259 next: function next() {
1260 if (i < length) {
1261 entry.value = ints[i++];
1262 } else {
1263 entry.done = true;
1264 }
1265 return entry;
1266 }
1267 };
1268 return hashIterableInts(iterator, seed);
1269};
1270var hashString = function hashString(str, seed) {
1271 var entry = {
1272 value: 0,
1273 done: false
1274 };
1275 var i = 0;
1276 var length = str.length;
1277 var iterator = {
1278 next: function next() {
1279 if (i < length) {
1280 entry.value = str.charCodeAt(i++);
1281 } else {
1282 entry.done = true;
1283 }
1284 return entry;
1285 }
1286 };
1287 return hashIterableInts(iterator, seed);
1288};
1289var hashStrings = function hashStrings() {
1290 return hashStringsArray(arguments);
1291};
1292var hashStringsArray = function hashStringsArray(strs) {
1293 var hash;
1294 for (var i = 0; i < strs.length; i++) {
1295 var str = strs[i];
1296 if (i === 0) {
1297 hash = hashString(str);
1298 } else {
1299 hash = hashString(str, hash);
1300 }
1301 }
1302 return hash;
1303};
1304
1305/*global console */
1306var warningsEnabled = true;
1307var warnSupported = console.warn != null; // eslint-disable-line no-console
1308var traceSupported = console.trace != null; // eslint-disable-line no-console
1309
1310var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991;
1311var trueify = function trueify() {
1312 return true;
1313};
1314var falsify = function falsify() {
1315 return false;
1316};
1317var zeroify = function zeroify() {
1318 return 0;
1319};
1320var noop$1 = function noop() {};
1321var error = function error(msg) {
1322 throw new Error(msg);
1323};
1324var warnings = function warnings(enabled) {
1325 if (enabled !== undefined) {
1326 warningsEnabled = !!enabled;
1327 } else {
1328 return warningsEnabled;
1329 }
1330};
1331var warn = function warn(msg) {
1332 /* eslint-disable no-console */
1333 if (!warnings()) {
1334 return;
1335 }
1336 if (warnSupported) {
1337 console.warn(msg);
1338 } else {
1339 console.log(msg);
1340 if (traceSupported) {
1341 console.trace();
1342 }
1343 }
1344}; /* eslint-enable */
1345
1346var clone = function clone(obj) {
1347 return extend({}, obj);
1348};
1349
1350// gets a shallow copy of the argument
1351var copy = function copy(obj) {
1352 if (obj == null) {
1353 return obj;
1354 }
1355 if (array(obj)) {
1356 return obj.slice();
1357 } else if (plainObject(obj)) {
1358 return clone(obj);
1359 } else {
1360 return obj;
1361 }
1362};
1363var copyArray$1 = function copyArray(arr) {
1364 return arr.slice();
1365};
1366var uuid = function uuid(a, b /* placeholders */) {
1367 for (
1368 // loop :)
1369 b = a = '';
1370 // b - result , a - numeric letiable
1371 a++ < 36;
1372 //
1373 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
1374 ?
1375 // return a random number or 4
1376 (a ^ 15 // if "a" is not 15
1377 ?
1378 // generate a random number from 0 to 15
1379 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
1380 : 4 // otherwise 4
1381 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
1382 ) {
1383 }
1384 return b;
1385};
1386var _staticEmptyObject = {};
1387var staticEmptyObject = function staticEmptyObject() {
1388 return _staticEmptyObject;
1389};
1390var defaults$g = function defaults(_defaults) {
1391 var keys = Object.keys(_defaults);
1392 return function (opts) {
1393 var filledOpts = {};
1394 for (var i = 0; i < keys.length; i++) {
1395 var key = keys[i];
1396 var optVal = opts == null ? undefined : opts[key];
1397 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
1398 }
1399 return filledOpts;
1400 };
1401};
1402var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
1403 for (var i = arr.length - 1; i >= 0; i--) {
1404 if (arr[i] === ele) {
1405 arr.splice(i, 1);
1406 if (oneCopy) {
1407 break;
1408 }
1409 }
1410 }
1411};
1412var clearArray = function clearArray(arr) {
1413 arr.splice(0, arr.length);
1414};
1415var push = function push(arr, otherArr) {
1416 for (var i = 0; i < otherArr.length; i++) {
1417 var el = otherArr[i];
1418 arr.push(el);
1419 }
1420};
1421var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
1422 if (prefix) {
1423 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1424 }
1425
1426 return obj[propName];
1427};
1428var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
1429 if (prefix) {
1430 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1431 }
1432
1433 obj[propName] = value;
1434};
1435
1436/* global Map */
1437var ObjectMap = /*#__PURE__*/function () {
1438 function ObjectMap() {
1439 _classCallCheck(this, ObjectMap);
1440 this._obj = {};
1441 }
1442 _createClass(ObjectMap, [{
1443 key: "set",
1444 value: function set(key, val) {
1445 this._obj[key] = val;
1446 return this;
1447 }
1448 }, {
1449 key: "delete",
1450 value: function _delete(key) {
1451 this._obj[key] = undefined;
1452 return this;
1453 }
1454 }, {
1455 key: "clear",
1456 value: function clear() {
1457 this._obj = {};
1458 }
1459 }, {
1460 key: "has",
1461 value: function has(key) {
1462 return this._obj[key] !== undefined;
1463 }
1464 }, {
1465 key: "get",
1466 value: function get(key) {
1467 return this._obj[key];
1468 }
1469 }]);
1470 return ObjectMap;
1471}();
1472var Map$2 = typeof Map !== 'undefined' ? Map : ObjectMap;
1473
1474/* global Set */
1475
1476var undef = "undefined" ;
1477var ObjectSet = /*#__PURE__*/function () {
1478 function ObjectSet(arrayOrObjectSet) {
1479 _classCallCheck(this, ObjectSet);
1480 this._obj = Object.create(null);
1481 this.size = 0;
1482 if (arrayOrObjectSet != null) {
1483 var arr;
1484 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1485 arr = arrayOrObjectSet.toArray();
1486 } else {
1487 arr = arrayOrObjectSet;
1488 }
1489 for (var i = 0; i < arr.length; i++) {
1490 this.add(arr[i]);
1491 }
1492 }
1493 }
1494 _createClass(ObjectSet, [{
1495 key: "instanceString",
1496 value: function instanceString() {
1497 return 'set';
1498 }
1499 }, {
1500 key: "add",
1501 value: function add(val) {
1502 var o = this._obj;
1503 if (o[val] !== 1) {
1504 o[val] = 1;
1505 this.size++;
1506 }
1507 }
1508 }, {
1509 key: "delete",
1510 value: function _delete(val) {
1511 var o = this._obj;
1512 if (o[val] === 1) {
1513 o[val] = 0;
1514 this.size--;
1515 }
1516 }
1517 }, {
1518 key: "clear",
1519 value: function clear() {
1520 this._obj = Object.create(null);
1521 }
1522 }, {
1523 key: "has",
1524 value: function has(val) {
1525 return this._obj[val] === 1;
1526 }
1527 }, {
1528 key: "toArray",
1529 value: function toArray() {
1530 var _this = this;
1531 return Object.keys(this._obj).filter(function (key) {
1532 return _this.has(key);
1533 });
1534 }
1535 }, {
1536 key: "forEach",
1537 value: function forEach(callback, thisArg) {
1538 return this.toArray().forEach(callback, thisArg);
1539 }
1540 }]);
1541 return ObjectSet;
1542}();
1543var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1544
1545// represents a node or an edge
1546var Element = function Element(cy, params) {
1547 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1548 if (cy === undefined || params === undefined || !core(cy)) {
1549 error('An element must have a core reference and parameters set');
1550 return;
1551 }
1552 var group = params.group;
1553
1554 // try to automatically infer the group if unspecified
1555 if (group == null) {
1556 if (params.data && params.data.source != null && params.data.target != null) {
1557 group = 'edges';
1558 } else {
1559 group = 'nodes';
1560 }
1561 }
1562
1563 // validate group
1564 if (group !== 'nodes' && group !== 'edges') {
1565 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1566 return;
1567 }
1568
1569 // make the element array-like, just like a collection
1570 this.length = 1;
1571 this[0] = this;
1572
1573 // NOTE: when something is added here, add also to ele.json()
1574 var _p = this._private = {
1575 cy: cy,
1576 single: true,
1577 // indicates this is an element
1578 data: params.data || {},
1579 // data object
1580 position: params.position || {
1581 x: 0,
1582 y: 0
1583 },
1584 // (x, y) position pair
1585 autoWidth: undefined,
1586 // width and height of nodes calculated by the renderer when set to special 'auto' value
1587 autoHeight: undefined,
1588 autoPadding: undefined,
1589 compoundBoundsClean: false,
1590 // whether the compound dimensions need to be recalculated the next time dimensions are read
1591 listeners: [],
1592 // array of bound listeners
1593 group: group,
1594 // string; 'nodes' or 'edges'
1595 style: {},
1596 // properties as set by the style
1597 rstyle: {},
1598 // properties for style sent from the renderer to the core
1599 styleCxts: [],
1600 // applied style contexts from the styler
1601 styleKeys: {},
1602 // per-group keys of style property values
1603 removed: true,
1604 // whether it's inside the vis; true if removed (set true here since we call restore)
1605 selected: params.selected ? true : false,
1606 // whether it's selected
1607 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1608 // whether it's selectable
1609 locked: params.locked ? true : false,
1610 // whether the element is locked (cannot be moved)
1611 grabbed: false,
1612 // whether the element is grabbed by the mouse; renderer sets this privately
1613 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1614 // whether the element can be grabbed
1615 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1616 // whether the element has passthrough panning enabled
1617 active: false,
1618 // whether the element is active from user interaction
1619 classes: new Set$1(),
1620 // map ( className => true )
1621 animation: {
1622 // object for currently-running animations
1623 current: [],
1624 queue: []
1625 },
1626 rscratch: {},
1627 // object in which the renderer can store information
1628 scratch: params.scratch || {},
1629 // scratch objects
1630 edges: [],
1631 // array of connected edges
1632 children: [],
1633 // array of children
1634 parent: params.parent && params.parent.isNode() ? params.parent : null,
1635 // parent ref
1636 traversalCache: {},
1637 // cache of output of traversal functions
1638 backgrounding: false,
1639 // whether background images are loading
1640 bbCache: null,
1641 // cache of the current bounding box
1642 bbCacheShift: {
1643 x: 0,
1644 y: 0
1645 },
1646 // shift applied to cached bb to be applied on next get
1647 bodyBounds: null,
1648 // bounds cache of element body, w/o overlay
1649 overlayBounds: null,
1650 // bounds cache of element body, including overlay
1651 labelBounds: {
1652 // bounds cache of labels
1653 all: null,
1654 source: null,
1655 target: null,
1656 main: null
1657 },
1658 arrowBounds: {
1659 // bounds cache of edge arrows
1660 source: null,
1661 target: null,
1662 'mid-source': null,
1663 'mid-target': null
1664 }
1665 };
1666 if (_p.position.x == null) {
1667 _p.position.x = 0;
1668 }
1669 if (_p.position.y == null) {
1670 _p.position.y = 0;
1671 }
1672
1673 // renderedPosition overrides if specified
1674 if (params.renderedPosition) {
1675 var rpos = params.renderedPosition;
1676 var pan = cy.pan();
1677 var zoom = cy.zoom();
1678 _p.position = {
1679 x: (rpos.x - pan.x) / zoom,
1680 y: (rpos.y - pan.y) / zoom
1681 };
1682 }
1683 var classes = [];
1684 if (array(params.classes)) {
1685 classes = params.classes;
1686 } else if (string(params.classes)) {
1687 classes = params.classes.split(/\s+/);
1688 }
1689 for (var i = 0, l = classes.length; i < l; i++) {
1690 var cls = classes[i];
1691 if (!cls || cls === '') {
1692 continue;
1693 }
1694 _p.classes.add(cls);
1695 }
1696 this.createEmitter();
1697 var bypass = params.style || params.css;
1698 if (bypass) {
1699 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1700 this.style(bypass);
1701 }
1702 if (restore === undefined || restore) {
1703 this.restore();
1704 }
1705};
1706
1707var defineSearch = function defineSearch(params) {
1708 params = {
1709 bfs: params.bfs || !params.dfs,
1710 dfs: params.dfs || !params.bfs
1711 };
1712
1713 // from pseudocode on wikipedia
1714 return function searchFn(roots, fn, directed) {
1715 var options;
1716 if (plainObject(roots) && !elementOrCollection(roots)) {
1717 options = roots;
1718 roots = options.roots || options.root;
1719 fn = options.visit;
1720 directed = options.directed;
1721 }
1722 directed = arguments.length === 2 && !fn$6(fn) ? fn : directed;
1723 fn = fn$6(fn) ? fn : function () {};
1724 var cy = this._private.cy;
1725 var v = roots = string(roots) ? this.filter(roots) : roots;
1726 var Q = [];
1727 var connectedNodes = [];
1728 var connectedBy = {};
1729 var id2depth = {};
1730 var V = {};
1731 var j = 0;
1732 var found;
1733 var _this$byGroup = this.byGroup(),
1734 nodes = _this$byGroup.nodes,
1735 edges = _this$byGroup.edges;
1736
1737 // enqueue v
1738 for (var i = 0; i < v.length; i++) {
1739 var vi = v[i];
1740 var viId = vi.id();
1741 if (vi.isNode()) {
1742 Q.unshift(vi);
1743 if (params.bfs) {
1744 V[viId] = true;
1745 connectedNodes.push(vi);
1746 }
1747 id2depth[viId] = 0;
1748 }
1749 }
1750 var _loop = function _loop() {
1751 var v = params.bfs ? Q.shift() : Q.pop();
1752 var vId = v.id();
1753 if (params.dfs) {
1754 if (V[vId]) {
1755 return "continue";
1756 }
1757 V[vId] = true;
1758 connectedNodes.push(v);
1759 }
1760 var depth = id2depth[vId];
1761 var prevEdge = connectedBy[vId];
1762 var src = prevEdge != null ? prevEdge.source() : null;
1763 var tgt = prevEdge != null ? prevEdge.target() : null;
1764 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1765 var ret = void 0;
1766 ret = fn(v, prevEdge, prevNode, j++, depth);
1767 if (ret === true) {
1768 found = v;
1769 return "break";
1770 }
1771 if (ret === false) {
1772 return "break";
1773 }
1774 var vwEdges = v.connectedEdges().filter(function (e) {
1775 return (!directed || e.source().same(v)) && edges.has(e);
1776 });
1777 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1778 var e = vwEdges[_i2];
1779 var w = e.connectedNodes().filter(function (n) {
1780 return !n.same(v) && nodes.has(n);
1781 });
1782 var wId = w.id();
1783 if (w.length !== 0 && !V[wId]) {
1784 w = w[0];
1785 Q.push(w);
1786 if (params.bfs) {
1787 V[wId] = true;
1788 connectedNodes.push(w);
1789 }
1790 connectedBy[wId] = e;
1791 id2depth[wId] = id2depth[vId] + 1;
1792 }
1793 }
1794 };
1795 while (Q.length !== 0) {
1796 var _ret = _loop();
1797 if (_ret === "continue") continue;
1798 if (_ret === "break") break;
1799 }
1800 var connectedEles = cy.collection();
1801 for (var _i = 0; _i < connectedNodes.length; _i++) {
1802 var node = connectedNodes[_i];
1803 var edge = connectedBy[node.id()];
1804 if (edge != null) {
1805 connectedEles.push(edge);
1806 }
1807 connectedEles.push(node);
1808 }
1809 return {
1810 path: cy.collection(connectedEles),
1811 found: cy.collection(found)
1812 };
1813 };
1814};
1815
1816// search, spanning trees, etc
1817var elesfn$v = {
1818 breadthFirstSearch: defineSearch({
1819 bfs: true
1820 }),
1821 depthFirstSearch: defineSearch({
1822 dfs: true
1823 })
1824};
1825
1826// nice, short mathematical alias
1827elesfn$v.bfs = elesfn$v.breadthFirstSearch;
1828elesfn$v.dfs = elesfn$v.depthFirstSearch;
1829
1830var heap$1 = createCommonjsModule(function (module, exports) {
1831// Generated by CoffeeScript 1.8.0
1832(function() {
1833 var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
1834
1835 floor = Math.floor, min = Math.min;
1836
1837
1838 /*
1839 Default comparison function to be used
1840 */
1841
1842 defaultCmp = function(x, y) {
1843 if (x < y) {
1844 return -1;
1845 }
1846 if (x > y) {
1847 return 1;
1848 }
1849 return 0;
1850 };
1851
1852
1853 /*
1854 Insert item x in list a, and keep it sorted assuming a is sorted.
1855
1856 If x is already in a, insert it to the right of the rightmost x.
1857
1858 Optional args lo (default 0) and hi (default a.length) bound the slice
1859 of a to be searched.
1860 */
1861
1862 insort = function(a, x, lo, hi, cmp) {
1863 var mid;
1864 if (lo == null) {
1865 lo = 0;
1866 }
1867 if (cmp == null) {
1868 cmp = defaultCmp;
1869 }
1870 if (lo < 0) {
1871 throw new Error('lo must be non-negative');
1872 }
1873 if (hi == null) {
1874 hi = a.length;
1875 }
1876 while (lo < hi) {
1877 mid = floor((lo + hi) / 2);
1878 if (cmp(x, a[mid]) < 0) {
1879 hi = mid;
1880 } else {
1881 lo = mid + 1;
1882 }
1883 }
1884 return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
1885 };
1886
1887
1888 /*
1889 Push item onto heap, maintaining the heap invariant.
1890 */
1891
1892 heappush = function(array, item, cmp) {
1893 if (cmp == null) {
1894 cmp = defaultCmp;
1895 }
1896 array.push(item);
1897 return _siftdown(array, 0, array.length - 1, cmp);
1898 };
1899
1900
1901 /*
1902 Pop the smallest item off the heap, maintaining the heap invariant.
1903 */
1904
1905 heappop = function(array, cmp) {
1906 var lastelt, returnitem;
1907 if (cmp == null) {
1908 cmp = defaultCmp;
1909 }
1910 lastelt = array.pop();
1911 if (array.length) {
1912 returnitem = array[0];
1913 array[0] = lastelt;
1914 _siftup(array, 0, cmp);
1915 } else {
1916 returnitem = lastelt;
1917 }
1918 return returnitem;
1919 };
1920
1921
1922 /*
1923 Pop and return the current smallest value, and add the new item.
1924
1925 This is more efficient than heappop() followed by heappush(), and can be
1926 more appropriate when using a fixed size heap. Note that the value
1927 returned may be larger than item! That constrains reasonable use of
1928 this routine unless written as part of a conditional replacement:
1929 if item > array[0]
1930 item = heapreplace(array, item)
1931 */
1932
1933 heapreplace = function(array, item, cmp) {
1934 var returnitem;
1935 if (cmp == null) {
1936 cmp = defaultCmp;
1937 }
1938 returnitem = array[0];
1939 array[0] = item;
1940 _siftup(array, 0, cmp);
1941 return returnitem;
1942 };
1943
1944
1945 /*
1946 Fast version of a heappush followed by a heappop.
1947 */
1948
1949 heappushpop = function(array, item, cmp) {
1950 var _ref;
1951 if (cmp == null) {
1952 cmp = defaultCmp;
1953 }
1954 if (array.length && cmp(array[0], item) < 0) {
1955 _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
1956 _siftup(array, 0, cmp);
1957 }
1958 return item;
1959 };
1960
1961
1962 /*
1963 Transform list into a heap, in-place, in O(array.length) time.
1964 */
1965
1966 heapify = function(array, cmp) {
1967 var i, _i, _len, _ref1, _results, _results1;
1968 if (cmp == null) {
1969 cmp = defaultCmp;
1970 }
1971 _ref1 = (function() {
1972 _results1 = [];
1973 for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
1974 return _results1;
1975 }).apply(this).reverse();
1976 _results = [];
1977 for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
1978 i = _ref1[_i];
1979 _results.push(_siftup(array, i, cmp));
1980 }
1981 return _results;
1982 };
1983
1984
1985 /*
1986 Update the position of the given item in the heap.
1987 This function should be called every time the item is being modified.
1988 */
1989
1990 updateItem = function(array, item, cmp) {
1991 var pos;
1992 if (cmp == null) {
1993 cmp = defaultCmp;
1994 }
1995 pos = array.indexOf(item);
1996 if (pos === -1) {
1997 return;
1998 }
1999 _siftdown(array, 0, pos, cmp);
2000 return _siftup(array, pos, cmp);
2001 };
2002
2003
2004 /*
2005 Find the n largest elements in a dataset.
2006 */
2007
2008 nlargest = function(array, n, cmp) {
2009 var elem, result, _i, _len, _ref;
2010 if (cmp == null) {
2011 cmp = defaultCmp;
2012 }
2013 result = array.slice(0, n);
2014 if (!result.length) {
2015 return result;
2016 }
2017 heapify(result, cmp);
2018 _ref = array.slice(n);
2019 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2020 elem = _ref[_i];
2021 heappushpop(result, elem, cmp);
2022 }
2023 return result.sort(cmp).reverse();
2024 };
2025
2026
2027 /*
2028 Find the n smallest elements in a dataset.
2029 */
2030
2031 nsmallest = function(array, n, cmp) {
2032 var elem, los, result, _i, _j, _len, _ref, _ref1, _results;
2033 if (cmp == null) {
2034 cmp = defaultCmp;
2035 }
2036 if (n * 10 <= array.length) {
2037 result = array.slice(0, n).sort(cmp);
2038 if (!result.length) {
2039 return result;
2040 }
2041 los = result[result.length - 1];
2042 _ref = array.slice(n);
2043 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2044 elem = _ref[_i];
2045 if (cmp(elem, los) < 0) {
2046 insort(result, elem, 0, null, cmp);
2047 result.pop();
2048 los = result[result.length - 1];
2049 }
2050 }
2051 return result;
2052 }
2053 heapify(array, cmp);
2054 _results = [];
2055 for (_j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; 0 <= _ref1 ? ++_j : --_j) {
2056 _results.push(heappop(array, cmp));
2057 }
2058 return _results;
2059 };
2060
2061 _siftdown = function(array, startpos, pos, cmp) {
2062 var newitem, parent, parentpos;
2063 if (cmp == null) {
2064 cmp = defaultCmp;
2065 }
2066 newitem = array[pos];
2067 while (pos > startpos) {
2068 parentpos = (pos - 1) >> 1;
2069 parent = array[parentpos];
2070 if (cmp(newitem, parent) < 0) {
2071 array[pos] = parent;
2072 pos = parentpos;
2073 continue;
2074 }
2075 break;
2076 }
2077 return array[pos] = newitem;
2078 };
2079
2080 _siftup = function(array, pos, cmp) {
2081 var childpos, endpos, newitem, rightpos, startpos;
2082 if (cmp == null) {
2083 cmp = defaultCmp;
2084 }
2085 endpos = array.length;
2086 startpos = pos;
2087 newitem = array[pos];
2088 childpos = 2 * pos + 1;
2089 while (childpos < endpos) {
2090 rightpos = childpos + 1;
2091 if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
2092 childpos = rightpos;
2093 }
2094 array[pos] = array[childpos];
2095 pos = childpos;
2096 childpos = 2 * pos + 1;
2097 }
2098 array[pos] = newitem;
2099 return _siftdown(array, startpos, pos, cmp);
2100 };
2101
2102 Heap = (function() {
2103 Heap.push = heappush;
2104
2105 Heap.pop = heappop;
2106
2107 Heap.replace = heapreplace;
2108
2109 Heap.pushpop = heappushpop;
2110
2111 Heap.heapify = heapify;
2112
2113 Heap.updateItem = updateItem;
2114
2115 Heap.nlargest = nlargest;
2116
2117 Heap.nsmallest = nsmallest;
2118
2119 function Heap(cmp) {
2120 this.cmp = cmp != null ? cmp : defaultCmp;
2121 this.nodes = [];
2122 }
2123
2124 Heap.prototype.push = function(x) {
2125 return heappush(this.nodes, x, this.cmp);
2126 };
2127
2128 Heap.prototype.pop = function() {
2129 return heappop(this.nodes, this.cmp);
2130 };
2131
2132 Heap.prototype.peek = function() {
2133 return this.nodes[0];
2134 };
2135
2136 Heap.prototype.contains = function(x) {
2137 return this.nodes.indexOf(x) !== -1;
2138 };
2139
2140 Heap.prototype.replace = function(x) {
2141 return heapreplace(this.nodes, x, this.cmp);
2142 };
2143
2144 Heap.prototype.pushpop = function(x) {
2145 return heappushpop(this.nodes, x, this.cmp);
2146 };
2147
2148 Heap.prototype.heapify = function() {
2149 return heapify(this.nodes, this.cmp);
2150 };
2151
2152 Heap.prototype.updateItem = function(x) {
2153 return updateItem(this.nodes, x, this.cmp);
2154 };
2155
2156 Heap.prototype.clear = function() {
2157 return this.nodes = [];
2158 };
2159
2160 Heap.prototype.empty = function() {
2161 return this.nodes.length === 0;
2162 };
2163
2164 Heap.prototype.size = function() {
2165 return this.nodes.length;
2166 };
2167
2168 Heap.prototype.clone = function() {
2169 var heap;
2170 heap = new Heap();
2171 heap.nodes = this.nodes.slice(0);
2172 return heap;
2173 };
2174
2175 Heap.prototype.toArray = function() {
2176 return this.nodes.slice(0);
2177 };
2178
2179 Heap.prototype.insert = Heap.prototype.push;
2180
2181 Heap.prototype.top = Heap.prototype.peek;
2182
2183 Heap.prototype.front = Heap.prototype.peek;
2184
2185 Heap.prototype.has = Heap.prototype.contains;
2186
2187 Heap.prototype.copy = Heap.prototype.clone;
2188
2189 return Heap;
2190
2191 })();
2192
2193 (function(root, factory) {
2194 {
2195 return module.exports = factory();
2196 }
2197 })(this, function() {
2198 return Heap;
2199 });
2200
2201}).call(commonjsGlobal);
2202});
2203
2204var heap = heap$1;
2205
2206var dijkstraDefaults = defaults$g({
2207 root: null,
2208 weight: function weight(edge) {
2209 return 1;
2210 },
2211 directed: false
2212});
2213var elesfn$u = {
2214 dijkstra: function dijkstra(options) {
2215 if (!plainObject(options)) {
2216 var args = arguments;
2217 options = {
2218 root: args[0],
2219 weight: args[1],
2220 directed: args[2]
2221 };
2222 }
2223 var _dijkstraDefaults = dijkstraDefaults(options),
2224 root = _dijkstraDefaults.root,
2225 weight = _dijkstraDefaults.weight,
2226 directed = _dijkstraDefaults.directed;
2227 var eles = this;
2228 var weightFn = weight;
2229 var source = string(root) ? this.filter(root)[0] : root[0];
2230 var dist = {};
2231 var prev = {};
2232 var knownDist = {};
2233 var _this$byGroup = this.byGroup(),
2234 nodes = _this$byGroup.nodes,
2235 edges = _this$byGroup.edges;
2236 edges.unmergeBy(function (ele) {
2237 return ele.isLoop();
2238 });
2239 var getDist = function getDist(node) {
2240 return dist[node.id()];
2241 };
2242 var setDist = function setDist(node, d) {
2243 dist[node.id()] = d;
2244 Q.updateItem(node);
2245 };
2246 var Q = new heap(function (a, b) {
2247 return getDist(a) - getDist(b);
2248 });
2249 for (var i = 0; i < nodes.length; i++) {
2250 var node = nodes[i];
2251 dist[node.id()] = node.same(source) ? 0 : Infinity;
2252 Q.push(node);
2253 }
2254 var distBetween = function distBetween(u, v) {
2255 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
2256 var smallestDistance = Infinity;
2257 var smallestEdge;
2258 for (var _i = 0; _i < uvs.length; _i++) {
2259 var edge = uvs[_i];
2260 var _weight = weightFn(edge);
2261 if (_weight < smallestDistance || !smallestEdge) {
2262 smallestDistance = _weight;
2263 smallestEdge = edge;
2264 }
2265 }
2266 return {
2267 edge: smallestEdge,
2268 dist: smallestDistance
2269 };
2270 };
2271 while (Q.size() > 0) {
2272 var u = Q.pop();
2273 var smalletsDist = getDist(u);
2274 var uid = u.id();
2275 knownDist[uid] = smalletsDist;
2276 if (smalletsDist === Infinity) {
2277 continue;
2278 }
2279 var neighbors = u.neighborhood().intersect(nodes);
2280 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
2281 var v = neighbors[_i2];
2282 var vid = v.id();
2283 var vDist = distBetween(u, v);
2284 var alt = smalletsDist + vDist.dist;
2285 if (alt < getDist(v)) {
2286 setDist(v, alt);
2287 prev[vid] = {
2288 node: u,
2289 edge: vDist.edge
2290 };
2291 }
2292 } // for
2293 } // while
2294
2295 return {
2296 distanceTo: function distanceTo(node) {
2297 var target = string(node) ? nodes.filter(node)[0] : node[0];
2298 return knownDist[target.id()];
2299 },
2300 pathTo: function pathTo(node) {
2301 var target = string(node) ? nodes.filter(node)[0] : node[0];
2302 var S = [];
2303 var u = target;
2304 var uid = u.id();
2305 if (target.length > 0) {
2306 S.unshift(target);
2307 while (prev[uid]) {
2308 var p = prev[uid];
2309 S.unshift(p.edge);
2310 S.unshift(p.node);
2311 u = p.node;
2312 uid = u.id();
2313 }
2314 }
2315 return eles.spawn(S);
2316 }
2317 };
2318 }
2319};
2320
2321var elesfn$t = {
2322 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
2323 // implemented from pseudocode from wikipedia
2324 kruskal: function kruskal(weightFn) {
2325 weightFn = weightFn || function (edge) {
2326 return 1;
2327 };
2328 var _this$byGroup = this.byGroup(),
2329 nodes = _this$byGroup.nodes,
2330 edges = _this$byGroup.edges;
2331 var numNodes = nodes.length;
2332 var forest = new Array(numNodes);
2333 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
2334
2335 var findSetIndex = function findSetIndex(ele) {
2336 for (var i = 0; i < forest.length; i++) {
2337 var eles = forest[i];
2338 if (eles.has(ele)) {
2339 return i;
2340 }
2341 }
2342 };
2343
2344 // start with one forest per node
2345 for (var i = 0; i < numNodes; i++) {
2346 forest[i] = this.spawn(nodes[i]);
2347 }
2348 var S = edges.sort(function (a, b) {
2349 return weightFn(a) - weightFn(b);
2350 });
2351 for (var _i = 0; _i < S.length; _i++) {
2352 var edge = S[_i];
2353 var u = edge.source()[0];
2354 var v = edge.target()[0];
2355 var setUIndex = findSetIndex(u);
2356 var setVIndex = findSetIndex(v);
2357 var setU = forest[setUIndex];
2358 var setV = forest[setVIndex];
2359 if (setUIndex !== setVIndex) {
2360 A.merge(edge);
2361
2362 // combine forests for u and v
2363 setU.merge(setV);
2364 forest.splice(setVIndex, 1);
2365 }
2366 }
2367 return A;
2368 }
2369};
2370
2371var aStarDefaults = defaults$g({
2372 root: null,
2373 goal: null,
2374 weight: function weight(edge) {
2375 return 1;
2376 },
2377 heuristic: function heuristic(edge) {
2378 return 0;
2379 },
2380 directed: false
2381});
2382var elesfn$s = {
2383 // Implemented from pseudocode from wikipedia
2384 aStar: function aStar(options) {
2385 var cy = this.cy();
2386 var _aStarDefaults = aStarDefaults(options),
2387 root = _aStarDefaults.root,
2388 goal = _aStarDefaults.goal,
2389 heuristic = _aStarDefaults.heuristic,
2390 directed = _aStarDefaults.directed,
2391 weight = _aStarDefaults.weight;
2392 root = cy.collection(root)[0];
2393 goal = cy.collection(goal)[0];
2394 var sid = root.id();
2395 var tid = goal.id();
2396 var gScore = {};
2397 var fScore = {};
2398 var closedSetIds = {};
2399 var openSet = new heap(function (a, b) {
2400 return fScore[a.id()] - fScore[b.id()];
2401 });
2402 var openSetIds = new Set$1();
2403 var cameFrom = {};
2404 var cameFromEdge = {};
2405 var addToOpenSet = function addToOpenSet(ele, id) {
2406 openSet.push(ele);
2407 openSetIds.add(id);
2408 };
2409 var cMin, cMinId;
2410 var popFromOpenSet = function popFromOpenSet() {
2411 cMin = openSet.pop();
2412 cMinId = cMin.id();
2413 openSetIds["delete"](cMinId);
2414 };
2415 var isInOpenSet = function isInOpenSet(id) {
2416 return openSetIds.has(id);
2417 };
2418 addToOpenSet(root, sid);
2419 gScore[sid] = 0;
2420 fScore[sid] = heuristic(root);
2421
2422 // Counter
2423 var steps = 0;
2424
2425 // Main loop
2426 while (openSet.size() > 0) {
2427 popFromOpenSet();
2428 steps++;
2429
2430 // If we've found our goal, then we are done
2431 if (cMinId === tid) {
2432 var path = [];
2433 var pathNode = goal;
2434 var pathNodeId = tid;
2435 var pathEdge = cameFromEdge[pathNodeId];
2436 for (;;) {
2437 path.unshift(pathNode);
2438 if (pathEdge != null) {
2439 path.unshift(pathEdge);
2440 }
2441 pathNode = cameFrom[pathNodeId];
2442 if (pathNode == null) {
2443 break;
2444 }
2445 pathNodeId = pathNode.id();
2446 pathEdge = cameFromEdge[pathNodeId];
2447 }
2448 return {
2449 found: true,
2450 distance: gScore[cMinId],
2451 path: this.spawn(path),
2452 steps: steps
2453 };
2454 }
2455
2456 // Add cMin to processed nodes
2457 closedSetIds[cMinId] = true;
2458
2459 // Update scores for neighbors of cMin
2460 // Take into account if graph is directed or not
2461 var vwEdges = cMin._private.edges;
2462 for (var i = 0; i < vwEdges.length; i++) {
2463 var e = vwEdges[i];
2464
2465 // edge must be in set of calling eles
2466 if (!this.hasElementWithId(e.id())) {
2467 continue;
2468 }
2469
2470 // cMin must be the source of edge if directed
2471 if (directed && e.data('source') !== cMinId) {
2472 continue;
2473 }
2474 var wSrc = e.source();
2475 var wTgt = e.target();
2476 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
2477 var wid = w.id();
2478
2479 // node must be in set of calling eles
2480 if (!this.hasElementWithId(wid)) {
2481 continue;
2482 }
2483
2484 // if node is in closedSet, ignore it
2485 if (closedSetIds[wid]) {
2486 continue;
2487 }
2488
2489 // New tentative score for node w
2490 var tempScore = gScore[cMinId] + weight(e);
2491
2492 // Update gScore for node w if:
2493 // w not present in openSet
2494 // OR
2495 // tentative gScore is less than previous value
2496
2497 // w not in openSet
2498 if (!isInOpenSet(wid)) {
2499 gScore[wid] = tempScore;
2500 fScore[wid] = tempScore + heuristic(w);
2501 addToOpenSet(w, wid);
2502 cameFrom[wid] = cMin;
2503 cameFromEdge[wid] = e;
2504 continue;
2505 }
2506
2507 // w already in openSet, but with greater gScore
2508 if (tempScore < gScore[wid]) {
2509 gScore[wid] = tempScore;
2510 fScore[wid] = tempScore + heuristic(w);
2511 cameFrom[wid] = cMin;
2512 cameFromEdge[wid] = e;
2513 }
2514 } // End of neighbors update
2515 } // End of main loop
2516
2517 // If we've reached here, then we've not reached our goal
2518 return {
2519 found: false,
2520 distance: undefined,
2521 path: undefined,
2522 steps: steps
2523 };
2524 }
2525}; // elesfn
2526
2527var floydWarshallDefaults = defaults$g({
2528 weight: function weight(edge) {
2529 return 1;
2530 },
2531 directed: false
2532});
2533var elesfn$r = {
2534 // Implemented from pseudocode from wikipedia
2535 floydWarshall: function floydWarshall(options) {
2536 var cy = this.cy();
2537 var _floydWarshallDefault = floydWarshallDefaults(options),
2538 weight = _floydWarshallDefault.weight,
2539 directed = _floydWarshallDefault.directed;
2540 var weightFn = weight;
2541 var _this$byGroup = this.byGroup(),
2542 nodes = _this$byGroup.nodes,
2543 edges = _this$byGroup.edges;
2544 var N = nodes.length;
2545 var Nsq = N * N;
2546 var indexOf = function indexOf(node) {
2547 return nodes.indexOf(node);
2548 };
2549 var atIndex = function atIndex(i) {
2550 return nodes[i];
2551 };
2552
2553 // Initialize distance matrix
2554 var dist = new Array(Nsq);
2555 for (var n = 0; n < Nsq; n++) {
2556 var j = n % N;
2557 var i = (n - j) / N;
2558 if (i === j) {
2559 dist[n] = 0;
2560 } else {
2561 dist[n] = Infinity;
2562 }
2563 }
2564
2565 // Initialize matrix used for path reconstruction
2566 // Initialize distance matrix
2567 var next = new Array(Nsq);
2568 var edgeNext = new Array(Nsq);
2569
2570 // Process edges
2571 for (var _i = 0; _i < edges.length; _i++) {
2572 var edge = edges[_i];
2573 var src = edge.source()[0];
2574 var tgt = edge.target()[0];
2575 if (src === tgt) {
2576 continue;
2577 } // exclude loops
2578
2579 var s = indexOf(src);
2580 var t = indexOf(tgt);
2581 var st = s * N + t; // source to target index
2582 var _weight = weightFn(edge);
2583
2584 // Check if already process another edge between same 2 nodes
2585 if (dist[st] > _weight) {
2586 dist[st] = _weight;
2587 next[st] = t;
2588 edgeNext[st] = edge;
2589 }
2590
2591 // If undirected graph, process 'reversed' edge
2592 if (!directed) {
2593 var ts = t * N + s; // target to source index
2594
2595 if (!directed && dist[ts] > _weight) {
2596 dist[ts] = _weight;
2597 next[ts] = s;
2598 edgeNext[ts] = edge;
2599 }
2600 }
2601 }
2602
2603 // Main loop
2604 for (var k = 0; k < N; k++) {
2605 for (var _i2 = 0; _i2 < N; _i2++) {
2606 var ik = _i2 * N + k;
2607 for (var _j = 0; _j < N; _j++) {
2608 var ij = _i2 * N + _j;
2609 var kj = k * N + _j;
2610 if (dist[ik] + dist[kj] < dist[ij]) {
2611 dist[ij] = dist[ik] + dist[kj];
2612 next[ij] = next[ik];
2613 }
2614 }
2615 }
2616 }
2617 var getArgEle = function getArgEle(ele) {
2618 return (string(ele) ? cy.filter(ele) : ele)[0];
2619 };
2620 var indexOfArgEle = function indexOfArgEle(ele) {
2621 return indexOf(getArgEle(ele));
2622 };
2623 var res = {
2624 distance: function distance(from, to) {
2625 var i = indexOfArgEle(from);
2626 var j = indexOfArgEle(to);
2627 return dist[i * N + j];
2628 },
2629 path: function path(from, to) {
2630 var i = indexOfArgEle(from);
2631 var j = indexOfArgEle(to);
2632 var fromNode = atIndex(i);
2633 if (i === j) {
2634 return fromNode.collection();
2635 }
2636 if (next[i * N + j] == null) {
2637 return cy.collection();
2638 }
2639 var path = cy.collection();
2640 var prev = i;
2641 var edge;
2642 path.merge(fromNode);
2643 while (i !== j) {
2644 prev = i;
2645 i = next[i * N + j];
2646 edge = edgeNext[prev * N + i];
2647 path.merge(edge);
2648 path.merge(atIndex(i));
2649 }
2650 return path;
2651 }
2652 };
2653 return res;
2654 } // floydWarshall
2655}; // elesfn
2656
2657var bellmanFordDefaults = defaults$g({
2658 weight: function weight(edge) {
2659 return 1;
2660 },
2661 directed: false,
2662 root: null
2663});
2664var elesfn$q = {
2665 // Implemented from pseudocode from wikipedia
2666 bellmanFord: function bellmanFord(options) {
2667 var _this = this;
2668 var _bellmanFordDefaults = bellmanFordDefaults(options),
2669 weight = _bellmanFordDefaults.weight,
2670 directed = _bellmanFordDefaults.directed,
2671 root = _bellmanFordDefaults.root;
2672 var weightFn = weight;
2673 var eles = this;
2674 var cy = this.cy();
2675 var _this$byGroup = this.byGroup(),
2676 edges = _this$byGroup.edges,
2677 nodes = _this$byGroup.nodes;
2678 var numNodes = nodes.length;
2679 var infoMap = new Map$2();
2680 var hasNegativeWeightCycle = false;
2681 var negativeWeightCycles = [];
2682 root = cy.collection(root)[0]; // in case selector passed
2683
2684 edges.unmergeBy(function (edge) {
2685 return edge.isLoop();
2686 });
2687 var numEdges = edges.length;
2688 var getInfo = function getInfo(node) {
2689 var obj = infoMap.get(node.id());
2690 if (!obj) {
2691 obj = {};
2692 infoMap.set(node.id(), obj);
2693 }
2694 return obj;
2695 };
2696 var getNodeFromTo = function getNodeFromTo(to) {
2697 return (string(to) ? cy.$(to) : to)[0];
2698 };
2699 var distanceTo = function distanceTo(to) {
2700 return getInfo(getNodeFromTo(to)).dist;
2701 };
2702 var pathTo = function pathTo(to) {
2703 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2704 var end = getNodeFromTo(to);
2705 var path = [];
2706 var node = end;
2707 for (;;) {
2708 if (node == null) {
2709 return _this.spawn();
2710 }
2711 var _getInfo = getInfo(node),
2712 edge = _getInfo.edge,
2713 pred = _getInfo.pred;
2714 path.unshift(node[0]);
2715 if (node.same(thisStart) && path.length > 0) {
2716 break;
2717 }
2718 if (edge != null) {
2719 path.unshift(edge);
2720 }
2721 node = pred;
2722 }
2723 return eles.spawn(path);
2724 };
2725
2726 // Initializations { dist, pred, edge }
2727 for (var i = 0; i < numNodes; i++) {
2728 var node = nodes[i];
2729 var info = getInfo(node);
2730 if (node.same(root)) {
2731 info.dist = 0;
2732 } else {
2733 info.dist = Infinity;
2734 }
2735 info.pred = null;
2736 info.edge = null;
2737 }
2738
2739 // Edges relaxation
2740 var replacedEdge = false;
2741 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2742 var dist = info1.dist + weight;
2743 if (dist < info2.dist && !edge.same(info1.edge)) {
2744 info2.dist = dist;
2745 info2.pred = node1;
2746 info2.edge = edge;
2747 replacedEdge = true;
2748 }
2749 };
2750 for (var _i = 1; _i < numNodes; _i++) {
2751 replacedEdge = false;
2752 for (var e = 0; e < numEdges; e++) {
2753 var edge = edges[e];
2754 var src = edge.source();
2755 var tgt = edge.target();
2756 var _weight = weightFn(edge);
2757 var srcInfo = getInfo(src);
2758 var tgtInfo = getInfo(tgt);
2759 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight);
2760
2761 // If undirected graph, we need to take into account the 'reverse' edge
2762 if (!directed) {
2763 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2764 }
2765 }
2766 if (!replacedEdge) {
2767 break;
2768 }
2769 }
2770 if (replacedEdge) {
2771 // Check for negative weight cycles
2772 var negativeWeightCycleIds = [];
2773 for (var _e = 0; _e < numEdges; _e++) {
2774 var _edge = edges[_e];
2775 var _src = _edge.source();
2776 var _tgt = _edge.target();
2777 var _weight2 = weightFn(_edge);
2778 var srcDist = getInfo(_src).dist;
2779 var tgtDist = getInfo(_tgt).dist;
2780 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2781 if (!hasNegativeWeightCycle) {
2782 warn('Graph contains a negative weight cycle for Bellman-Ford');
2783 hasNegativeWeightCycle = true;
2784 }
2785 if (options.findNegativeWeightCycles !== false) {
2786 var negativeNodes = [];
2787 if (srcDist + _weight2 < tgtDist) {
2788 negativeNodes.push(_src);
2789 }
2790 if (!directed && tgtDist + _weight2 < srcDist) {
2791 negativeNodes.push(_tgt);
2792 }
2793 var numNegativeNodes = negativeNodes.length;
2794 for (var n = 0; n < numNegativeNodes; n++) {
2795 var start = negativeNodes[n];
2796 var cycle = [start];
2797 cycle.push(getInfo(start).edge);
2798 var _node = getInfo(start).pred;
2799 while (cycle.indexOf(_node) === -1) {
2800 cycle.push(_node);
2801 cycle.push(getInfo(_node).edge);
2802 _node = getInfo(_node).pred;
2803 }
2804 cycle = cycle.slice(cycle.indexOf(_node));
2805 var smallestId = cycle[0].id();
2806 var smallestIndex = 0;
2807 for (var c = 2; c < cycle.length; c += 2) {
2808 if (cycle[c].id() < smallestId) {
2809 smallestId = cycle[c].id();
2810 smallestIndex = c;
2811 }
2812 }
2813 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2814 cycle.push(cycle[0]);
2815 var cycleId = cycle.map(function (el) {
2816 return el.id();
2817 }).join(",");
2818 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2819 negativeWeightCycles.push(eles.spawn(cycle));
2820 negativeWeightCycleIds.push(cycleId);
2821 }
2822 }
2823 } else {
2824 break;
2825 }
2826 }
2827 }
2828 }
2829 return {
2830 distanceTo: distanceTo,
2831 pathTo: pathTo,
2832 hasNegativeWeightCycle: hasNegativeWeightCycle,
2833 negativeWeightCycles: negativeWeightCycles
2834 };
2835 } // bellmanFord
2836}; // elesfn
2837
2838var sqrt2 = Math.sqrt(2);
2839
2840// Function which colapses 2 (meta) nodes into one
2841// Updates the remaining edge lists
2842// Receives as a paramater the edge which causes the collapse
2843var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2844 if (remainingEdges.length === 0) {
2845 error("Karger-Stein must be run on a connected (sub)graph");
2846 }
2847 var edgeInfo = remainingEdges[edgeIndex];
2848 var sourceIn = edgeInfo[1];
2849 var targetIn = edgeInfo[2];
2850 var partition1 = nodeMap[sourceIn];
2851 var partition2 = nodeMap[targetIn];
2852 var newEdges = remainingEdges; // re-use array
2853
2854 // Delete all edges between partition1 and partition2
2855 for (var i = newEdges.length - 1; i >= 0; i--) {
2856 var edge = newEdges[i];
2857 var src = edge[1];
2858 var tgt = edge[2];
2859 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2860 newEdges.splice(i, 1);
2861 }
2862 }
2863
2864 // All edges pointing to partition2 should now point to partition1
2865 for (var _i = 0; _i < newEdges.length; _i++) {
2866 var _edge = newEdges[_i];
2867 if (_edge[1] === partition2) {
2868 // Check source
2869 newEdges[_i] = _edge.slice(); // copy
2870 newEdges[_i][1] = partition1;
2871 } else if (_edge[2] === partition2) {
2872 // Check target
2873 newEdges[_i] = _edge.slice(); // copy
2874 newEdges[_i][2] = partition1;
2875 }
2876 }
2877
2878 // Move all nodes from partition2 to partition1
2879 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2880 if (nodeMap[_i2] === partition2) {
2881 nodeMap[_i2] = partition1;
2882 }
2883 }
2884 return newEdges;
2885};
2886
2887// Contracts a graph until we reach a certain number of meta nodes
2888var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2889 while (size > sizeLimit) {
2890 // Choose an edge randomly
2891 var edgeIndex = Math.floor(Math.random() * remainingEdges.length);
2892
2893 // Collapse graph based on edge
2894 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2895 size--;
2896 }
2897 return remainingEdges;
2898};
2899var elesfn$p = {
2900 // Computes the minimum cut of an undirected graph
2901 // Returns the correct answer with high probability
2902 kargerStein: function kargerStein() {
2903 var _this = this;
2904 var _this$byGroup = this.byGroup(),
2905 nodes = _this$byGroup.nodes,
2906 edges = _this$byGroup.edges;
2907 edges.unmergeBy(function (edge) {
2908 return edge.isLoop();
2909 });
2910 var numNodes = nodes.length;
2911 var numEdges = edges.length;
2912 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2913 var stopSize = Math.floor(numNodes / sqrt2);
2914 if (numNodes < 2) {
2915 error('At least 2 nodes are required for Karger-Stein algorithm');
2916 return undefined;
2917 }
2918
2919 // Now store edge destination as indexes
2920 // Format for each edge (edge index, source node index, target node index)
2921 var edgeIndexes = [];
2922 for (var i = 0; i < numEdges; i++) {
2923 var e = edges[i];
2924 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2925 }
2926
2927 // We will store the best cut found here
2928 var minCutSize = Infinity;
2929 var minCutEdgeIndexes = [];
2930 var minCutNodeMap = new Array(numNodes);
2931
2932 // Initial meta node partition
2933 var metaNodeMap = new Array(numNodes);
2934 var metaNodeMap2 = new Array(numNodes);
2935 var copyNodesMap = function copyNodesMap(from, to) {
2936 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2937 to[_i3] = from[_i3];
2938 }
2939 };
2940
2941 // Main loop
2942 for (var iter = 0; iter <= numIter; iter++) {
2943 // Reset meta node partition
2944 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2945 metaNodeMap[_i4] = _i4;
2946 }
2947
2948 // Contract until stop point (stopSize nodes)
2949 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2950 var edgesState2 = edgesState.slice(); // copy
2951
2952 // Create a copy of the colapsed nodes state
2953 copyNodesMap(metaNodeMap, metaNodeMap2);
2954
2955 // Run 2 iterations starting in the stop state
2956 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2957 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2);
2958
2959 // Is any of the 2 results the best cut so far?
2960 if (res1.length <= res2.length && res1.length < minCutSize) {
2961 minCutSize = res1.length;
2962 minCutEdgeIndexes = res1;
2963 copyNodesMap(metaNodeMap, minCutNodeMap);
2964 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2965 minCutSize = res2.length;
2966 minCutEdgeIndexes = res2;
2967 copyNodesMap(metaNodeMap2, minCutNodeMap);
2968 }
2969 } // end of main loop
2970
2971 // Construct result
2972 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2973 return edges[e[0]];
2974 }));
2975 var partition1 = this.spawn();
2976 var partition2 = this.spawn();
2977
2978 // traverse metaNodeMap for best cut
2979 var witnessNodePartition = minCutNodeMap[0];
2980 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2981 var partitionId = minCutNodeMap[_i5];
2982 var node = nodes[_i5];
2983 if (partitionId === witnessNodePartition) {
2984 partition1.merge(node);
2985 } else {
2986 partition2.merge(node);
2987 }
2988 }
2989
2990 // construct components corresponding to each disjoint subset of nodes
2991 var constructComponent = function constructComponent(subset) {
2992 var component = _this.spawn();
2993 subset.forEach(function (node) {
2994 component.merge(node);
2995 node.connectedEdges().forEach(function (edge) {
2996 // ensure edge is within calling collection and edge is not in cut
2997 if (_this.contains(edge) && !cut.contains(edge)) {
2998 component.merge(edge);
2999 }
3000 });
3001 });
3002 return component;
3003 };
3004 var components = [constructComponent(partition1), constructComponent(partition2)];
3005 var ret = {
3006 cut: cut,
3007 components: components,
3008 // n.b. partitions are included to be compatible with the old api spec
3009 // (could be removed in a future major version)
3010 partition1: partition1,
3011 partition2: partition2
3012 };
3013 return ret;
3014 }
3015}; // elesfn
3016
3017var copyPosition = function copyPosition(p) {
3018 return {
3019 x: p.x,
3020 y: p.y
3021 };
3022};
3023var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3024 return {
3025 x: p.x * zoom + pan.x,
3026 y: p.y * zoom + pan.y
3027 };
3028};
3029var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3030 return {
3031 x: (p.x - pan.x) / zoom,
3032 y: (p.y - pan.y) / zoom
3033 };
3034};
3035var array2point = function array2point(arr) {
3036 return {
3037 x: arr[0],
3038 y: arr[1]
3039 };
3040};
3041var min = function min(arr) {
3042 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3043 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3044 var min = Infinity;
3045 for (var i = begin; i < end; i++) {
3046 var val = arr[i];
3047 if (isFinite(val)) {
3048 min = Math.min(val, min);
3049 }
3050 }
3051 return min;
3052};
3053var max = function max(arr) {
3054 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3055 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3056 var max = -Infinity;
3057 for (var i = begin; i < end; i++) {
3058 var val = arr[i];
3059 if (isFinite(val)) {
3060 max = Math.max(val, max);
3061 }
3062 }
3063 return max;
3064};
3065var mean = function mean(arr) {
3066 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3067 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3068 var total = 0;
3069 var n = 0;
3070 for (var i = begin; i < end; i++) {
3071 var val = arr[i];
3072 if (isFinite(val)) {
3073 total += val;
3074 n++;
3075 }
3076 }
3077 return total / n;
3078};
3079var median = function median(arr) {
3080 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3081 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3082 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3083 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3084 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3085 if (copy) {
3086 arr = arr.slice(begin, end);
3087 } else {
3088 if (end < arr.length) {
3089 arr.splice(end, arr.length - end);
3090 }
3091 if (begin > 0) {
3092 arr.splice(0, begin);
3093 }
3094 }
3095
3096 // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3097 var off = 0; // offset from non-finite values
3098 for (var i = arr.length - 1; i >= 0; i--) {
3099 var v = arr[i];
3100 if (includeHoles) {
3101 if (!isFinite(v)) {
3102 arr[i] = -Infinity;
3103 off++;
3104 }
3105 } else {
3106 // just remove it if we don't want to consider holes
3107 arr.splice(i, 1);
3108 }
3109 }
3110 if (sort) {
3111 arr.sort(function (a, b) {
3112 return a - b;
3113 }); // requires copy = true if you don't want to change the orig
3114 }
3115
3116 var len = arr.length;
3117 var mid = Math.floor(len / 2);
3118 if (len % 2 !== 0) {
3119 return arr[mid + 1 + off];
3120 } else {
3121 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3122 }
3123};
3124var deg2rad = function deg2rad(deg) {
3125 return Math.PI * deg / 180;
3126};
3127var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3128 return Math.atan2(dispY, dispX) - Math.PI / 2;
3129};
3130var log2 = Math.log2 || function (n) {
3131 return Math.log(n) / Math.log(2);
3132};
3133var signum = function signum(x) {
3134 if (x > 0) {
3135 return 1;
3136 } else if (x < 0) {
3137 return -1;
3138 } else {
3139 return 0;
3140 }
3141};
3142var dist = function dist(p1, p2) {
3143 return Math.sqrt(sqdist(p1, p2));
3144};
3145var sqdist = function sqdist(p1, p2) {
3146 var dx = p2.x - p1.x;
3147 var dy = p2.y - p1.y;
3148 return dx * dx + dy * dy;
3149};
3150var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3151 var length = v.length;
3152
3153 // First, get sum of all elements
3154 var total = 0;
3155 for (var i = 0; i < length; i++) {
3156 total += v[i];
3157 }
3158
3159 // Now, divide each by the sum of all elements
3160 for (var _i = 0; _i < length; _i++) {
3161 v[_i] = v[_i] / total;
3162 }
3163 return v;
3164};
3165
3166// from http://en.wikipedia.org/wiki/Bézier_curve#Quadratic_curves
3167var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3168 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3169};
3170var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3171 return {
3172 x: qbezierAt(p0.x, p1.x, p2.x, t),
3173 y: qbezierAt(p0.y, p1.y, p2.y, t)
3174 };
3175};
3176var lineAt = function lineAt(p0, p1, t, d) {
3177 var vec = {
3178 x: p1.x - p0.x,
3179 y: p1.y - p0.y
3180 };
3181 var vecDist = dist(p0, p1);
3182 var normVec = {
3183 x: vec.x / vecDist,
3184 y: vec.y / vecDist
3185 };
3186 t = t == null ? 0 : t;
3187 d = d != null ? d : t * vecDist;
3188 return {
3189 x: p0.x + normVec.x * d,
3190 y: p0.y + normVec.y * d
3191 };
3192};
3193var bound = function bound(min, val, max) {
3194 return Math.max(min, Math.min(max, val));
3195};
3196
3197// makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3198var makeBoundingBox = function makeBoundingBox(bb) {
3199 if (bb == null) {
3200 return {
3201 x1: Infinity,
3202 y1: Infinity,
3203 x2: -Infinity,
3204 y2: -Infinity,
3205 w: 0,
3206 h: 0
3207 };
3208 } else if (bb.x1 != null && bb.y1 != null) {
3209 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3210 return {
3211 x1: bb.x1,
3212 y1: bb.y1,
3213 x2: bb.x2,
3214 y2: bb.y2,
3215 w: bb.x2 - bb.x1,
3216 h: bb.y2 - bb.y1
3217 };
3218 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3219 return {
3220 x1: bb.x1,
3221 y1: bb.y1,
3222 x2: bb.x1 + bb.w,
3223 y2: bb.y1 + bb.h,
3224 w: bb.w,
3225 h: bb.h
3226 };
3227 }
3228 }
3229};
3230var copyBoundingBox = function copyBoundingBox(bb) {
3231 return {
3232 x1: bb.x1,
3233 x2: bb.x2,
3234 w: bb.w,
3235 y1: bb.y1,
3236 y2: bb.y2,
3237 h: bb.h
3238 };
3239};
3240var clearBoundingBox = function clearBoundingBox(bb) {
3241 bb.x1 = Infinity;
3242 bb.y1 = Infinity;
3243 bb.x2 = -Infinity;
3244 bb.y2 = -Infinity;
3245 bb.w = 0;
3246 bb.h = 0;
3247};
3248var shiftBoundingBox = function shiftBoundingBox(bb, dx, dy) {
3249 return {
3250 x1: bb.x1 + dx,
3251 x2: bb.x2 + dx,
3252 y1: bb.y1 + dy,
3253 y2: bb.y2 + dy,
3254 w: bb.w,
3255 h: bb.h
3256 };
3257};
3258var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3259 // update bb1 with bb2 bounds
3260
3261 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3262 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3263 bb1.w = bb1.x2 - bb1.x1;
3264 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3265 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3266 bb1.h = bb1.y2 - bb1.y1;
3267};
3268var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3269 bb.x1 = Math.min(bb.x1, x);
3270 bb.x2 = Math.max(bb.x2, x);
3271 bb.w = bb.x2 - bb.x1;
3272 bb.y1 = Math.min(bb.y1, y);
3273 bb.y2 = Math.max(bb.y2, y);
3274 bb.h = bb.y2 - bb.y1;
3275};
3276var expandBoundingBox = function expandBoundingBox(bb) {
3277 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3278 bb.x1 -= padding;
3279 bb.x2 += padding;
3280 bb.y1 -= padding;
3281 bb.y2 += padding;
3282 bb.w = bb.x2 - bb.x1;
3283 bb.h = bb.y2 - bb.y1;
3284 return bb;
3285};
3286var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3287 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3288 var top, right, bottom, left;
3289 if (padding.length === 1) {
3290 top = right = bottom = left = padding[0];
3291 } else if (padding.length === 2) {
3292 top = bottom = padding[0];
3293 left = right = padding[1];
3294 } else if (padding.length === 4) {
3295 var _padding = _slicedToArray(padding, 4);
3296 top = _padding[0];
3297 right = _padding[1];
3298 bottom = _padding[2];
3299 left = _padding[3];
3300 }
3301 bb.x1 -= left;
3302 bb.x2 += right;
3303 bb.y1 -= top;
3304 bb.y2 += bottom;
3305 bb.w = bb.x2 - bb.x1;
3306 bb.h = bb.y2 - bb.y1;
3307 return bb;
3308};
3309
3310// assign the values of bb2 into bb1
3311var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3312 bb1.x1 = bb2.x1;
3313 bb1.y1 = bb2.y1;
3314 bb1.x2 = bb2.x2;
3315 bb1.y2 = bb2.y2;
3316 bb1.w = bb1.x2 - bb1.x1;
3317 bb1.h = bb1.y2 - bb1.y1;
3318};
3319var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3320 // case: one bb to right of other
3321 if (bb1.x1 > bb2.x2) {
3322 return false;
3323 }
3324 if (bb2.x1 > bb1.x2) {
3325 return false;
3326 }
3327
3328 // case: one bb to left of other
3329 if (bb1.x2 < bb2.x1) {
3330 return false;
3331 }
3332 if (bb2.x2 < bb1.x1) {
3333 return false;
3334 }
3335
3336 // case: one bb above other
3337 if (bb1.y2 < bb2.y1) {
3338 return false;
3339 }
3340 if (bb2.y2 < bb1.y1) {
3341 return false;
3342 }
3343
3344 // case: one bb below other
3345 if (bb1.y1 > bb2.y2) {
3346 return false;
3347 }
3348 if (bb2.y1 > bb1.y2) {
3349 return false;
3350 }
3351
3352 // otherwise, must have some overlap
3353 return true;
3354};
3355var inBoundingBox = function inBoundingBox(bb, x, y) {
3356 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3357};
3358var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3359 return inBoundingBox(bb, pt.x, pt.y);
3360};
3361var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3362 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3363};
3364var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3365 var radius = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'auto';
3366 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : radius;
3367 var halfWidth = width / 2;
3368 var halfHeight = height / 2;
3369 cornerRadius = Math.min(cornerRadius, halfWidth, halfHeight);
3370 var doWidth = cornerRadius !== halfWidth,
3371 doHeight = cornerRadius !== halfHeight;
3372
3373 // Check intersections with straight line segments
3374 var straightLineIntersections;
3375
3376 // Top segment, left to right
3377 if (doWidth) {
3378 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3379 var topStartY = nodeY - halfHeight - padding;
3380 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3381 var topEndY = topStartY;
3382 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3383 if (straightLineIntersections.length > 0) {
3384 return straightLineIntersections;
3385 }
3386 }
3387
3388 // Right segment, top to bottom
3389 if (doHeight) {
3390 var rightStartX = nodeX + halfWidth + padding;
3391 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3392 var rightEndX = rightStartX;
3393 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3394 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3395 if (straightLineIntersections.length > 0) {
3396 return straightLineIntersections;
3397 }
3398 }
3399
3400 // Bottom segment, left to right
3401 if (doWidth) {
3402 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3403 var bottomStartY = nodeY + halfHeight + padding;
3404 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3405 var bottomEndY = bottomStartY;
3406 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3407 if (straightLineIntersections.length > 0) {
3408 return straightLineIntersections;
3409 }
3410 }
3411
3412 // Left segment, top to bottom
3413 if (doHeight) {
3414 var leftStartX = nodeX - halfWidth - padding;
3415 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3416 var leftEndX = leftStartX;
3417 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3418 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3419 if (straightLineIntersections.length > 0) {
3420 return straightLineIntersections;
3421 }
3422 }
3423
3424 // Check intersections with arc segments
3425 var arcIntersections;
3426
3427 // Top Left
3428 {
3429 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3430 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3431 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding);
3432
3433 // Ensure the intersection is on the desired quarter of the circle
3434 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3435 return [arcIntersections[0], arcIntersections[1]];
3436 }
3437 }
3438
3439 // Top Right
3440 {
3441 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3442 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3443 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding);
3444
3445 // Ensure the intersection is on the desired quarter of the circle
3446 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3447 return [arcIntersections[0], arcIntersections[1]];
3448 }
3449 }
3450
3451 // Bottom Right
3452 {
3453 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3454 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3455 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding);
3456
3457 // Ensure the intersection is on the desired quarter of the circle
3458 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3459 return [arcIntersections[0], arcIntersections[1]];
3460 }
3461 }
3462
3463 // Bottom Left
3464 {
3465 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3466 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3467 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding);
3468
3469 // Ensure the intersection is on the desired quarter of the circle
3470 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3471 return [arcIntersections[0], arcIntersections[1]];
3472 }
3473 }
3474 return []; // if nothing
3475};
3476
3477var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3478 var t = tolerance;
3479 var x1 = Math.min(lx1, lx2);
3480 var x2 = Math.max(lx1, lx2);
3481 var y1 = Math.min(ly1, ly2);
3482 var y2 = Math.max(ly1, ly2);
3483 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3484};
3485var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3486 var bb = {
3487 x1: Math.min(x1, x3, x2) - tolerance,
3488 x2: Math.max(x1, x3, x2) + tolerance,
3489 y1: Math.min(y1, y3, y2) - tolerance,
3490 y2: Math.max(y1, y3, y2) + tolerance
3491 };
3492
3493 // if outside the rough bounding box for the bezier, then it can't be a hit
3494 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3495 // console.log('bezier out of rough bb')
3496 return false;
3497 } else {
3498 // console.log('do more expensive check');
3499 return true;
3500 }
3501};
3502var solveQuadratic = function solveQuadratic(a, b, c, val) {
3503 c -= val;
3504 var r = b * b - 4 * a * c;
3505 if (r < 0) {
3506 return [];
3507 }
3508 var sqrtR = Math.sqrt(r);
3509 var denom = 2 * a;
3510 var root1 = (-b + sqrtR) / denom;
3511 var root2 = (-b - sqrtR) / denom;
3512 return [root1, root2];
3513};
3514var solveCubic = function solveCubic(a, b, c, d, result) {
3515 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3516 // r is the real component, i is the imaginary component
3517
3518 // An implementation of the Cardano method from the year 1545
3519 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3520
3521 var epsilon = 0.00001;
3522
3523 // avoid division by zero while keeping the overall expression close in value
3524 if (a === 0) {
3525 a = epsilon;
3526 }
3527 b /= a;
3528 c /= a;
3529 d /= a;
3530 var discriminant, q, r, dum1, s, t, term1, r13;
3531 q = (3.0 * c - b * b) / 9.0;
3532 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3533 r /= 54.0;
3534 discriminant = q * q * q + r * r;
3535 result[1] = 0;
3536 term1 = b / 3.0;
3537 if (discriminant > 0) {
3538 s = r + Math.sqrt(discriminant);
3539 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3540 t = r - Math.sqrt(discriminant);
3541 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3542 result[0] = -term1 + s + t;
3543 term1 += (s + t) / 2.0;
3544 result[4] = result[2] = -term1;
3545 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3546 result[3] = term1;
3547 result[5] = -term1;
3548 return;
3549 }
3550 result[5] = result[3] = 0;
3551 if (discriminant === 0) {
3552 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3553 result[0] = -term1 + 2.0 * r13;
3554 result[4] = result[2] = -(r13 + term1);
3555 return;
3556 }
3557 q = -q;
3558 dum1 = q * q * q;
3559 dum1 = Math.acos(r / Math.sqrt(dum1));
3560 r13 = 2.0 * Math.sqrt(q);
3561 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3562 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3563 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3564 return;
3565};
3566var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3567 // Find minimum distance by using the minimum of the distance
3568 // function between the given point and the curve
3569
3570 // This gives the coefficients of the resulting cubic equation
3571 // whose roots tell us where a possible minimum is
3572 // (Coefficients are divided by 4)
3573
3574 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;
3575 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;
3576 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;
3577 var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y;
3578
3579 // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
3580
3581 var roots = [];
3582
3583 // Use the cubic solving algorithm
3584 solveCubic(a, b, c, d, roots);
3585 var zeroThreshold = 0.0000001;
3586 var params = [];
3587 for (var index = 0; index < 6; index += 2) {
3588 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3589 params.push(roots[index]);
3590 }
3591 }
3592 params.push(1.0);
3593 params.push(0.0);
3594 var minDistanceSquared = -1;
3595 var curX, curY, distSquared;
3596 for (var i = 0; i < params.length; i++) {
3597 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3598 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3599 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2);
3600 // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3601 if (minDistanceSquared >= 0) {
3602 if (distSquared < minDistanceSquared) {
3603 minDistanceSquared = distSquared;
3604 }
3605 } else {
3606 minDistanceSquared = distSquared;
3607 }
3608 }
3609 return minDistanceSquared;
3610};
3611var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3612 var offset = [x - x1, y - y1];
3613 var line = [x2 - x1, y2 - y1];
3614 var lineSq = line[0] * line[0] + line[1] * line[1];
3615 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3616 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3617 var adjSq = dotProduct * dotProduct / lineSq;
3618 if (dotProduct < 0) {
3619 return hypSq;
3620 }
3621 if (adjSq > lineSq) {
3622 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3623 }
3624 return hypSq - adjSq;
3625};
3626var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3627 var x1, y1, x2, y2;
3628 var y3;
3629
3630 // Intersect with vertical line through (x, y)
3631 var up = 0;
3632 // let down = 0;
3633 for (var i = 0; i < points.length / 2; i++) {
3634 x1 = points[i * 2];
3635 y1 = points[i * 2 + 1];
3636 if (i + 1 < points.length / 2) {
3637 x2 = points[(i + 1) * 2];
3638 y2 = points[(i + 1) * 2 + 1];
3639 } else {
3640 x2 = points[(i + 1 - points.length / 2) * 2];
3641 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3642 }
3643 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3644 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3645 if (y3 > y) {
3646 up++;
3647 }
3648
3649 // if( y3 < y ){
3650 // down++;
3651 // }
3652 } else {
3653 continue;
3654 }
3655 }
3656 if (up % 2 === 0) {
3657 return false;
3658 } else {
3659 return true;
3660 }
3661};
3662var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3663 var transformedPoints = new Array(basePoints.length);
3664
3665 // Gives negative angle
3666 var angle;
3667 if (direction[0] != null) {
3668 angle = Math.atan(direction[1] / direction[0]);
3669 if (direction[0] < 0) {
3670 angle = angle + Math.PI / 2;
3671 } else {
3672 angle = -angle - Math.PI / 2;
3673 }
3674 } else {
3675 angle = direction;
3676 }
3677 var cos = Math.cos(-angle);
3678 var sin = Math.sin(-angle);
3679
3680 // console.log("base: " + basePoints);
3681 for (var i = 0; i < transformedPoints.length / 2; i++) {
3682 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3683 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3684 transformedPoints[i * 2] += centerX;
3685 transformedPoints[i * 2 + 1] += centerY;
3686 }
3687 var points;
3688 if (padding > 0) {
3689 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3690 points = joinLines(expandedLineSet);
3691 } else {
3692 points = transformedPoints;
3693 }
3694 return pointInsidePolygonPoints(x, y, points);
3695};
3696var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height, corners) {
3697 var cutPolygonPoints = new Array(basePoints.length * 2);
3698 for (var i = 0; i < corners.length; i++) {
3699 var corner = corners[i];
3700 cutPolygonPoints[i * 4 + 0] = corner.startX;
3701 cutPolygonPoints[i * 4 + 1] = corner.startY;
3702 cutPolygonPoints[i * 4 + 2] = corner.stopX;
3703 cutPolygonPoints[i * 4 + 3] = corner.stopY;
3704 var squaredDistance = Math.pow(corner.cx - x, 2) + Math.pow(corner.cy - y, 2);
3705 if (squaredDistance <= Math.pow(corner.radius, 2)) {
3706 return true;
3707 }
3708 }
3709 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3710};
3711var joinLines = function joinLines(lineSet) {
3712 var vertices = new Array(lineSet.length / 2);
3713 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3714 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3715 for (var i = 0; i < lineSet.length / 4; i++) {
3716 currentLineStartX = lineSet[i * 4];
3717 currentLineStartY = lineSet[i * 4 + 1];
3718 currentLineEndX = lineSet[i * 4 + 2];
3719 currentLineEndY = lineSet[i * 4 + 3];
3720 if (i < lineSet.length / 4 - 1) {
3721 nextLineStartX = lineSet[(i + 1) * 4];
3722 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3723 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3724 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3725 } else {
3726 nextLineStartX = lineSet[0];
3727 nextLineStartY = lineSet[1];
3728 nextLineEndX = lineSet[2];
3729 nextLineEndY = lineSet[3];
3730 }
3731 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3732 vertices[i * 2] = intersection[0];
3733 vertices[i * 2 + 1] = intersection[1];
3734 }
3735 return vertices;
3736};
3737var expandPolygon = function expandPolygon(points, pad) {
3738 var expandedLineSet = new Array(points.length * 2);
3739 var currentPointX, currentPointY, nextPointX, nextPointY;
3740 for (var i = 0; i < points.length / 2; i++) {
3741 currentPointX = points[i * 2];
3742 currentPointY = points[i * 2 + 1];
3743 if (i < points.length / 2 - 1) {
3744 nextPointX = points[(i + 1) * 2];
3745 nextPointY = points[(i + 1) * 2 + 1];
3746 } else {
3747 nextPointX = points[0];
3748 nextPointY = points[1];
3749 }
3750
3751 // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3752
3753 // Assume CCW polygon winding
3754
3755 var offsetX = nextPointY - currentPointY;
3756 var offsetY = -(nextPointX - currentPointX);
3757
3758 // Normalize
3759 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3760 var normalizedOffsetX = offsetX / offsetLength;
3761 var normalizedOffsetY = offsetY / offsetLength;
3762 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3763 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3764 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3765 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3766 }
3767 return expandedLineSet;
3768};
3769var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3770 var dispX = centerX - x;
3771 var dispY = centerY - y;
3772 dispX /= ellipseWradius;
3773 dispY /= ellipseHradius;
3774 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3775 var newLength = len - 1;
3776 if (newLength < 0) {
3777 return [];
3778 }
3779 var lenProportion = newLength / len;
3780 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3781};
3782var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3783 x -= centerX;
3784 y -= centerY;
3785 x /= width / 2 + padding;
3786 y /= height / 2 + padding;
3787 return x * x + y * y <= 1;
3788};
3789
3790// Returns intersections of increasing distance from line's start point
3791var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3792 // Calculate d, direction vector of line
3793 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3794 var f = [x1 - centerX, y1 - centerY];
3795 var a = d[0] * d[0] + d[1] * d[1];
3796 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3797 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3798 var discriminant = b * b - 4 * a * c;
3799 if (discriminant < 0) {
3800 return [];
3801 }
3802 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3803 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3804 var tMin = Math.min(t1, t2);
3805 var tMax = Math.max(t1, t2);
3806 var inRangeParams = [];
3807 if (tMin >= 0 && tMin <= 1) {
3808 inRangeParams.push(tMin);
3809 }
3810 if (tMax >= 0 && tMax <= 1) {
3811 inRangeParams.push(tMax);
3812 }
3813 if (inRangeParams.length === 0) {
3814 return [];
3815 }
3816 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3817 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3818 if (inRangeParams.length > 1) {
3819 if (inRangeParams[0] == inRangeParams[1]) {
3820 return [nearIntersectionX, nearIntersectionY];
3821 } else {
3822 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3823 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3824 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3825 }
3826 } else {
3827 return [nearIntersectionX, nearIntersectionY];
3828 }
3829};
3830var midOfThree = function midOfThree(a, b, c) {
3831 if (b <= a && a <= c || c <= a && a <= b) {
3832 return a;
3833 } else if (a <= b && b <= c || c <= b && b <= a) {
3834 return b;
3835 } else {
3836 return c;
3837 }
3838};
3839
3840// (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3841var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3842 var dx13 = x1 - x3;
3843 var dx21 = x2 - x1;
3844 var dx43 = x4 - x3;
3845 var dy13 = y1 - y3;
3846 var dy21 = y2 - y1;
3847 var dy43 = y4 - y3;
3848 var ua_t = dx43 * dy13 - dy43 * dx13;
3849 var ub_t = dx21 * dy13 - dy21 * dx13;
3850 var u_b = dy43 * dx21 - dx43 * dy21;
3851 if (u_b !== 0) {
3852 var ua = ua_t / u_b;
3853 var ub = ub_t / u_b;
3854 var flptThreshold = 0.001;
3855 var _min = 0 - flptThreshold;
3856 var _max = 1 + flptThreshold;
3857 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3858 return [x1 + ua * dx21, y1 + ua * dy21];
3859 } else {
3860 if (!infiniteLines) {
3861 return [];
3862 } else {
3863 return [x1 + ua * dx21, y1 + ua * dy21];
3864 }
3865 }
3866 } else {
3867 if (ua_t === 0 || ub_t === 0) {
3868 // Parallel, coincident lines. Check if overlap
3869
3870 // Check endpoint of second line
3871 if (midOfThree(x1, x2, x4) === x4) {
3872 return [x4, y4];
3873 }
3874
3875 // Check start point of second line
3876 if (midOfThree(x1, x2, x3) === x3) {
3877 return [x3, y3];
3878 }
3879
3880 // Endpoint of first line
3881 if (midOfThree(x3, x4, x2) === x2) {
3882 return [x2, y2];
3883 }
3884 return [];
3885 } else {
3886 // Parallel, non-coincident
3887 return [];
3888 }
3889 }
3890};
3891
3892// math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3893// intersect a node polygon (pts transformed)
3894//
3895// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3896// intersect the points (no transform)
3897var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3898 var intersections = [];
3899 var intersection;
3900 var transformedPoints = new Array(basePoints.length);
3901 var doTransform = true;
3902 if (width == null) {
3903 doTransform = false;
3904 }
3905 var points;
3906 if (doTransform) {
3907 for (var i = 0; i < transformedPoints.length / 2; i++) {
3908 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3909 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3910 }
3911 if (padding > 0) {
3912 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3913 points = joinLines(expandedLineSet);
3914 } else {
3915 points = transformedPoints;
3916 }
3917 } else {
3918 points = basePoints;
3919 }
3920 var currentX, currentY, nextX, nextY;
3921 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3922 currentX = points[_i2 * 2];
3923 currentY = points[_i2 * 2 + 1];
3924 if (_i2 < points.length / 2 - 1) {
3925 nextX = points[(_i2 + 1) * 2];
3926 nextY = points[(_i2 + 1) * 2 + 1];
3927 } else {
3928 nextX = points[0];
3929 nextY = points[1];
3930 }
3931 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3932 if (intersection.length !== 0) {
3933 intersections.push(intersection[0], intersection[1]);
3934 }
3935 }
3936 return intersections;
3937};
3938var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding, corners) {
3939 var intersections = [];
3940 var intersection;
3941 var lines = new Array(basePoints.length * 2);
3942 corners.forEach(function (corner, i) {
3943 if (i === 0) {
3944 lines[lines.length - 2] = corner.startX;
3945 lines[lines.length - 1] = corner.startY;
3946 } else {
3947 lines[i * 4 - 2] = corner.startX;
3948 lines[i * 4 - 1] = corner.startY;
3949 }
3950 lines[i * 4] = corner.stopX;
3951 lines[i * 4 + 1] = corner.stopY;
3952 intersection = intersectLineCircle(x, y, centerX, centerY, corner.cx, corner.cy, corner.radius);
3953 if (intersection.length !== 0) {
3954 intersections.push(intersection[0], intersection[1]);
3955 }
3956 });
3957 for (var i = 0; i < lines.length / 4; i++) {
3958 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[i * 4], lines[i * 4 + 1], lines[i * 4 + 2], lines[i * 4 + 3], false);
3959 if (intersection.length !== 0) {
3960 intersections.push(intersection[0], intersection[1]);
3961 }
3962 }
3963 if (intersections.length > 2) {
3964 var lowestIntersection = [intersections[0], intersections[1]];
3965 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3966 for (var _i3 = 1; _i3 < intersections.length / 2; _i3++) {
3967 var squaredDistance = Math.pow(intersections[_i3 * 2] - x, 2) + Math.pow(intersections[_i3 * 2 + 1] - y, 2);
3968 if (squaredDistance <= lowestSquaredDistance) {
3969 lowestIntersection[0] = intersections[_i3 * 2];
3970 lowestIntersection[1] = intersections[_i3 * 2 + 1];
3971 lowestSquaredDistance = squaredDistance;
3972 }
3973 }
3974 return lowestIntersection;
3975 }
3976 return intersections;
3977};
3978var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3979 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3980 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3981 var lenRatio = (length - amount) / length;
3982 if (lenRatio < 0) {
3983 lenRatio = 0.00001;
3984 }
3985 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3986};
3987var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3988 var points = generateUnitNgonPoints(sides, rotationRadians);
3989 points = fitPolygonToSquare(points);
3990 return points;
3991};
3992var fitPolygonToSquare = function fitPolygonToSquare(points) {
3993 var x, y;
3994 var sides = points.length / 2;
3995 var minX = Infinity,
3996 minY = Infinity,
3997 maxX = -Infinity,
3998 maxY = -Infinity;
3999 for (var i = 0; i < sides; i++) {
4000 x = points[2 * i];
4001 y = points[2 * i + 1];
4002 minX = Math.min(minX, x);
4003 maxX = Math.max(maxX, x);
4004 minY = Math.min(minY, y);
4005 maxY = Math.max(maxY, y);
4006 }
4007
4008 // stretch factors
4009 var sx = 2 / (maxX - minX);
4010 var sy = 2 / (maxY - minY);
4011 for (var _i4 = 0; _i4 < sides; _i4++) {
4012 x = points[2 * _i4] = points[2 * _i4] * sx;
4013 y = points[2 * _i4 + 1] = points[2 * _i4 + 1] * sy;
4014 minX = Math.min(minX, x);
4015 maxX = Math.max(maxX, x);
4016 minY = Math.min(minY, y);
4017 maxY = Math.max(maxY, y);
4018 }
4019 if (minY < -1) {
4020 for (var _i5 = 0; _i5 < sides; _i5++) {
4021 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] + (-1 - minY);
4022 }
4023 }
4024 return points;
4025};
4026var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4027 var increment = 1.0 / sides * 2 * Math.PI;
4028 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4029 startAngle += rotationRadians;
4030 var points = new Array(sides * 2);
4031 var currentAngle;
4032 for (var i = 0; i < sides; i++) {
4033 currentAngle = i * increment + startAngle;
4034 points[2 * i] = Math.cos(currentAngle); // x
4035 points[2 * i + 1] = Math.sin(-currentAngle); // y
4036 }
4037
4038 return points;
4039};
4040
4041// Set the default radius, unless half of width or height is smaller than default
4042var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4043 return Math.min(width / 4, height / 4, 8);
4044};
4045
4046// Set the default radius
4047var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4048 return Math.min(width / 10, height / 10, 8);
4049};
4050var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4051 return 8;
4052};
4053var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4054 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4055};
4056
4057// get curve width, height, and control point position offsets as a percentage of node height / width
4058var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4059 return {
4060 heightOffset: Math.min(15, 0.05 * height),
4061 widthOffset: Math.min(100, 0.25 * width),
4062 ctrlPtOffsetPct: 0.05
4063 };
4064};
4065
4066var pageRankDefaults = defaults$g({
4067 dampingFactor: 0.8,
4068 precision: 0.000001,
4069 iterations: 200,
4070 weight: function weight(edge) {
4071 return 1;
4072 }
4073});
4074var elesfn$o = {
4075 pageRank: function pageRank(options) {
4076 var _pageRankDefaults = pageRankDefaults(options),
4077 dampingFactor = _pageRankDefaults.dampingFactor,
4078 precision = _pageRankDefaults.precision,
4079 iterations = _pageRankDefaults.iterations,
4080 weight = _pageRankDefaults.weight;
4081 var cy = this._private.cy;
4082 var _this$byGroup = this.byGroup(),
4083 nodes = _this$byGroup.nodes,
4084 edges = _this$byGroup.edges;
4085 var numNodes = nodes.length;
4086 var numNodesSqd = numNodes * numNodes;
4087 var numEdges = edges.length;
4088
4089 // Construct transposed adjacency matrix
4090 // First lets have a zeroed matrix of the right size
4091 // We'll also keep track of the sum of each column
4092 var matrix = new Array(numNodesSqd);
4093 var columnSum = new Array(numNodes);
4094 var additionalProb = (1 - dampingFactor) / numNodes;
4095
4096 // Create null matrix
4097 for (var i = 0; i < numNodes; i++) {
4098 for (var j = 0; j < numNodes; j++) {
4099 var n = i * numNodes + j;
4100 matrix[n] = 0;
4101 }
4102 columnSum[i] = 0;
4103 }
4104
4105 // Now, process edges
4106 for (var _i = 0; _i < numEdges; _i++) {
4107 var edge = edges[_i];
4108 var srcId = edge.data('source');
4109 var tgtId = edge.data('target');
4110
4111 // Don't include loops in the matrix
4112 if (srcId === tgtId) {
4113 continue;
4114 }
4115 var s = nodes.indexOfId(srcId);
4116 var t = nodes.indexOfId(tgtId);
4117 var w = weight(edge);
4118 var _n = t * numNodes + s;
4119
4120 // Update matrix
4121 matrix[_n] += w;
4122
4123 // Update column sum
4124 columnSum[s] += w;
4125 }
4126
4127 // Add additional probability based on damping factor
4128 // Also, take into account columns that have sum = 0
4129 var p = 1.0 / numNodes + additionalProb; // Shorthand
4130
4131 // Traverse matrix, column by column
4132 for (var _j = 0; _j < numNodes; _j++) {
4133 if (columnSum[_j] === 0) {
4134 // No 'links' out from node jth, assume equal probability for each possible node
4135 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4136 var _n2 = _i2 * numNodes + _j;
4137 matrix[_n2] = p;
4138 }
4139 } else {
4140 // Node jth has outgoing link, compute normalized probabilities
4141 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4142 var _n3 = _i3 * numNodes + _j;
4143 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4144 }
4145 }
4146 }
4147
4148 // Compute dominant eigenvector using power method
4149 var eigenvector = new Array(numNodes);
4150 var temp = new Array(numNodes);
4151 var previous;
4152
4153 // Start with a vector of all 1's
4154 // Also, initialize a null vector which will be used as shorthand
4155 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4156 eigenvector[_i4] = 1;
4157 }
4158 for (var iter = 0; iter < iterations; iter++) {
4159 // Temp array with all 0's
4160 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4161 temp[_i5] = 0;
4162 }
4163
4164 // Multiply matrix with previous result
4165 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4166 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4167 var _n4 = _i6 * numNodes + _j2;
4168 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4169 }
4170 }
4171 inPlaceSumNormalize(temp);
4172 previous = eigenvector;
4173 eigenvector = temp;
4174 temp = previous;
4175 var diff = 0;
4176 // Compute difference (squared module) of both vectors
4177 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4178 var delta = previous[_i7] - eigenvector[_i7];
4179 diff += delta * delta;
4180 }
4181
4182 // If difference is less than the desired threshold, stop iterating
4183 if (diff < precision) {
4184 break;
4185 }
4186 }
4187
4188 // Construct result
4189 var res = {
4190 rank: function rank(node) {
4191 node = cy.collection(node)[0];
4192 return eigenvector[nodes.indexOf(node)];
4193 }
4194 };
4195 return res;
4196 } // pageRank
4197}; // elesfn
4198
4199var defaults$f = defaults$g({
4200 root: null,
4201 weight: function weight(edge) {
4202 return 1;
4203 },
4204 directed: false,
4205 alpha: 0
4206});
4207var elesfn$n = {
4208 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4209 options = defaults$f(options);
4210 var cy = this.cy();
4211 var nodes = this.nodes();
4212 var numNodes = nodes.length;
4213 if (!options.directed) {
4214 var degrees = {};
4215 var maxDegree = 0;
4216 for (var i = 0; i < numNodes; i++) {
4217 var node = nodes[i];
4218
4219 // add current node to the current options object and call degreeCentrality
4220 options.root = node;
4221 var currDegree = this.degreeCentrality(options);
4222 if (maxDegree < currDegree.degree) {
4223 maxDegree = currDegree.degree;
4224 }
4225 degrees[node.id()] = currDegree.degree;
4226 }
4227 return {
4228 degree: function degree(node) {
4229 if (maxDegree === 0) {
4230 return 0;
4231 }
4232 if (string(node)) {
4233 // from is a selector string
4234 node = cy.filter(node);
4235 }
4236 return degrees[node.id()] / maxDegree;
4237 }
4238 };
4239 } else {
4240 var indegrees = {};
4241 var outdegrees = {};
4242 var maxIndegree = 0;
4243 var maxOutdegree = 0;
4244 for (var _i = 0; _i < numNodes; _i++) {
4245 var _node = nodes[_i];
4246 var id = _node.id();
4247
4248 // add current node to the current options object and call degreeCentrality
4249 options.root = _node;
4250 var _currDegree = this.degreeCentrality(options);
4251 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4252 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4253 indegrees[id] = _currDegree.indegree;
4254 outdegrees[id] = _currDegree.outdegree;
4255 }
4256 return {
4257 indegree: function indegree(node) {
4258 if (maxIndegree == 0) {
4259 return 0;
4260 }
4261 if (string(node)) {
4262 // from is a selector string
4263 node = cy.filter(node);
4264 }
4265 return indegrees[node.id()] / maxIndegree;
4266 },
4267 outdegree: function outdegree(node) {
4268 if (maxOutdegree === 0) {
4269 return 0;
4270 }
4271 if (string(node)) {
4272 // from is a selector string
4273 node = cy.filter(node);
4274 }
4275 return outdegrees[node.id()] / maxOutdegree;
4276 }
4277 };
4278 }
4279 },
4280 // degreeCentralityNormalized
4281
4282 // Implemented from the algorithm in Opsahl's paper
4283 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4284 // check the heading 2 "Degree"
4285 degreeCentrality: function degreeCentrality(options) {
4286 options = defaults$f(options);
4287 var cy = this.cy();
4288 var callingEles = this;
4289 var _options = options,
4290 root = _options.root,
4291 weight = _options.weight,
4292 directed = _options.directed,
4293 alpha = _options.alpha;
4294 root = cy.collection(root)[0];
4295 if (!directed) {
4296 var connEdges = root.connectedEdges().intersection(callingEles);
4297 var k = connEdges.length;
4298 var s = 0;
4299
4300 // Now, sum edge weights
4301 for (var i = 0; i < connEdges.length; i++) {
4302 s += weight(connEdges[i]);
4303 }
4304 return {
4305 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4306 };
4307 } else {
4308 var edges = root.connectedEdges();
4309 var incoming = edges.filter(function (edge) {
4310 return edge.target().same(root) && callingEles.has(edge);
4311 });
4312 var outgoing = edges.filter(function (edge) {
4313 return edge.source().same(root) && callingEles.has(edge);
4314 });
4315 var k_in = incoming.length;
4316 var k_out = outgoing.length;
4317 var s_in = 0;
4318 var s_out = 0;
4319
4320 // Now, sum incoming edge weights
4321 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4322 s_in += weight(incoming[_i2]);
4323 }
4324
4325 // Now, sum outgoing edge weights
4326 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4327 s_out += weight(outgoing[_i3]);
4328 }
4329 return {
4330 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4331 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4332 };
4333 }
4334 } // degreeCentrality
4335}; // elesfn
4336
4337// nice, short mathematical alias
4338elesfn$n.dc = elesfn$n.degreeCentrality;
4339elesfn$n.dcn = elesfn$n.degreeCentralityNormalised = elesfn$n.degreeCentralityNormalized;
4340
4341var defaults$e = defaults$g({
4342 harmonic: true,
4343 weight: function weight() {
4344 return 1;
4345 },
4346 directed: false,
4347 root: null
4348});
4349var elesfn$m = {
4350 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4351 var _defaults = defaults$e(options),
4352 harmonic = _defaults.harmonic,
4353 weight = _defaults.weight,
4354 directed = _defaults.directed;
4355 var cy = this.cy();
4356 var closenesses = {};
4357 var maxCloseness = 0;
4358 var nodes = this.nodes();
4359 var fw = this.floydWarshall({
4360 weight: weight,
4361 directed: directed
4362 });
4363
4364 // Compute closeness for every node and find the maximum closeness
4365 for (var i = 0; i < nodes.length; i++) {
4366 var currCloseness = 0;
4367 var node_i = nodes[i];
4368 for (var j = 0; j < nodes.length; j++) {
4369 if (i !== j) {
4370 var d = fw.distance(node_i, nodes[j]);
4371 if (harmonic) {
4372 currCloseness += 1 / d;
4373 } else {
4374 currCloseness += d;
4375 }
4376 }
4377 }
4378 if (!harmonic) {
4379 currCloseness = 1 / currCloseness;
4380 }
4381 if (maxCloseness < currCloseness) {
4382 maxCloseness = currCloseness;
4383 }
4384 closenesses[node_i.id()] = currCloseness;
4385 }
4386 return {
4387 closeness: function closeness(node) {
4388 if (maxCloseness == 0) {
4389 return 0;
4390 }
4391 if (string(node)) {
4392 // from is a selector string
4393 node = cy.filter(node)[0].id();
4394 } else {
4395 // from is a node
4396 node = node.id();
4397 }
4398 return closenesses[node] / maxCloseness;
4399 }
4400 };
4401 },
4402 // Implemented from pseudocode from wikipedia
4403 closenessCentrality: function closenessCentrality(options) {
4404 var _defaults2 = defaults$e(options),
4405 root = _defaults2.root,
4406 weight = _defaults2.weight,
4407 directed = _defaults2.directed,
4408 harmonic = _defaults2.harmonic;
4409 root = this.filter(root)[0];
4410
4411 // we need distance from this node to every other node
4412 var dijkstra = this.dijkstra({
4413 root: root,
4414 weight: weight,
4415 directed: directed
4416 });
4417 var totalDistance = 0;
4418 var nodes = this.nodes();
4419 for (var i = 0; i < nodes.length; i++) {
4420 var n = nodes[i];
4421 if (!n.same(root)) {
4422 var d = dijkstra.distanceTo(n);
4423 if (harmonic) {
4424 totalDistance += 1 / d;
4425 } else {
4426 totalDistance += d;
4427 }
4428 }
4429 }
4430 return harmonic ? totalDistance : 1 / totalDistance;
4431 } // closenessCentrality
4432}; // elesfn
4433
4434// nice, short mathematical alias
4435elesfn$m.cc = elesfn$m.closenessCentrality;
4436elesfn$m.ccn = elesfn$m.closenessCentralityNormalised = elesfn$m.closenessCentralityNormalized;
4437
4438var defaults$d = defaults$g({
4439 weight: null,
4440 directed: false
4441});
4442var elesfn$l = {
4443 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4444 betweennessCentrality: function betweennessCentrality(options) {
4445 var _defaults = defaults$d(options),
4446 directed = _defaults.directed,
4447 weight = _defaults.weight;
4448 var weighted = weight != null;
4449 var cy = this.cy();
4450
4451 // starting
4452 var V = this.nodes();
4453 var A = {};
4454 var _C = {};
4455 var max = 0;
4456 var C = {
4457 set: function set(key, val) {
4458 _C[key] = val;
4459 if (val > max) {
4460 max = val;
4461 }
4462 },
4463 get: function get(key) {
4464 return _C[key];
4465 }
4466 };
4467
4468 // A contains the neighborhoods of every node
4469 for (var i = 0; i < V.length; i++) {
4470 var v = V[i];
4471 var vid = v.id();
4472 if (directed) {
4473 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4474 } else {
4475 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4476 }
4477
4478 C.set(vid, 0);
4479 }
4480 var _loop = function _loop(s) {
4481 var sid = V[s].id();
4482 var S = []; // stack
4483 var P = {};
4484 var g = {};
4485 var d = {};
4486 var Q = new heap(function (a, b) {
4487 return d[a] - d[b];
4488 }); // queue
4489
4490 // init dictionaries
4491 for (var _i = 0; _i < V.length; _i++) {
4492 var _vid = V[_i].id();
4493 P[_vid] = [];
4494 g[_vid] = 0;
4495 d[_vid] = Infinity;
4496 }
4497 g[sid] = 1; // sigma
4498 d[sid] = 0; // distance to s
4499
4500 Q.push(sid);
4501 while (!Q.empty()) {
4502 var _v = Q.pop();
4503 S.push(_v);
4504 if (weighted) {
4505 for (var j = 0; j < A[_v].length; j++) {
4506 var w = A[_v][j];
4507 var vEle = cy.getElementById(_v);
4508 var edge = void 0;
4509 if (vEle.edgesTo(w).length > 0) {
4510 edge = vEle.edgesTo(w)[0];
4511 } else {
4512 edge = w.edgesTo(vEle)[0];
4513 }
4514 var edgeWeight = weight(edge);
4515 w = w.id();
4516 if (d[w] > d[_v] + edgeWeight) {
4517 d[w] = d[_v] + edgeWeight;
4518 if (Q.nodes.indexOf(w) < 0) {
4519 //if w is not in Q
4520 Q.push(w);
4521 } else {
4522 // update position if w is in Q
4523 Q.updateItem(w);
4524 }
4525 g[w] = 0;
4526 P[w] = [];
4527 }
4528 if (d[w] == d[_v] + edgeWeight) {
4529 g[w] = g[w] + g[_v];
4530 P[w].push(_v);
4531 }
4532 }
4533 } else {
4534 for (var _j = 0; _j < A[_v].length; _j++) {
4535 var _w = A[_v][_j].id();
4536 if (d[_w] == Infinity) {
4537 Q.push(_w);
4538 d[_w] = d[_v] + 1;
4539 }
4540 if (d[_w] == d[_v] + 1) {
4541 g[_w] = g[_w] + g[_v];
4542 P[_w].push(_v);
4543 }
4544 }
4545 }
4546 }
4547 var e = {};
4548 for (var _i2 = 0; _i2 < V.length; _i2++) {
4549 e[V[_i2].id()] = 0;
4550 }
4551 while (S.length > 0) {
4552 var _w2 = S.pop();
4553 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4554 var _v2 = P[_w2][_j2];
4555 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4556 }
4557 if (_w2 != V[s].id()) {
4558 C.set(_w2, C.get(_w2) + e[_w2]);
4559 }
4560 }
4561 };
4562 for (var s = 0; s < V.length; s++) {
4563 _loop(s);
4564 }
4565 var ret = {
4566 betweenness: function betweenness(node) {
4567 var id = cy.collection(node).id();
4568 return C.get(id);
4569 },
4570 betweennessNormalized: function betweennessNormalized(node) {
4571 if (max == 0) {
4572 return 0;
4573 }
4574 var id = cy.collection(node).id();
4575 return C.get(id) / max;
4576 }
4577 };
4578
4579 // alias
4580 ret.betweennessNormalised = ret.betweennessNormalized;
4581 return ret;
4582 } // betweennessCentrality
4583}; // elesfn
4584
4585// nice, short mathematical alias
4586elesfn$l.bc = elesfn$l.betweennessCentrality;
4587
4588// Implemented by Zoe Xi @zoexi for GSOC 2016
4589
4590/* eslint-disable no-unused-vars */
4591var defaults$c = defaults$g({
4592 expandFactor: 2,
4593 // affects time of computation and cluster granularity to some extent: M * M
4594 inflateFactor: 2,
4595 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4596 multFactor: 1,
4597 // optional self loops for each node. Use a neutral value to improve cluster computations.
4598 maxIterations: 20,
4599 // maximum number of iterations of the MCL algorithm in a single run
4600 attributes: [
4601 // attributes/features used to group nodes, ie. similarity values between nodes
4602 function (edge) {
4603 return 1;
4604 }]
4605});
4606/* eslint-enable */
4607
4608var setOptions$3 = function setOptions(options) {
4609 return defaults$c(options);
4610};
4611/* eslint-enable */
4612
4613var getSimilarity$1 = function getSimilarity(edge, attributes) {
4614 var total = 0;
4615 for (var i = 0; i < attributes.length; i++) {
4616 total += attributes[i](edge);
4617 }
4618 return total;
4619};
4620var addLoops = function addLoops(M, n, val) {
4621 for (var i = 0; i < n; i++) {
4622 M[i * n + i] = val;
4623 }
4624};
4625var normalize = function normalize(M, n) {
4626 var sum;
4627 for (var col = 0; col < n; col++) {
4628 sum = 0;
4629 for (var row = 0; row < n; row++) {
4630 sum += M[row * n + col];
4631 }
4632 for (var _row = 0; _row < n; _row++) {
4633 M[_row * n + col] = M[_row * n + col] / sum;
4634 }
4635 }
4636};
4637
4638// TODO: blocked matrix multiplication?
4639var mmult = function mmult(A, B, n) {
4640 var C = new Array(n * n);
4641 for (var i = 0; i < n; i++) {
4642 for (var j = 0; j < n; j++) {
4643 C[i * n + j] = 0;
4644 }
4645 for (var k = 0; k < n; k++) {
4646 for (var _j = 0; _j < n; _j++) {
4647 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4648 }
4649 }
4650 }
4651 return C;
4652};
4653var expand = function expand(M, n, expandFactor /** power **/) {
4654 var _M = M.slice(0);
4655 for (var p = 1; p < expandFactor; p++) {
4656 M = mmult(M, _M, n);
4657 }
4658 return M;
4659};
4660var inflate = function inflate(M, n, inflateFactor /** r **/) {
4661 var _M = new Array(n * n);
4662
4663 // M(i,j) ^ inflatePower
4664 for (var i = 0; i < n * n; i++) {
4665 _M[i] = Math.pow(M[i], inflateFactor);
4666 }
4667 normalize(_M, n);
4668 return _M;
4669};
4670var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4671 // Check that both matrices have the same elements (i,j)
4672 for (var i = 0; i < n2; i++) {
4673 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4674 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4675 if (v1 !== v2) {
4676 return false;
4677 }
4678 }
4679 return true;
4680};
4681var assign$2 = function assign(M, n, nodes, cy) {
4682 var clusters = [];
4683 for (var i = 0; i < n; i++) {
4684 var cluster = [];
4685 for (var j = 0; j < n; j++) {
4686 // Row-wise attractors and elements that they attract belong in same cluster
4687 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4688 cluster.push(nodes[j]);
4689 }
4690 }
4691 if (cluster.length !== 0) {
4692 clusters.push(cy.collection(cluster));
4693 }
4694 }
4695 return clusters;
4696};
4697var isDuplicate = function isDuplicate(c1, c2) {
4698 for (var i = 0; i < c1.length; i++) {
4699 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4700 return false;
4701 }
4702 }
4703 return true;
4704};
4705var removeDuplicates = function removeDuplicates(clusters) {
4706 for (var i = 0; i < clusters.length; i++) {
4707 for (var j = 0; j < clusters.length; j++) {
4708 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4709 clusters.splice(j, 1);
4710 }
4711 }
4712 }
4713 return clusters;
4714};
4715var markovClustering = function markovClustering(options) {
4716 var nodes = this.nodes();
4717 var edges = this.edges();
4718 var cy = this.cy();
4719
4720 // Set parameters of algorithm:
4721 var opts = setOptions$3(options);
4722
4723 // Map each node to its position in node array
4724 var id2position = {};
4725 for (var i = 0; i < nodes.length; i++) {
4726 id2position[nodes[i].id()] = i;
4727 }
4728
4729 // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4730 var n = nodes.length,
4731 n2 = n * n;
4732 var M = new Array(n2),
4733 _M;
4734 for (var _i = 0; _i < n2; _i++) {
4735 M[_i] = 0;
4736 }
4737 for (var e = 0; e < edges.length; e++) {
4738 var edge = edges[e];
4739 var _i2 = id2position[edge.source().id()];
4740 var j = id2position[edge.target().id()];
4741 var sim = getSimilarity$1(edge, opts.attributes);
4742 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4743 M[j * n + _i2] += sim;
4744 }
4745
4746 // Begin Markov cluster algorithm
4747
4748 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4749 addLoops(M, n, opts.multFactor);
4750
4751 // Step 2: M = normalize( M );
4752 normalize(M, n);
4753 var isStillMoving = true;
4754 var iterations = 0;
4755 while (isStillMoving && iterations < opts.maxIterations) {
4756 isStillMoving = false;
4757
4758 // Step 3:
4759 _M = expand(M, n, opts.expandFactor);
4760
4761 // Step 4:
4762 M = inflate(_M, n, opts.inflateFactor);
4763
4764 // Step 5: check to see if ~steady state has been reached
4765 if (!hasConverged(M, _M, n2, 4)) {
4766 isStillMoving = true;
4767 }
4768 iterations++;
4769 }
4770
4771 // Build clusters from matrix
4772 var clusters = assign$2(M, n, nodes, cy);
4773
4774 // Remove duplicate clusters due to symmetry of graph and M matrix
4775 clusters = removeDuplicates(clusters);
4776 return clusters;
4777};
4778var markovClustering$1 = {
4779 markovClustering: markovClustering,
4780 mcl: markovClustering
4781};
4782
4783// Common distance metrics for clustering algorithms
4784var identity = function identity(x) {
4785 return x;
4786};
4787var absDiff = function absDiff(p, q) {
4788 return Math.abs(q - p);
4789};
4790var addAbsDiff = function addAbsDiff(total, p, q) {
4791 return total + absDiff(p, q);
4792};
4793var addSquaredDiff = function addSquaredDiff(total, p, q) {
4794 return total + Math.pow(q - p, 2);
4795};
4796var sqrt = function sqrt(x) {
4797 return Math.sqrt(x);
4798};
4799var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4800 return Math.max(currentMax, absDiff(p, q));
4801};
4802var getDistance = function getDistance(length, getP, getQ, init, visit) {
4803 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4804 var ret = init;
4805 var p, q;
4806 for (var dim = 0; dim < length; dim++) {
4807 p = getP(dim);
4808 q = getQ(dim);
4809 ret = visit(ret, p, q);
4810 }
4811 return post(ret);
4812};
4813var distances = {
4814 euclidean: function euclidean(length, getP, getQ) {
4815 if (length >= 2) {
4816 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4817 } else {
4818 // for single attr case, more efficient to avoid sqrt
4819 return getDistance(length, getP, getQ, 0, addAbsDiff);
4820 }
4821 },
4822 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4823 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4824 },
4825 manhattan: function manhattan(length, getP, getQ) {
4826 return getDistance(length, getP, getQ, 0, addAbsDiff);
4827 },
4828 max: function max(length, getP, getQ) {
4829 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4830 }
4831};
4832
4833// in case the user accidentally doesn't use camel case
4834distances['squared-euclidean'] = distances['squaredEuclidean'];
4835distances['squaredeuclidean'] = distances['squaredEuclidean'];
4836function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4837 var impl;
4838 if (fn$6(method)) {
4839 impl = method;
4840 } else {
4841 impl = distances[method] || distances.euclidean;
4842 }
4843 if (length === 0 && fn$6(method)) {
4844 return impl(nodeP, nodeQ);
4845 } else {
4846 return impl(length, getP, getQ, nodeP, nodeQ);
4847 }
4848}
4849
4850var defaults$b = defaults$g({
4851 k: 2,
4852 m: 2,
4853 sensitivityThreshold: 0.0001,
4854 distance: 'euclidean',
4855 maxIterations: 10,
4856 attributes: [],
4857 testMode: false,
4858 testCentroids: null
4859});
4860var setOptions$2 = function setOptions(options) {
4861 return defaults$b(options);
4862};
4863
4864var getDist = function getDist(type, node, centroid, attributes, mode) {
4865 var noNodeP = mode !== 'kMedoids';
4866 var getP = noNodeP ? function (i) {
4867 return centroid[i];
4868 } : function (i) {
4869 return attributes[i](centroid);
4870 };
4871 var getQ = function getQ(i) {
4872 return attributes[i](node);
4873 };
4874 var nodeP = centroid;
4875 var nodeQ = node;
4876 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4877};
4878var randomCentroids = function randomCentroids(nodes, k, attributes) {
4879 var ndim = attributes.length;
4880 var min = new Array(ndim);
4881 var max = new Array(ndim);
4882 var centroids = new Array(k);
4883 var centroid = null;
4884
4885 // Find min, max values for each attribute dimension
4886 for (var i = 0; i < ndim; i++) {
4887 min[i] = nodes.min(attributes[i]).value;
4888 max[i] = nodes.max(attributes[i]).value;
4889 }
4890
4891 // Build k centroids, each represented as an n-dim feature vector
4892 for (var c = 0; c < k; c++) {
4893 centroid = [];
4894 for (var _i = 0; _i < ndim; _i++) {
4895 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4896 }
4897
4898 centroids[c] = centroid;
4899 }
4900 return centroids;
4901};
4902var classify = function classify(node, centroids, distance, attributes, type) {
4903 var min = Infinity;
4904 var index = 0;
4905 for (var i = 0; i < centroids.length; i++) {
4906 var dist = getDist(distance, node, centroids[i], attributes, type);
4907 if (dist < min) {
4908 min = dist;
4909 index = i;
4910 }
4911 }
4912 return index;
4913};
4914var buildCluster = function buildCluster(centroid, nodes, assignment) {
4915 var cluster = [];
4916 var node = null;
4917 for (var n = 0; n < nodes.length; n++) {
4918 node = nodes[n];
4919 if (assignment[node.id()] === centroid) {
4920 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4921 cluster.push(node);
4922 }
4923 }
4924 return cluster;
4925};
4926var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4927 return Math.abs(v2 - v1) <= sensitivityThreshold;
4928};
4929var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4930 for (var i = 0; i < v1.length; i++) {
4931 for (var j = 0; j < v1[i].length; j++) {
4932 var diff = Math.abs(v1[i][j] - v2[i][j]);
4933 if (diff > sensitivityThreshold) {
4934 return false;
4935 }
4936 }
4937 }
4938 return true;
4939};
4940var seenBefore = function seenBefore(node, medoids, n) {
4941 for (var i = 0; i < n; i++) {
4942 if (node === medoids[i]) return true;
4943 }
4944 return false;
4945};
4946var randomMedoids = function randomMedoids(nodes, k) {
4947 var medoids = new Array(k);
4948
4949 // For small data sets, the probability of medoid conflict is greater,
4950 // so we need to check to see if we've already seen or chose this node before.
4951 if (nodes.length < 50) {
4952 // Randomly select k medoids from the n nodes
4953 for (var i = 0; i < k; i++) {
4954 var node = nodes[Math.floor(Math.random() * nodes.length)];
4955
4956 // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
4957 // Instead choose a different random node.
4958 while (seenBefore(node, medoids, i)) {
4959 node = nodes[Math.floor(Math.random() * nodes.length)];
4960 }
4961 medoids[i] = node;
4962 }
4963 } else {
4964 // Relatively large data set, so pretty safe to not check and just select random nodes
4965 for (var _i2 = 0; _i2 < k; _i2++) {
4966 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4967 }
4968 }
4969 return medoids;
4970};
4971var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4972 var cost = 0;
4973 for (var n = 0; n < cluster.length; n++) {
4974 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4975 }
4976 return cost;
4977};
4978var kMeans = function kMeans(options) {
4979 var cy = this.cy();
4980 var nodes = this.nodes();
4981 var node = null;
4982
4983 // Set parameters of algorithm: # of clusters, distance metric, etc.
4984 var opts = setOptions$2(options);
4985
4986 // Begin k-means algorithm
4987 var clusters = new Array(opts.k);
4988 var assignment = {};
4989 var centroids;
4990
4991 // Step 1: Initialize centroid positions
4992 if (opts.testMode) {
4993 if (typeof opts.testCentroids === 'number') {
4994 // TODO: implement a seeded random number generator.
4995 opts.testCentroids;
4996 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4997 } else if (_typeof(opts.testCentroids) === 'object') {
4998 centroids = opts.testCentroids;
4999 } else {
5000 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5001 }
5002 } else {
5003 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5004 }
5005 var isStillMoving = true;
5006 var iterations = 0;
5007 while (isStillMoving && iterations < opts.maxIterations) {
5008 // Step 2: Assign nodes to the nearest centroid
5009 for (var n = 0; n < nodes.length; n++) {
5010 node = nodes[n];
5011 // Determine which cluster this node belongs to: node id => cluster #
5012 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5013 }
5014
5015 // Step 3: For each of the k clusters, update its centroid
5016 isStillMoving = false;
5017 for (var c = 0; c < opts.k; c++) {
5018 // Get all nodes that belong to this cluster
5019 var cluster = buildCluster(c, nodes, assignment);
5020 if (cluster.length === 0) {
5021 // If cluster is empty, break out early & move to next cluster
5022 continue;
5023 }
5024
5025 // Update centroids by calculating avg of all nodes within the cluster.
5026 var ndim = opts.attributes.length;
5027 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5028 var newCentroid = new Array(ndim);
5029 var sum = new Array(ndim);
5030 for (var d = 0; d < ndim; d++) {
5031 sum[d] = 0.0;
5032 for (var i = 0; i < cluster.length; i++) {
5033 node = cluster[i];
5034 sum[d] += opts.attributes[d](node);
5035 }
5036 newCentroid[d] = sum[d] / cluster.length;
5037
5038 // Check to see if algorithm has converged, i.e. when centroids no longer change
5039 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5040 isStillMoving = true;
5041 }
5042 }
5043 centroids[c] = newCentroid;
5044 clusters[c] = cy.collection(cluster);
5045 }
5046 iterations++;
5047 }
5048 return clusters;
5049};
5050var kMedoids = function kMedoids(options) {
5051 var cy = this.cy();
5052 var nodes = this.nodes();
5053 var node = null;
5054 var opts = setOptions$2(options);
5055
5056 // Begin k-medoids algorithm
5057 var clusters = new Array(opts.k);
5058 var medoids;
5059 var assignment = {};
5060 var curCost;
5061 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5062
5063 // Step 1: Initialize k medoids
5064 if (opts.testMode) {
5065 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5066 medoids = opts.testCentroids;
5067 } else {
5068 medoids = randomMedoids(nodes, opts.k);
5069 }
5070 } else {
5071 medoids = randomMedoids(nodes, opts.k);
5072 }
5073 var isStillMoving = true;
5074 var iterations = 0;
5075 while (isStillMoving && iterations < opts.maxIterations) {
5076 // Step 2: Assign nodes to the nearest medoid
5077 for (var n = 0; n < nodes.length; n++) {
5078 node = nodes[n];
5079 // Determine which cluster this node belongs to: node id => cluster #
5080 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5081 }
5082 isStillMoving = false;
5083 // Step 3: For each medoid m, and for each node associated with mediod m,
5084 // select the node with the lowest configuration cost as new medoid.
5085 for (var m = 0; m < medoids.length; m++) {
5086 // Get all nodes that belong to this medoid
5087 var cluster = buildCluster(m, nodes, assignment);
5088 if (cluster.length === 0) {
5089 // If cluster is empty, break out early & move to next cluster
5090 continue;
5091 }
5092 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5093
5094 // Select different medoid if its configuration has the lowest cost
5095 for (var _n = 0; _n < cluster.length; _n++) {
5096 curCost = findCost(cluster[_n], cluster, opts.attributes);
5097 if (curCost < minCosts[m]) {
5098 minCosts[m] = curCost;
5099 medoids[m] = cluster[_n];
5100 isStillMoving = true;
5101 }
5102 }
5103 clusters[m] = cy.collection(cluster);
5104 }
5105 iterations++;
5106 }
5107 return clusters;
5108};
5109var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5110 var numerator, denominator;
5111 for (var n = 0; n < nodes.length; n++) {
5112 for (var c = 0; c < centroids.length; c++) {
5113 weight[n][c] = Math.pow(U[n][c], opts.m);
5114 }
5115 }
5116 for (var _c = 0; _c < centroids.length; _c++) {
5117 for (var dim = 0; dim < opts.attributes.length; dim++) {
5118 numerator = 0;
5119 denominator = 0;
5120 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5121 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5122 denominator += weight[_n2][_c];
5123 }
5124 centroids[_c][dim] = numerator / denominator;
5125 }
5126 }
5127};
5128var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5129 // Save previous step
5130 for (var i = 0; i < U.length; i++) {
5131 _U[i] = U[i].slice();
5132 }
5133 var sum, numerator, denominator;
5134 var pow = 2 / (opts.m - 1);
5135 for (var c = 0; c < centroids.length; c++) {
5136 for (var n = 0; n < nodes.length; n++) {
5137 sum = 0;
5138 for (var k = 0; k < centroids.length; k++) {
5139 // against all other centroids
5140 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5141 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5142 sum += Math.pow(numerator / denominator, pow);
5143 }
5144 U[n][c] = 1 / sum;
5145 }
5146 }
5147};
5148var assign$1 = function assign(nodes, U, opts, cy) {
5149 var clusters = new Array(opts.k);
5150 for (var c = 0; c < clusters.length; c++) {
5151 clusters[c] = [];
5152 }
5153 var max;
5154 var index;
5155 for (var n = 0; n < U.length; n++) {
5156 // for each node (U is N x C matrix)
5157 max = -Infinity;
5158 index = -1;
5159 // Determine which cluster the node is most likely to belong in
5160 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5161 if (U[n][_c2] > max) {
5162 max = U[n][_c2];
5163 index = _c2;
5164 }
5165 }
5166 clusters[index].push(nodes[n]);
5167 }
5168
5169 // Turn every array into a collection of nodes
5170 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5171 clusters[_c3] = cy.collection(clusters[_c3]);
5172 }
5173 return clusters;
5174};
5175var fuzzyCMeans = function fuzzyCMeans(options) {
5176 var cy = this.cy();
5177 var nodes = this.nodes();
5178 var opts = setOptions$2(options);
5179
5180 // Begin fuzzy c-means algorithm
5181 var clusters;
5182 var centroids;
5183 var U;
5184 var _U;
5185 var weight;
5186
5187 // Step 1: Initialize letiables.
5188 _U = new Array(nodes.length);
5189 for (var i = 0; i < nodes.length; i++) {
5190 // N x C matrix
5191 _U[i] = new Array(opts.k);
5192 }
5193 U = new Array(nodes.length);
5194 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5195 // N x C matrix
5196 U[_i3] = new Array(opts.k);
5197 }
5198 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5199 var total = 0;
5200 for (var j = 0; j < opts.k; j++) {
5201 U[_i4][j] = Math.random();
5202 total += U[_i4][j];
5203 }
5204 for (var _j = 0; _j < opts.k; _j++) {
5205 U[_i4][_j] = U[_i4][_j] / total;
5206 }
5207 }
5208 centroids = new Array(opts.k);
5209 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5210 centroids[_i5] = new Array(opts.attributes.length);
5211 }
5212 weight = new Array(nodes.length);
5213 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5214 // N x C matrix
5215 weight[_i6] = new Array(opts.k);
5216 }
5217 // end init FCM
5218
5219 var isStillMoving = true;
5220 var iterations = 0;
5221 while (isStillMoving && iterations < opts.maxIterations) {
5222 isStillMoving = false;
5223
5224 // Step 2: Calculate the centroids for each step.
5225 updateCentroids(centroids, nodes, U, weight, opts);
5226
5227 // Step 3: Update the partition matrix U.
5228 updateMembership(U, _U, centroids, nodes, opts);
5229
5230 // Step 4: Check for convergence.
5231 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5232 isStillMoving = true;
5233 }
5234 iterations++;
5235 }
5236
5237 // Assign nodes to clusters with highest probability.
5238 clusters = assign$1(nodes, U, opts, cy);
5239 return {
5240 clusters: clusters,
5241 degreeOfMembership: U
5242 };
5243};
5244var kClustering = {
5245 kMeans: kMeans,
5246 kMedoids: kMedoids,
5247 fuzzyCMeans: fuzzyCMeans,
5248 fcm: fuzzyCMeans
5249};
5250
5251// Implemented by Zoe Xi @zoexi for GSOC 2016
5252var defaults$a = defaults$g({
5253 distance: 'euclidean',
5254 // distance metric to compare nodes
5255 linkage: 'min',
5256 // linkage criterion : how to determine the distance between clusters of nodes
5257 mode: 'threshold',
5258 // mode:'threshold' => clusters must be threshold distance apart
5259 threshold: Infinity,
5260 // the distance threshold
5261 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5262 addDendrogram: false,
5263 // whether to add the dendrogram to the graph for viz
5264 dendrogramDepth: 0,
5265 // depth at which dendrogram branches are merged into the returned clusters
5266 attributes: [] // array of attr functions
5267});
5268
5269var linkageAliases = {
5270 'single': 'min',
5271 'complete': 'max'
5272};
5273var setOptions$1 = function setOptions(options) {
5274 var opts = defaults$a(options);
5275 var preferredAlias = linkageAliases[opts.linkage];
5276 if (preferredAlias != null) {
5277 opts.linkage = preferredAlias;
5278 }
5279 return opts;
5280};
5281var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5282 // Find two closest clusters from cached mins
5283 var minKey = 0;
5284 var min = Infinity;
5285 var dist;
5286 var attrs = opts.attributes;
5287 var getDist = function getDist(n1, n2) {
5288 return clusteringDistance(opts.distance, attrs.length, function (i) {
5289 return attrs[i](n1);
5290 }, function (i) {
5291 return attrs[i](n2);
5292 }, n1, n2);
5293 };
5294 for (var i = 0; i < clusters.length; i++) {
5295 var key = clusters[i].key;
5296 var _dist = dists[key][mins[key]];
5297 if (_dist < min) {
5298 minKey = key;
5299 min = _dist;
5300 }
5301 }
5302 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5303 return false;
5304 }
5305 var c1 = index[minKey];
5306 var c2 = index[mins[minKey]];
5307 var merged;
5308
5309 // Merge two closest clusters
5310 if (opts.mode === 'dendrogram') {
5311 merged = {
5312 left: c1,
5313 right: c2,
5314 key: c1.key
5315 };
5316 } else {
5317 merged = {
5318 value: c1.value.concat(c2.value),
5319 key: c1.key
5320 };
5321 }
5322 clusters[c1.index] = merged;
5323 clusters.splice(c2.index, 1);
5324 index[c1.key] = merged;
5325
5326 // Update distances with new merged cluster
5327 for (var _i = 0; _i < clusters.length; _i++) {
5328 var cur = clusters[_i];
5329 if (c1.key === cur.key) {
5330 dist = Infinity;
5331 } else if (opts.linkage === 'min') {
5332 dist = dists[c1.key][cur.key];
5333 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5334 dist = dists[c2.key][cur.key];
5335 }
5336 } else if (opts.linkage === 'max') {
5337 dist = dists[c1.key][cur.key];
5338 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5339 dist = dists[c2.key][cur.key];
5340 }
5341 } else if (opts.linkage === 'mean') {
5342 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5343 } else {
5344 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5345 }
5346 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5347 }
5348
5349 // Update cached mins
5350 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5351 var key1 = clusters[_i2].key;
5352 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5353 var _min = key1;
5354 for (var j = 0; j < clusters.length; j++) {
5355 var key2 = clusters[j].key;
5356 if (dists[key1][key2] < dists[key1][_min]) {
5357 _min = key2;
5358 }
5359 }
5360 mins[key1] = _min;
5361 }
5362 clusters[_i2].index = _i2;
5363 }
5364
5365 // Clean up meta data used for clustering
5366 c1.key = c2.key = c1.index = c2.index = null;
5367 return true;
5368};
5369var getAllChildren = function getAllChildren(root, arr, cy) {
5370 if (!root) return;
5371 if (root.value) {
5372 arr.push(root.value);
5373 } else {
5374 if (root.left) getAllChildren(root.left, arr);
5375 if (root.right) getAllChildren(root.right, arr);
5376 }
5377};
5378var buildDendrogram = function buildDendrogram(root, cy) {
5379 if (!root) return '';
5380 if (root.left && root.right) {
5381 var leftStr = buildDendrogram(root.left, cy);
5382 var rightStr = buildDendrogram(root.right, cy);
5383 var node = cy.add({
5384 group: 'nodes',
5385 data: {
5386 id: leftStr + ',' + rightStr
5387 }
5388 });
5389 cy.add({
5390 group: 'edges',
5391 data: {
5392 source: leftStr,
5393 target: node.id()
5394 }
5395 });
5396 cy.add({
5397 group: 'edges',
5398 data: {
5399 source: rightStr,
5400 target: node.id()
5401 }
5402 });
5403 return node.id();
5404 } else if (root.value) {
5405 return root.value.id();
5406 }
5407};
5408var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5409 if (!root) return [];
5410 var left = [],
5411 right = [],
5412 leaves = [];
5413 if (k === 0) {
5414 // don't cut tree, simply return all nodes as 1 single cluster
5415 if (root.left) getAllChildren(root.left, left);
5416 if (root.right) getAllChildren(root.right, right);
5417 leaves = left.concat(right);
5418 return [cy.collection(leaves)];
5419 } else if (k === 1) {
5420 // cut at root
5421
5422 if (root.value) {
5423 // leaf node
5424 return [cy.collection(root.value)];
5425 } else {
5426 if (root.left) getAllChildren(root.left, left);
5427 if (root.right) getAllChildren(root.right, right);
5428 return [cy.collection(left), cy.collection(right)];
5429 }
5430 } else {
5431 if (root.value) {
5432 return [cy.collection(root.value)];
5433 } else {
5434 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5435 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5436 return left.concat(right);
5437 }
5438 }
5439};
5440
5441var hierarchicalClustering = function hierarchicalClustering(options) {
5442 var cy = this.cy();
5443 var nodes = this.nodes();
5444
5445 // Set parameters of algorithm: linkage type, distance metric, etc.
5446 var opts = setOptions$1(options);
5447 var attrs = opts.attributes;
5448 var getDist = function getDist(n1, n2) {
5449 return clusteringDistance(opts.distance, attrs.length, function (i) {
5450 return attrs[i](n1);
5451 }, function (i) {
5452 return attrs[i](n2);
5453 }, n1, n2);
5454 };
5455
5456 // Begin hierarchical algorithm
5457 var clusters = [];
5458 var dists = []; // distances between each pair of clusters
5459 var mins = []; // closest cluster for each cluster
5460 var index = []; // hash of all clusters by key
5461
5462 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5463 for (var n = 0; n < nodes.length; n++) {
5464 var cluster = {
5465 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5466 key: n,
5467 index: n
5468 };
5469 clusters[n] = cluster;
5470 index[n] = cluster;
5471 dists[n] = [];
5472 mins[n] = 0;
5473 }
5474
5475 // Calculate the distance between each pair of clusters
5476 for (var i = 0; i < clusters.length; i++) {
5477 for (var j = 0; j <= i; j++) {
5478 var dist = void 0;
5479 if (opts.mode === 'dendrogram') {
5480 // modes store cluster values differently
5481 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5482 } else {
5483 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5484 }
5485 dists[i][j] = dist;
5486 dists[j][i] = dist;
5487 if (dist < dists[i][mins[i]]) {
5488 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5489 }
5490 }
5491 }
5492
5493 // Find the closest pair of clusters and merge them into a single cluster.
5494 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5495 var merged = mergeClosest(clusters, index, dists, mins, opts);
5496 while (merged) {
5497 merged = mergeClosest(clusters, index, dists, mins, opts);
5498 }
5499 var retClusters;
5500
5501 // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5502 // in addition to returning the clusters.
5503 if (opts.mode === 'dendrogram') {
5504 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5505 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5506 } else {
5507 // Regular mode simply returns the clusters
5508
5509 retClusters = new Array(clusters.length);
5510 clusters.forEach(function (cluster, i) {
5511 // Clean up meta data used for clustering
5512 cluster.key = cluster.index = null;
5513 retClusters[i] = cy.collection(cluster.value);
5514 });
5515 }
5516 return retClusters;
5517};
5518var hierarchicalClustering$1 = {
5519 hierarchicalClustering: hierarchicalClustering,
5520 hca: hierarchicalClustering
5521};
5522
5523// Implemented by Zoe Xi @zoexi for GSOC 2016
5524var defaults$9 = defaults$g({
5525 distance: 'euclidean',
5526 // distance metric to compare attributes between two nodes
5527 preference: 'median',
5528 // suitability of a data point to serve as an exemplar
5529 damping: 0.8,
5530 // damping factor between [0.5, 1)
5531 maxIterations: 1000,
5532 // max number of iterations to run
5533 minIterations: 100,
5534 // min number of iterations to run in order for clustering to stop
5535 attributes: [// functions to quantify the similarity between any two points
5536 // e.g. node => node.data('weight')
5537 ]
5538});
5539var setOptions = function setOptions(options) {
5540 var dmp = options.damping;
5541 var pref = options.preference;
5542 if (!(0.5 <= dmp && dmp < 1)) {
5543 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5544 }
5545 var validPrefs = ['median', 'mean', 'min', 'max'];
5546 if (!(validPrefs.some(function (v) {
5547 return v === pref;
5548 }) || number$1(pref))) {
5549 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5550 return "'".concat(p, "'");
5551 }).join(', '), "] or a number. Got: ").concat(pref));
5552 }
5553 return defaults$9(options);
5554};
5555
5556var getSimilarity = function getSimilarity(type, n1, n2, attributes) {
5557 var attr = function attr(n, i) {
5558 return attributes[i](n);
5559 };
5560
5561 // nb negative because similarity should have an inverse relationship to distance
5562 return -clusteringDistance(type, attributes.length, function (i) {
5563 return attr(n1, i);
5564 }, function (i) {
5565 return attr(n2, i);
5566 }, n1, n2);
5567};
5568var getPreference = function getPreference(S, preference) {
5569 // larger preference = greater # of clusters
5570 var p = null;
5571 if (preference === 'median') {
5572 p = median(S);
5573 } else if (preference === 'mean') {
5574 p = mean(S);
5575 } else if (preference === 'min') {
5576 p = min(S);
5577 } else if (preference === 'max') {
5578 p = max(S);
5579 } else {
5580 // Custom preference number, as set by user
5581 p = preference;
5582 }
5583 return p;
5584};
5585var findExemplars = function findExemplars(n, R, A) {
5586 var indices = [];
5587 for (var i = 0; i < n; i++) {
5588 if (R[i * n + i] + A[i * n + i] > 0) {
5589 indices.push(i);
5590 }
5591 }
5592 return indices;
5593};
5594var assignClusters = function assignClusters(n, S, exemplars) {
5595 var clusters = [];
5596 for (var i = 0; i < n; i++) {
5597 var index = -1;
5598 var max = -Infinity;
5599 for (var ei = 0; ei < exemplars.length; ei++) {
5600 var e = exemplars[ei];
5601 if (S[i * n + e] > max) {
5602 index = e;
5603 max = S[i * n + e];
5604 }
5605 }
5606 if (index > 0) {
5607 clusters.push(index);
5608 }
5609 }
5610 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5611 clusters[exemplars[_ei]] = exemplars[_ei];
5612 }
5613 return clusters;
5614};
5615var assign = function assign(n, S, exemplars) {
5616 var clusters = assignClusters(n, S, exemplars);
5617 for (var ei = 0; ei < exemplars.length; ei++) {
5618 var ii = [];
5619 for (var c = 0; c < clusters.length; c++) {
5620 if (clusters[c] === exemplars[ei]) {
5621 ii.push(c);
5622 }
5623 }
5624 var maxI = -1;
5625 var maxSum = -Infinity;
5626 for (var i = 0; i < ii.length; i++) {
5627 var sum = 0;
5628 for (var j = 0; j < ii.length; j++) {
5629 sum += S[ii[j] * n + ii[i]];
5630 }
5631 if (sum > maxSum) {
5632 maxI = i;
5633 maxSum = sum;
5634 }
5635 }
5636 exemplars[ei] = ii[maxI];
5637 }
5638 clusters = assignClusters(n, S, exemplars);
5639 return clusters;
5640};
5641var affinityPropagation = function affinityPropagation(options) {
5642 var cy = this.cy();
5643 var nodes = this.nodes();
5644 var opts = setOptions(options);
5645
5646 // Map each node to its position in node array
5647 var id2position = {};
5648 for (var i = 0; i < nodes.length; i++) {
5649 id2position[nodes[i].id()] = i;
5650 }
5651
5652 // Begin affinity propagation algorithm
5653
5654 var n; // number of data points
5655 var n2; // size of matrices
5656 var S; // similarity matrix (1D array)
5657 var p; // preference/suitability of a data point to serve as an exemplar
5658 var R; // responsibility matrix (1D array)
5659 var A; // availability matrix (1D array)
5660
5661 n = nodes.length;
5662 n2 = n * n;
5663
5664 // Initialize and build S similarity matrix
5665 S = new Array(n2);
5666 for (var _i = 0; _i < n2; _i++) {
5667 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5668 }
5669
5670 for (var _i2 = 0; _i2 < n; _i2++) {
5671 for (var j = 0; j < n; j++) {
5672 if (_i2 !== j) {
5673 S[_i2 * n + j] = getSimilarity(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5674 }
5675 }
5676 }
5677
5678 // Place preferences on the diagonal of S
5679 p = getPreference(S, opts.preference);
5680 for (var _i3 = 0; _i3 < n; _i3++) {
5681 S[_i3 * n + _i3] = p;
5682 }
5683
5684 // Initialize R responsibility matrix
5685 R = new Array(n2);
5686 for (var _i4 = 0; _i4 < n2; _i4++) {
5687 R[_i4] = 0.0;
5688 }
5689
5690 // Initialize A availability matrix
5691 A = new Array(n2);
5692 for (var _i5 = 0; _i5 < n2; _i5++) {
5693 A[_i5] = 0.0;
5694 }
5695 var old = new Array(n);
5696 var Rp = new Array(n);
5697 var se = new Array(n);
5698 for (var _i6 = 0; _i6 < n; _i6++) {
5699 old[_i6] = 0.0;
5700 Rp[_i6] = 0.0;
5701 se[_i6] = 0;
5702 }
5703 var e = new Array(n * opts.minIterations);
5704 for (var _i7 = 0; _i7 < e.length; _i7++) {
5705 e[_i7] = 0;
5706 }
5707 var iter;
5708 for (iter = 0; iter < opts.maxIterations; iter++) {
5709 // main algorithmic loop
5710
5711 // Update R responsibility matrix
5712 for (var _i8 = 0; _i8 < n; _i8++) {
5713 var max = -Infinity,
5714 max2 = -Infinity,
5715 maxI = -1,
5716 AS = 0.0;
5717 for (var _j = 0; _j < n; _j++) {
5718 old[_j] = R[_i8 * n + _j];
5719 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5720 if (AS >= max) {
5721 max2 = max;
5722 max = AS;
5723 maxI = _j;
5724 } else if (AS > max2) {
5725 max2 = AS;
5726 }
5727 }
5728 for (var _j2 = 0; _j2 < n; _j2++) {
5729 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5730 }
5731 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5732 }
5733
5734 // Update A availability matrix
5735 for (var _i9 = 0; _i9 < n; _i9++) {
5736 var sum = 0;
5737 for (var _j3 = 0; _j3 < n; _j3++) {
5738 old[_j3] = A[_j3 * n + _i9];
5739 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5740 sum += Rp[_j3];
5741 }
5742 sum -= Rp[_i9];
5743 Rp[_i9] = R[_i9 * n + _i9];
5744 sum += Rp[_i9];
5745 for (var _j4 = 0; _j4 < n; _j4++) {
5746 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5747 }
5748 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5749 }
5750
5751 // Check for convergence
5752 var K = 0;
5753 for (var _i10 = 0; _i10 < n; _i10++) {
5754 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5755 e[iter % opts.minIterations * n + _i10] = E;
5756 K += E;
5757 }
5758 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5759 var _sum = 0;
5760 for (var _i11 = 0; _i11 < n; _i11++) {
5761 se[_i11] = 0;
5762 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5763 se[_i11] += e[_j5 * n + _i11];
5764 }
5765 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5766 _sum++;
5767 }
5768 }
5769 if (_sum === n) {
5770 // then we have convergence
5771 break;
5772 }
5773 }
5774 }
5775
5776 // Identify exemplars (cluster centers)
5777 var exemplarsIndices = findExemplars(n, R, A);
5778
5779 // Assign nodes to clusters
5780 var clusterIndices = assign(n, S, exemplarsIndices);
5781 var clusters = {};
5782 for (var c = 0; c < exemplarsIndices.length; c++) {
5783 clusters[exemplarsIndices[c]] = [];
5784 }
5785 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5786 var pos = id2position[nodes[_i12].id()];
5787 var clusterIndex = clusterIndices[pos];
5788 if (clusterIndex != null) {
5789 // the node may have not been assigned a cluster if no valid attributes were specified
5790 clusters[clusterIndex].push(nodes[_i12]);
5791 }
5792 }
5793 var retClusters = new Array(exemplarsIndices.length);
5794 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5795 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5796 }
5797 return retClusters;
5798};
5799var affinityPropagation$1 = {
5800 affinityPropagation: affinityPropagation,
5801 ap: affinityPropagation
5802};
5803
5804var hierholzerDefaults = defaults$g({
5805 root: undefined,
5806 directed: false
5807});
5808var elesfn$k = {
5809 hierholzer: function hierholzer(options) {
5810 if (!plainObject(options)) {
5811 var args = arguments;
5812 options = {
5813 root: args[0],
5814 directed: args[1]
5815 };
5816 }
5817 var _hierholzerDefaults = hierholzerDefaults(options),
5818 root = _hierholzerDefaults.root,
5819 directed = _hierholzerDefaults.directed;
5820 var eles = this;
5821 var dflag = false;
5822 var oddIn;
5823 var oddOut;
5824 var startVertex;
5825 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5826 var nodes = {};
5827 var edges = {};
5828 if (directed) {
5829 eles.forEach(function (ele) {
5830 var id = ele.id();
5831 if (ele.isNode()) {
5832 var ind = ele.indegree(true);
5833 var outd = ele.outdegree(true);
5834 var d1 = ind - outd;
5835 var d2 = outd - ind;
5836 if (d1 == 1) {
5837 if (oddIn) dflag = true;else oddIn = id;
5838 } else if (d2 == 1) {
5839 if (oddOut) dflag = true;else oddOut = id;
5840 } else if (d2 > 1 || d1 > 1) {
5841 dflag = true;
5842 }
5843 nodes[id] = [];
5844 ele.outgoers().forEach(function (e) {
5845 if (e.isEdge()) nodes[id].push(e.id());
5846 });
5847 } else {
5848 edges[id] = [undefined, ele.target().id()];
5849 }
5850 });
5851 } else {
5852 eles.forEach(function (ele) {
5853 var id = ele.id();
5854 if (ele.isNode()) {
5855 var d = ele.degree(true);
5856 if (d % 2) {
5857 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5858 }
5859 nodes[id] = [];
5860 ele.connectedEdges().forEach(function (e) {
5861 return nodes[id].push(e.id());
5862 });
5863 } else {
5864 edges[id] = [ele.source().id(), ele.target().id()];
5865 }
5866 });
5867 }
5868 var result = {
5869 found: false,
5870 trail: undefined
5871 };
5872 if (dflag) return result;else if (oddOut && oddIn) {
5873 if (directed) {
5874 if (startVertex && oddOut != startVertex) {
5875 return result;
5876 }
5877 startVertex = oddOut;
5878 } else {
5879 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5880 return result;
5881 } else if (!startVertex) {
5882 startVertex = oddOut;
5883 }
5884 }
5885 } else {
5886 if (!startVertex) startVertex = eles[0].id();
5887 }
5888 var walk = function walk(v) {
5889 var currentNode = v;
5890 var subtour = [v];
5891 var adj, adjTail, adjHead;
5892 while (nodes[currentNode].length) {
5893 adj = nodes[currentNode].shift();
5894 adjTail = edges[adj][0];
5895 adjHead = edges[adj][1];
5896 if (currentNode != adjHead) {
5897 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5898 return e != adj;
5899 });
5900 currentNode = adjHead;
5901 } else if (!directed && currentNode != adjTail) {
5902 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5903 return e != adj;
5904 });
5905 currentNode = adjTail;
5906 }
5907 subtour.unshift(adj);
5908 subtour.unshift(currentNode);
5909 }
5910 return subtour;
5911 };
5912 var trail = [];
5913 var subtour = [];
5914 subtour = walk(startVertex);
5915 while (subtour.length != 1) {
5916 if (nodes[subtour[0]].length == 0) {
5917 trail.unshift(eles.getElementById(subtour.shift()));
5918 trail.unshift(eles.getElementById(subtour.shift()));
5919 } else {
5920 subtour = walk(subtour.shift()).concat(subtour);
5921 }
5922 }
5923 trail.unshift(eles.getElementById(subtour.shift())); // final node
5924
5925 for (var d in nodes) {
5926 if (nodes[d].length) {
5927 return result;
5928 }
5929 }
5930 result.found = true;
5931 result.trail = this.spawn(trail, true);
5932 return result;
5933 }
5934};
5935
5936var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5937 var eles = this;
5938 var nodes = {};
5939 var id = 0;
5940 var edgeCount = 0;
5941 var components = [];
5942 var stack = [];
5943 var visitedEdges = {};
5944 var buildComponent = function buildComponent(x, y) {
5945 var i = stack.length - 1;
5946 var cutset = [];
5947 var component = eles.spawn();
5948 while (stack[i].x != x || stack[i].y != y) {
5949 cutset.push(stack.pop().edge);
5950 i--;
5951 }
5952 cutset.push(stack.pop().edge);
5953 cutset.forEach(function (edge) {
5954 var connectedNodes = edge.connectedNodes().intersection(eles);
5955 component.merge(edge);
5956 connectedNodes.forEach(function (node) {
5957 var nodeId = node.id();
5958 var connectedEdges = node.connectedEdges().intersection(eles);
5959 component.merge(node);
5960 if (!nodes[nodeId].cutVertex) {
5961 component.merge(connectedEdges);
5962 } else {
5963 component.merge(connectedEdges.filter(function (edge) {
5964 return edge.isLoop();
5965 }));
5966 }
5967 });
5968 });
5969 components.push(component);
5970 };
5971 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5972 if (root === parent) edgeCount += 1;
5973 nodes[currentNode] = {
5974 id: id,
5975 low: id++,
5976 cutVertex: false
5977 };
5978 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5979 if (edges.size() === 0) {
5980 components.push(eles.spawn(eles.getElementById(currentNode)));
5981 } else {
5982 var sourceId, targetId, otherNodeId, edgeId;
5983 edges.forEach(function (edge) {
5984 sourceId = edge.source().id();
5985 targetId = edge.target().id();
5986 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5987 if (otherNodeId !== parent) {
5988 edgeId = edge.id();
5989 if (!visitedEdges[edgeId]) {
5990 visitedEdges[edgeId] = true;
5991 stack.push({
5992 x: currentNode,
5993 y: otherNodeId,
5994 edge: edge
5995 });
5996 }
5997 if (!(otherNodeId in nodes)) {
5998 biconnectedSearch(root, otherNodeId, currentNode);
5999 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6000 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6001 nodes[currentNode].cutVertex = true;
6002 buildComponent(currentNode, otherNodeId);
6003 }
6004 } else {
6005 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6006 }
6007 }
6008 });
6009 }
6010 };
6011 eles.forEach(function (ele) {
6012 if (ele.isNode()) {
6013 var nodeId = ele.id();
6014 if (!(nodeId in nodes)) {
6015 edgeCount = 0;
6016 biconnectedSearch(nodeId, nodeId);
6017 nodes[nodeId].cutVertex = edgeCount > 1;
6018 }
6019 }
6020 });
6021 var cutVertices = Object.keys(nodes).filter(function (id) {
6022 return nodes[id].cutVertex;
6023 }).map(function (id) {
6024 return eles.getElementById(id);
6025 });
6026 return {
6027 cut: eles.spawn(cutVertices),
6028 components: components
6029 };
6030};
6031var hopcroftTarjanBiconnected$1 = {
6032 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6033 htbc: hopcroftTarjanBiconnected,
6034 htb: hopcroftTarjanBiconnected,
6035 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6036};
6037
6038var tarjanStronglyConnected = function tarjanStronglyConnected() {
6039 var eles = this;
6040 var nodes = {};
6041 var index = 0;
6042 var components = [];
6043 var stack = [];
6044 var cut = eles.spawn(eles);
6045 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6046 stack.push(sourceNodeId);
6047 nodes[sourceNodeId] = {
6048 index: index,
6049 low: index++,
6050 explored: false
6051 };
6052 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6053 connectedEdges.forEach(function (edge) {
6054 var targetNodeId = edge.target().id();
6055 if (targetNodeId !== sourceNodeId) {
6056 if (!(targetNodeId in nodes)) {
6057 stronglyConnectedSearch(targetNodeId);
6058 }
6059 if (!nodes[targetNodeId].explored) {
6060 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6061 }
6062 }
6063 });
6064 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6065 var componentNodes = eles.spawn();
6066 for (;;) {
6067 var nodeId = stack.pop();
6068 componentNodes.merge(eles.getElementById(nodeId));
6069 nodes[nodeId].low = nodes[sourceNodeId].index;
6070 nodes[nodeId].explored = true;
6071 if (nodeId === sourceNodeId) {
6072 break;
6073 }
6074 }
6075 var componentEdges = componentNodes.edgesWith(componentNodes);
6076 var component = componentNodes.merge(componentEdges);
6077 components.push(component);
6078 cut = cut.difference(component);
6079 }
6080 };
6081 eles.forEach(function (ele) {
6082 if (ele.isNode()) {
6083 var nodeId = ele.id();
6084 if (!(nodeId in nodes)) {
6085 stronglyConnectedSearch(nodeId);
6086 }
6087 }
6088 });
6089 return {
6090 cut: cut,
6091 components: components
6092 };
6093};
6094var tarjanStronglyConnected$1 = {
6095 tarjanStronglyConnected: tarjanStronglyConnected,
6096 tsc: tarjanStronglyConnected,
6097 tscc: tarjanStronglyConnected,
6098 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6099};
6100
6101var elesfn$j = {};
6102[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) {
6103 extend(elesfn$j, props);
6104});
6105
6106/*!
6107Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6108Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6109Licensed under The MIT License (http://opensource.org/licenses/MIT)
6110*/
6111
6112/* promise states [Promises/A+ 2.1] */
6113var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
6114var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
6115var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
6116
6117/* promise object constructor */
6118var api = function api(executor) {
6119 /* optionally support non-constructor/plain-function call */
6120 if (!(this instanceof api)) return new api(executor);
6121
6122 /* initialize object */
6123 this.id = 'Thenable/1.0.7';
6124 this.state = STATE_PENDING; /* initial state */
6125 this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
6126 this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
6127 this.onFulfilled = []; /* initial handlers */
6128 this.onRejected = []; /* initial handlers */
6129
6130 /* provide optional information-hiding proxy */
6131 this.proxy = {
6132 then: this.then.bind(this)
6133 };
6134
6135 /* support optional executor function */
6136 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6137};
6138
6139/* promise API methods */
6140api.prototype = {
6141 /* promise resolving methods */
6142 fulfill: function fulfill(value) {
6143 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6144 },
6145 reject: function reject(value) {
6146 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6147 },
6148 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6149 then: function then(onFulfilled, onRejected) {
6150 var curr = this;
6151 var next = new api(); /* [Promises/A+ 2.2.7] */
6152 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
6153 curr.onRejected.push(resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
6154 execute(curr);
6155 return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */
6156 }
6157};
6158
6159/* deliver an action */
6160var deliver = function deliver(curr, state, name, value) {
6161 if (curr.state === STATE_PENDING) {
6162 curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6163 curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6164 execute(curr);
6165 }
6166 return curr;
6167};
6168
6169/* execute all handlers */
6170var execute = function execute(curr) {
6171 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6172};
6173
6174/* execute particular set of handlers */
6175var execute_handlers = function execute_handlers(curr, name, value) {
6176 /* global setImmediate: true */
6177 /* global setTimeout: true */
6178
6179 /* short-circuit processing */
6180 if (curr[name].length === 0) return;
6181
6182 /* iterate over all handlers, exactly once */
6183 var handlers = curr[name];
6184 curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6185 var func = function func() {
6186 for (var i = 0; i < handlers.length; i++) {
6187 handlers[i](value);
6188 } /* [Promises/A+ 2.2.5] */
6189 };
6190
6191 /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
6192 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6193};
6194
6195/* generate a resolver function */
6196var resolver = function resolver(cb, next, method) {
6197 return function (value) {
6198 if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6199 next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */else {
6200 var result;
6201 try {
6202 result = cb(value);
6203 } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */ catch (e) {
6204 next.reject(e); /* [Promises/A+ 2.2.7.2] */
6205 return;
6206 }
6207 resolve(next, result); /* [Promises/A+ 2.2.7.1] */
6208 }
6209 };
6210};
6211
6212/* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */
6213var resolve = function resolve(promise, x) {
6214 /* sanity check arguments */ /* [Promises/A+ 2.3.1] */
6215 if (promise === x || promise.proxy === x) {
6216 promise.reject(new TypeError('cannot resolve promise with itself'));
6217 return;
6218 }
6219
6220 /* surgically check for a "then" method
6221 (mainly to just call the "getter" of "then" only once) */
6222 var then;
6223 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6224 try {
6225 then = x.then;
6226 } /* [Promises/A+ 2.3.3.1, 3.5] */ catch (e) {
6227 promise.reject(e); /* [Promises/A+ 2.3.3.2] */
6228 return;
6229 }
6230 }
6231
6232 /* handle own Thenables [Promises/A+ 2.3.2]
6233 and similar "thenables" [Promises/A+ 2.3.3] */
6234 if (typeof then === 'function') {
6235 var resolved = false;
6236 try {
6237 /* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */
6238 then.call(x, /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
6239 function (y) {
6240 if (resolved) return;
6241 resolved = true; /* [Promises/A+ 2.3.3.3.3] */
6242 if (y === x) /* [Promises/A+ 3.6] */
6243 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6244 }, /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
6245 function (r) {
6246 if (resolved) return;
6247 resolved = true; /* [Promises/A+ 2.3.3.3.3] */
6248 promise.reject(r);
6249 });
6250 } catch (e) {
6251 if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
6252 promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
6253 }
6254
6255 return;
6256 }
6257
6258 /* handle other values */
6259 promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
6260};
6261
6262// so we always have Promise.all()
6263api.all = function (ps) {
6264 return new api(function (resolveAll, rejectAll) {
6265 var vals = new Array(ps.length);
6266 var doneCount = 0;
6267 var fulfill = function fulfill(i, val) {
6268 vals[i] = val;
6269 doneCount++;
6270 if (doneCount === ps.length) {
6271 resolveAll(vals);
6272 }
6273 };
6274 for (var i = 0; i < ps.length; i++) {
6275 (function (i) {
6276 var p = ps[i];
6277 var isPromise = p != null && p.then != null;
6278 if (isPromise) {
6279 p.then(function (val) {
6280 fulfill(i, val);
6281 }, function (err) {
6282 rejectAll(err);
6283 });
6284 } else {
6285 var val = p;
6286 fulfill(i, val);
6287 }
6288 })(i);
6289 }
6290 });
6291};
6292api.resolve = function (val) {
6293 return new api(function (resolve, reject) {
6294 resolve(val);
6295 });
6296};
6297api.reject = function (val) {
6298 return new api(function (resolve, reject) {
6299 reject(val);
6300 });
6301};
6302var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6303
6304var Animation = function Animation(target, opts, opts2) {
6305 var isCore = core(target);
6306 var isEle = !isCore;
6307 var _p = this._private = extend({
6308 duration: 1000
6309 }, opts, opts2);
6310 _p.target = target;
6311 _p.style = _p.style || _p.css;
6312 _p.started = false;
6313 _p.playing = false;
6314 _p.hooked = false;
6315 _p.applying = false;
6316 _p.progress = 0;
6317 _p.completes = [];
6318 _p.frames = [];
6319 if (_p.complete && fn$6(_p.complete)) {
6320 _p.completes.push(_p.complete);
6321 }
6322 if (isEle) {
6323 var pos = target.position();
6324 _p.startPosition = _p.startPosition || {
6325 x: pos.x,
6326 y: pos.y
6327 };
6328 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6329 }
6330 if (isCore) {
6331 var pan = target.pan();
6332 _p.startPan = {
6333 x: pan.x,
6334 y: pan.y
6335 };
6336 _p.startZoom = target.zoom();
6337 }
6338
6339 // for future timeline/animations impl
6340 this.length = 1;
6341 this[0] = this;
6342};
6343var anifn = Animation.prototype;
6344extend(anifn, {
6345 instanceString: function instanceString() {
6346 return 'animation';
6347 },
6348 hook: function hook() {
6349 var _p = this._private;
6350 if (!_p.hooked) {
6351 // add to target's animation queue
6352 var q;
6353 var tAni = _p.target._private.animation;
6354 if (_p.queue) {
6355 q = tAni.queue;
6356 } else {
6357 q = tAni.current;
6358 }
6359 q.push(this);
6360
6361 // add to the animation loop pool
6362 if (elementOrCollection(_p.target)) {
6363 _p.target.cy().addToAnimationPool(_p.target);
6364 }
6365 _p.hooked = true;
6366 }
6367 return this;
6368 },
6369 play: function play() {
6370 var _p = this._private;
6371
6372 // autorewind
6373 if (_p.progress === 1) {
6374 _p.progress = 0;
6375 }
6376 _p.playing = true;
6377 _p.started = false; // needs to be started by animation loop
6378 _p.stopped = false;
6379 this.hook();
6380
6381 // the animation loop will start the animation...
6382
6383 return this;
6384 },
6385 playing: function playing() {
6386 return this._private.playing;
6387 },
6388 apply: function apply() {
6389 var _p = this._private;
6390 _p.applying = true;
6391 _p.started = false; // needs to be started by animation loop
6392 _p.stopped = false;
6393 this.hook();
6394
6395 // the animation loop will apply the animation at this progress
6396
6397 return this;
6398 },
6399 applying: function applying() {
6400 return this._private.applying;
6401 },
6402 pause: function pause() {
6403 var _p = this._private;
6404 _p.playing = false;
6405 _p.started = false;
6406 return this;
6407 },
6408 stop: function stop() {
6409 var _p = this._private;
6410 _p.playing = false;
6411 _p.started = false;
6412 _p.stopped = true; // to be removed from animation queues
6413
6414 return this;
6415 },
6416 rewind: function rewind() {
6417 return this.progress(0);
6418 },
6419 fastforward: function fastforward() {
6420 return this.progress(1);
6421 },
6422 time: function time(t) {
6423 var _p = this._private;
6424 if (t === undefined) {
6425 return _p.progress * _p.duration;
6426 } else {
6427 return this.progress(t / _p.duration);
6428 }
6429 },
6430 progress: function progress(p) {
6431 var _p = this._private;
6432 var wasPlaying = _p.playing;
6433 if (p === undefined) {
6434 return _p.progress;
6435 } else {
6436 if (wasPlaying) {
6437 this.pause();
6438 }
6439 _p.progress = p;
6440 _p.started = false;
6441 if (wasPlaying) {
6442 this.play();
6443 }
6444 }
6445 return this;
6446 },
6447 completed: function completed() {
6448 return this._private.progress === 1;
6449 },
6450 reverse: function reverse() {
6451 var _p = this._private;
6452 var wasPlaying = _p.playing;
6453 if (wasPlaying) {
6454 this.pause();
6455 }
6456 _p.progress = 1 - _p.progress;
6457 _p.started = false;
6458 var swap = function swap(a, b) {
6459 var _pa = _p[a];
6460 if (_pa == null) {
6461 return;
6462 }
6463 _p[a] = _p[b];
6464 _p[b] = _pa;
6465 };
6466 swap('zoom', 'startZoom');
6467 swap('pan', 'startPan');
6468 swap('position', 'startPosition');
6469
6470 // swap styles
6471 if (_p.style) {
6472 for (var i = 0; i < _p.style.length; i++) {
6473 var prop = _p.style[i];
6474 var name = prop.name;
6475 var startStyleProp = _p.startStyle[name];
6476 _p.startStyle[name] = prop;
6477 _p.style[i] = startStyleProp;
6478 }
6479 }
6480 if (wasPlaying) {
6481 this.play();
6482 }
6483 return this;
6484 },
6485 promise: function promise(type) {
6486 var _p = this._private;
6487 var arr;
6488 switch (type) {
6489 case 'frame':
6490 arr = _p.frames;
6491 break;
6492 default:
6493 case 'complete':
6494 case 'completed':
6495 arr = _p.completes;
6496 }
6497 return new Promise$1(function (resolve, reject) {
6498 arr.push(function () {
6499 resolve();
6500 });
6501 });
6502 }
6503});
6504anifn.complete = anifn.completed;
6505anifn.run = anifn.play;
6506anifn.running = anifn.playing;
6507
6508var define$3 = {
6509 animated: function animated() {
6510 return function animatedImpl() {
6511 var self = this;
6512 var selfIsArrayLike = self.length !== undefined;
6513 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6514 var cy = this._private.cy || this;
6515 if (!cy.styleEnabled()) {
6516 return false;
6517 }
6518 var ele = all[0];
6519 if (ele) {
6520 return ele._private.animation.current.length > 0;
6521 }
6522 };
6523 },
6524 // animated
6525
6526 clearQueue: function clearQueue() {
6527 return function clearQueueImpl() {
6528 var self = this;
6529 var selfIsArrayLike = self.length !== undefined;
6530 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6531 var cy = this._private.cy || this;
6532 if (!cy.styleEnabled()) {
6533 return this;
6534 }
6535 for (var i = 0; i < all.length; i++) {
6536 var ele = all[i];
6537 ele._private.animation.queue = [];
6538 }
6539 return this;
6540 };
6541 },
6542 // clearQueue
6543
6544 delay: function delay() {
6545 return function delayImpl(time, complete) {
6546 var cy = this._private.cy || this;
6547 if (!cy.styleEnabled()) {
6548 return this;
6549 }
6550 return this.animate({
6551 delay: time,
6552 duration: time,
6553 complete: complete
6554 });
6555 };
6556 },
6557 // delay
6558
6559 delayAnimation: function delayAnimation() {
6560 return function delayAnimationImpl(time, complete) {
6561 var cy = this._private.cy || this;
6562 if (!cy.styleEnabled()) {
6563 return this;
6564 }
6565 return this.animation({
6566 delay: time,
6567 duration: time,
6568 complete: complete
6569 });
6570 };
6571 },
6572 // delay
6573
6574 animation: function animation() {
6575 return function animationImpl(properties, params) {
6576 var self = this;
6577 var selfIsArrayLike = self.length !== undefined;
6578 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6579 var cy = this._private.cy || this;
6580 var isCore = !selfIsArrayLike;
6581 var isEles = !isCore;
6582 if (!cy.styleEnabled()) {
6583 return this;
6584 }
6585 var style = cy.style();
6586 properties = extend({}, properties, params);
6587 var propertiesEmpty = Object.keys(properties).length === 0;
6588 if (propertiesEmpty) {
6589 return new Animation(all[0], properties); // nothing to animate
6590 }
6591
6592 if (properties.duration === undefined) {
6593 properties.duration = 400;
6594 }
6595 switch (properties.duration) {
6596 case 'slow':
6597 properties.duration = 600;
6598 break;
6599 case 'fast':
6600 properties.duration = 200;
6601 break;
6602 }
6603 if (isEles) {
6604 properties.style = style.getPropsList(properties.style || properties.css);
6605 properties.css = undefined;
6606 }
6607 if (isEles && properties.renderedPosition != null) {
6608 var rpos = properties.renderedPosition;
6609 var pan = cy.pan();
6610 var zoom = cy.zoom();
6611 properties.position = renderedToModelPosition(rpos, zoom, pan);
6612 }
6613
6614 // override pan w/ panBy if set
6615 if (isCore && properties.panBy != null) {
6616 var panBy = properties.panBy;
6617 var cyPan = cy.pan();
6618 properties.pan = {
6619 x: cyPan.x + panBy.x,
6620 y: cyPan.y + panBy.y
6621 };
6622 }
6623
6624 // override pan w/ center if set
6625 var center = properties.center || properties.centre;
6626 if (isCore && center != null) {
6627 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6628 if (centerPan != null) {
6629 properties.pan = centerPan;
6630 }
6631 }
6632
6633 // override pan & zoom w/ fit if set
6634 if (isCore && properties.fit != null) {
6635 var fit = properties.fit;
6636 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6637 if (fitVp != null) {
6638 properties.pan = fitVp.pan;
6639 properties.zoom = fitVp.zoom;
6640 }
6641 }
6642
6643 // override zoom (& potentially pan) w/ zoom obj if set
6644 if (isCore && plainObject(properties.zoom)) {
6645 var vp = cy.getZoomedViewport(properties.zoom);
6646 if (vp != null) {
6647 if (vp.zoomed) {
6648 properties.zoom = vp.zoom;
6649 }
6650 if (vp.panned) {
6651 properties.pan = vp.pan;
6652 }
6653 } else {
6654 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6655 }
6656 }
6657
6658 return new Animation(all[0], properties);
6659 };
6660 },
6661 // animate
6662
6663 animate: function animate() {
6664 return function animateImpl(properties, params) {
6665 var self = this;
6666 var selfIsArrayLike = self.length !== undefined;
6667 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6668 var cy = this._private.cy || this;
6669 if (!cy.styleEnabled()) {
6670 return this;
6671 }
6672 if (params) {
6673 properties = extend({}, properties, params);
6674 }
6675
6676 // manually hook and run the animation
6677 for (var i = 0; i < all.length; i++) {
6678 var ele = all[i];
6679 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6680 var ani = ele.animation(properties, queue ? {
6681 queue: true
6682 } : undefined);
6683 ani.play();
6684 }
6685 return this; // chaining
6686 };
6687 },
6688
6689 // animate
6690
6691 stop: function stop() {
6692 return function stopImpl(clearQueue, jumpToEnd) {
6693 var self = this;
6694 var selfIsArrayLike = self.length !== undefined;
6695 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6696 var cy = this._private.cy || this;
6697 if (!cy.styleEnabled()) {
6698 return this;
6699 }
6700 for (var i = 0; i < all.length; i++) {
6701 var ele = all[i];
6702 var _p = ele._private;
6703 var anis = _p.animation.current;
6704 for (var j = 0; j < anis.length; j++) {
6705 var ani = anis[j];
6706 var ani_p = ani._private;
6707 if (jumpToEnd) {
6708 // next iteration of the animation loop, the animation
6709 // will go straight to the end and be removed
6710 ani_p.duration = 0;
6711 }
6712 }
6713
6714 // clear the queue of future animations
6715 if (clearQueue) {
6716 _p.animation.queue = [];
6717 }
6718 if (!jumpToEnd) {
6719 _p.animation.current = [];
6720 }
6721 }
6722
6723 // we have to notify (the animation loop doesn't do it for us on `stop`)
6724 cy.notify('draw');
6725 return this;
6726 };
6727 } // stop
6728}; // define
6729
6730/**
6731 * Checks if `value` is classified as an `Array` object.
6732 *
6733 * @static
6734 * @memberOf _
6735 * @since 0.1.0
6736 * @category Lang
6737 * @param {*} value The value to check.
6738 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
6739 * @example
6740 *
6741 * _.isArray([1, 2, 3]);
6742 * // => true
6743 *
6744 * _.isArray(document.body.children);
6745 * // => false
6746 *
6747 * _.isArray('abc');
6748 * // => false
6749 *
6750 * _.isArray(_.noop);
6751 * // => false
6752 */
6753var isArray = Array.isArray;
6754
6755var isArray_1 = isArray;
6756
6757/** Used to match property names within property paths. */
6758var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
6759 reIsPlainProp = /^\w*$/;
6760
6761/**
6762 * Checks if `value` is a property name and not a property path.
6763 *
6764 * @private
6765 * @param {*} value The value to check.
6766 * @param {Object} [object] The object to query keys on.
6767 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
6768 */
6769function isKey(value, object) {
6770 if (isArray_1(value)) {
6771 return false;
6772 }
6773 var type = typeof value;
6774 if (type == 'number' || type == 'symbol' || type == 'boolean' ||
6775 value == null || isSymbol_1(value)) {
6776 return true;
6777 }
6778 return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
6779 (object != null && value in Object(object));
6780}
6781
6782var _isKey = isKey;
6783
6784/** `Object#toString` result references. */
6785var asyncTag = '[object AsyncFunction]',
6786 funcTag = '[object Function]',
6787 genTag = '[object GeneratorFunction]',
6788 proxyTag = '[object Proxy]';
6789
6790/**
6791 * Checks if `value` is classified as a `Function` object.
6792 *
6793 * @static
6794 * @memberOf _
6795 * @since 0.1.0
6796 * @category Lang
6797 * @param {*} value The value to check.
6798 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
6799 * @example
6800 *
6801 * _.isFunction(_);
6802 * // => true
6803 *
6804 * _.isFunction(/abc/);
6805 * // => false
6806 */
6807function isFunction(value) {
6808 if (!isObject_1(value)) {
6809 return false;
6810 }
6811 // The use of `Object#toString` avoids issues with the `typeof` operator
6812 // in Safari 9 which returns 'object' for typed arrays and other constructors.
6813 var tag = _baseGetTag(value);
6814 return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
6815}
6816
6817var isFunction_1 = isFunction;
6818
6819/** Used to detect overreaching core-js shims. */
6820var coreJsData = _root['__core-js_shared__'];
6821
6822var _coreJsData = coreJsData;
6823
6824/** Used to detect methods masquerading as native. */
6825var maskSrcKey = (function() {
6826 var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
6827 return uid ? ('Symbol(src)_1.' + uid) : '';
6828}());
6829
6830/**
6831 * Checks if `func` has its source masked.
6832 *
6833 * @private
6834 * @param {Function} func The function to check.
6835 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
6836 */
6837function isMasked(func) {
6838 return !!maskSrcKey && (maskSrcKey in func);
6839}
6840
6841var _isMasked = isMasked;
6842
6843/** Used for built-in method references. */
6844var funcProto$1 = Function.prototype;
6845
6846/** Used to resolve the decompiled source of functions. */
6847var funcToString$1 = funcProto$1.toString;
6848
6849/**
6850 * Converts `func` to its source code.
6851 *
6852 * @private
6853 * @param {Function} func The function to convert.
6854 * @returns {string} Returns the source code.
6855 */
6856function toSource(func) {
6857 if (func != null) {
6858 try {
6859 return funcToString$1.call(func);
6860 } catch (e) {}
6861 try {
6862 return (func + '');
6863 } catch (e) {}
6864 }
6865 return '';
6866}
6867
6868var _toSource = toSource;
6869
6870/**
6871 * Used to match `RegExp`
6872 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
6873 */
6874var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
6875
6876/** Used to detect host constructors (Safari). */
6877var reIsHostCtor = /^\[object .+?Constructor\]$/;
6878
6879/** Used for built-in method references. */
6880var funcProto = Function.prototype,
6881 objectProto$3 = Object.prototype;
6882
6883/** Used to resolve the decompiled source of functions. */
6884var funcToString = funcProto.toString;
6885
6886/** Used to check objects for own properties. */
6887var hasOwnProperty$3 = objectProto$3.hasOwnProperty;
6888
6889/** Used to detect if a method is native. */
6890var reIsNative = RegExp('^' +
6891 funcToString.call(hasOwnProperty$3).replace(reRegExpChar, '\\$&')
6892 .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
6893);
6894
6895/**
6896 * The base implementation of `_.isNative` without bad shim checks.
6897 *
6898 * @private
6899 * @param {*} value The value to check.
6900 * @returns {boolean} Returns `true` if `value` is a native function,
6901 * else `false`.
6902 */
6903function baseIsNative(value) {
6904 if (!isObject_1(value) || _isMasked(value)) {
6905 return false;
6906 }
6907 var pattern = isFunction_1(value) ? reIsNative : reIsHostCtor;
6908 return pattern.test(_toSource(value));
6909}
6910
6911var _baseIsNative = baseIsNative;
6912
6913/**
6914 * Gets the value at `key` of `object`.
6915 *
6916 * @private
6917 * @param {Object} [object] The object to query.
6918 * @param {string} key The key of the property to get.
6919 * @returns {*} Returns the property value.
6920 */
6921function getValue$1(object, key) {
6922 return object == null ? undefined : object[key];
6923}
6924
6925var _getValue = getValue$1;
6926
6927/**
6928 * Gets the native function at `key` of `object`.
6929 *
6930 * @private
6931 * @param {Object} object The object to query.
6932 * @param {string} key The key of the method to get.
6933 * @returns {*} Returns the function if it's native, else `undefined`.
6934 */
6935function getNative(object, key) {
6936 var value = _getValue(object, key);
6937 return _baseIsNative(value) ? value : undefined;
6938}
6939
6940var _getNative = getNative;
6941
6942/* Built-in method references that are verified to be native. */
6943var nativeCreate = _getNative(Object, 'create');
6944
6945var _nativeCreate = nativeCreate;
6946
6947/**
6948 * Removes all key-value entries from the hash.
6949 *
6950 * @private
6951 * @name clear
6952 * @memberOf Hash
6953 */
6954function hashClear() {
6955 this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
6956 this.size = 0;
6957}
6958
6959var _hashClear = hashClear;
6960
6961/**
6962 * Removes `key` and its value from the hash.
6963 *
6964 * @private
6965 * @name delete
6966 * @memberOf Hash
6967 * @param {Object} hash The hash to modify.
6968 * @param {string} key The key of the value to remove.
6969 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
6970 */
6971function hashDelete(key) {
6972 var result = this.has(key) && delete this.__data__[key];
6973 this.size -= result ? 1 : 0;
6974 return result;
6975}
6976
6977var _hashDelete = hashDelete;
6978
6979/** Used to stand-in for `undefined` hash values. */
6980var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
6981
6982/** Used for built-in method references. */
6983var objectProto$2 = Object.prototype;
6984
6985/** Used to check objects for own properties. */
6986var hasOwnProperty$2 = objectProto$2.hasOwnProperty;
6987
6988/**
6989 * Gets the hash value for `key`.
6990 *
6991 * @private
6992 * @name get
6993 * @memberOf Hash
6994 * @param {string} key The key of the value to get.
6995 * @returns {*} Returns the entry value.
6996 */
6997function hashGet(key) {
6998 var data = this.__data__;
6999 if (_nativeCreate) {
7000 var result = data[key];
7001 return result === HASH_UNDEFINED$1 ? undefined : result;
7002 }
7003 return hasOwnProperty$2.call(data, key) ? data[key] : undefined;
7004}
7005
7006var _hashGet = hashGet;
7007
7008/** Used for built-in method references. */
7009var objectProto$1 = Object.prototype;
7010
7011/** Used to check objects for own properties. */
7012var hasOwnProperty$1 = objectProto$1.hasOwnProperty;
7013
7014/**
7015 * Checks if a hash value for `key` exists.
7016 *
7017 * @private
7018 * @name has
7019 * @memberOf Hash
7020 * @param {string} key The key of the entry to check.
7021 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7022 */
7023function hashHas(key) {
7024 var data = this.__data__;
7025 return _nativeCreate ? (data[key] !== undefined) : hasOwnProperty$1.call(data, key);
7026}
7027
7028var _hashHas = hashHas;
7029
7030/** Used to stand-in for `undefined` hash values. */
7031var HASH_UNDEFINED = '__lodash_hash_undefined__';
7032
7033/**
7034 * Sets the hash `key` to `value`.
7035 *
7036 * @private
7037 * @name set
7038 * @memberOf Hash
7039 * @param {string} key The key of the value to set.
7040 * @param {*} value The value to set.
7041 * @returns {Object} Returns the hash instance.
7042 */
7043function hashSet(key, value) {
7044 var data = this.__data__;
7045 this.size += this.has(key) ? 0 : 1;
7046 data[key] = (_nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
7047 return this;
7048}
7049
7050var _hashSet = hashSet;
7051
7052/**
7053 * Creates a hash object.
7054 *
7055 * @private
7056 * @constructor
7057 * @param {Array} [entries] The key-value pairs to cache.
7058 */
7059function Hash(entries) {
7060 var index = -1,
7061 length = entries == null ? 0 : entries.length;
7062
7063 this.clear();
7064 while (++index < length) {
7065 var entry = entries[index];
7066 this.set(entry[0], entry[1]);
7067 }
7068}
7069
7070// Add methods to `Hash`.
7071Hash.prototype.clear = _hashClear;
7072Hash.prototype['delete'] = _hashDelete;
7073Hash.prototype.get = _hashGet;
7074Hash.prototype.has = _hashHas;
7075Hash.prototype.set = _hashSet;
7076
7077var _Hash = Hash;
7078
7079/**
7080 * Removes all key-value entries from the list cache.
7081 *
7082 * @private
7083 * @name clear
7084 * @memberOf ListCache
7085 */
7086function listCacheClear() {
7087 this.__data__ = [];
7088 this.size = 0;
7089}
7090
7091var _listCacheClear = listCacheClear;
7092
7093/**
7094 * Performs a
7095 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7096 * comparison between two values to determine if they are equivalent.
7097 *
7098 * @static
7099 * @memberOf _
7100 * @since 4.0.0
7101 * @category Lang
7102 * @param {*} value The value to compare.
7103 * @param {*} other The other value to compare.
7104 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
7105 * @example
7106 *
7107 * var object = { 'a': 1 };
7108 * var other = { 'a': 1 };
7109 *
7110 * _.eq(object, object);
7111 * // => true
7112 *
7113 * _.eq(object, other);
7114 * // => false
7115 *
7116 * _.eq('a', 'a');
7117 * // => true
7118 *
7119 * _.eq('a', Object('a'));
7120 * // => false
7121 *
7122 * _.eq(NaN, NaN);
7123 * // => true
7124 */
7125function eq(value, other) {
7126 return value === other || (value !== value && other !== other);
7127}
7128
7129var eq_1 = eq;
7130
7131/**
7132 * Gets the index at which the `key` is found in `array` of key-value pairs.
7133 *
7134 * @private
7135 * @param {Array} array The array to inspect.
7136 * @param {*} key The key to search for.
7137 * @returns {number} Returns the index of the matched value, else `-1`.
7138 */
7139function assocIndexOf(array, key) {
7140 var length = array.length;
7141 while (length--) {
7142 if (eq_1(array[length][0], key)) {
7143 return length;
7144 }
7145 }
7146 return -1;
7147}
7148
7149var _assocIndexOf = assocIndexOf;
7150
7151/** Used for built-in method references. */
7152var arrayProto = Array.prototype;
7153
7154/** Built-in value references. */
7155var splice = arrayProto.splice;
7156
7157/**
7158 * Removes `key` and its value from the list cache.
7159 *
7160 * @private
7161 * @name delete
7162 * @memberOf ListCache
7163 * @param {string} key The key of the value to remove.
7164 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7165 */
7166function listCacheDelete(key) {
7167 var data = this.__data__,
7168 index = _assocIndexOf(data, key);
7169
7170 if (index < 0) {
7171 return false;
7172 }
7173 var lastIndex = data.length - 1;
7174 if (index == lastIndex) {
7175 data.pop();
7176 } else {
7177 splice.call(data, index, 1);
7178 }
7179 --this.size;
7180 return true;
7181}
7182
7183var _listCacheDelete = listCacheDelete;
7184
7185/**
7186 * Gets the list cache value for `key`.
7187 *
7188 * @private
7189 * @name get
7190 * @memberOf ListCache
7191 * @param {string} key The key of the value to get.
7192 * @returns {*} Returns the entry value.
7193 */
7194function listCacheGet(key) {
7195 var data = this.__data__,
7196 index = _assocIndexOf(data, key);
7197
7198 return index < 0 ? undefined : data[index][1];
7199}
7200
7201var _listCacheGet = listCacheGet;
7202
7203/**
7204 * Checks if a list cache value for `key` exists.
7205 *
7206 * @private
7207 * @name has
7208 * @memberOf ListCache
7209 * @param {string} key The key of the entry to check.
7210 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7211 */
7212function listCacheHas(key) {
7213 return _assocIndexOf(this.__data__, key) > -1;
7214}
7215
7216var _listCacheHas = listCacheHas;
7217
7218/**
7219 * Sets the list cache `key` to `value`.
7220 *
7221 * @private
7222 * @name set
7223 * @memberOf ListCache
7224 * @param {string} key The key of the value to set.
7225 * @param {*} value The value to set.
7226 * @returns {Object} Returns the list cache instance.
7227 */
7228function listCacheSet(key, value) {
7229 var data = this.__data__,
7230 index = _assocIndexOf(data, key);
7231
7232 if (index < 0) {
7233 ++this.size;
7234 data.push([key, value]);
7235 } else {
7236 data[index][1] = value;
7237 }
7238 return this;
7239}
7240
7241var _listCacheSet = listCacheSet;
7242
7243/**
7244 * Creates an list cache object.
7245 *
7246 * @private
7247 * @constructor
7248 * @param {Array} [entries] The key-value pairs to cache.
7249 */
7250function ListCache(entries) {
7251 var index = -1,
7252 length = entries == null ? 0 : entries.length;
7253
7254 this.clear();
7255 while (++index < length) {
7256 var entry = entries[index];
7257 this.set(entry[0], entry[1]);
7258 }
7259}
7260
7261// Add methods to `ListCache`.
7262ListCache.prototype.clear = _listCacheClear;
7263ListCache.prototype['delete'] = _listCacheDelete;
7264ListCache.prototype.get = _listCacheGet;
7265ListCache.prototype.has = _listCacheHas;
7266ListCache.prototype.set = _listCacheSet;
7267
7268var _ListCache = ListCache;
7269
7270/* Built-in method references that are verified to be native. */
7271var Map$1 = _getNative(_root, 'Map');
7272
7273var _Map = Map$1;
7274
7275/**
7276 * Removes all key-value entries from the map.
7277 *
7278 * @private
7279 * @name clear
7280 * @memberOf MapCache
7281 */
7282function mapCacheClear() {
7283 this.size = 0;
7284 this.__data__ = {
7285 'hash': new _Hash,
7286 'map': new (_Map || _ListCache),
7287 'string': new _Hash
7288 };
7289}
7290
7291var _mapCacheClear = mapCacheClear;
7292
7293/**
7294 * Checks if `value` is suitable for use as unique object key.
7295 *
7296 * @private
7297 * @param {*} value The value to check.
7298 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
7299 */
7300function isKeyable(value) {
7301 var type = typeof value;
7302 return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
7303 ? (value !== '__proto__')
7304 : (value === null);
7305}
7306
7307var _isKeyable = isKeyable;
7308
7309/**
7310 * Gets the data for `map`.
7311 *
7312 * @private
7313 * @param {Object} map The map to query.
7314 * @param {string} key The reference key.
7315 * @returns {*} Returns the map data.
7316 */
7317function getMapData(map, key) {
7318 var data = map.__data__;
7319 return _isKeyable(key)
7320 ? data[typeof key == 'string' ? 'string' : 'hash']
7321 : data.map;
7322}
7323
7324var _getMapData = getMapData;
7325
7326/**
7327 * Removes `key` and its value from the map.
7328 *
7329 * @private
7330 * @name delete
7331 * @memberOf MapCache
7332 * @param {string} key The key of the value to remove.
7333 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7334 */
7335function mapCacheDelete(key) {
7336 var result = _getMapData(this, key)['delete'](key);
7337 this.size -= result ? 1 : 0;
7338 return result;
7339}
7340
7341var _mapCacheDelete = mapCacheDelete;
7342
7343/**
7344 * Gets the map value for `key`.
7345 *
7346 * @private
7347 * @name get
7348 * @memberOf MapCache
7349 * @param {string} key The key of the value to get.
7350 * @returns {*} Returns the entry value.
7351 */
7352function mapCacheGet(key) {
7353 return _getMapData(this, key).get(key);
7354}
7355
7356var _mapCacheGet = mapCacheGet;
7357
7358/**
7359 * Checks if a map value for `key` exists.
7360 *
7361 * @private
7362 * @name has
7363 * @memberOf MapCache
7364 * @param {string} key The key of the entry to check.
7365 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7366 */
7367function mapCacheHas(key) {
7368 return _getMapData(this, key).has(key);
7369}
7370
7371var _mapCacheHas = mapCacheHas;
7372
7373/**
7374 * Sets the map `key` to `value`.
7375 *
7376 * @private
7377 * @name set
7378 * @memberOf MapCache
7379 * @param {string} key The key of the value to set.
7380 * @param {*} value The value to set.
7381 * @returns {Object} Returns the map cache instance.
7382 */
7383function mapCacheSet(key, value) {
7384 var data = _getMapData(this, key),
7385 size = data.size;
7386
7387 data.set(key, value);
7388 this.size += data.size == size ? 0 : 1;
7389 return this;
7390}
7391
7392var _mapCacheSet = mapCacheSet;
7393
7394/**
7395 * Creates a map cache object to store key-value pairs.
7396 *
7397 * @private
7398 * @constructor
7399 * @param {Array} [entries] The key-value pairs to cache.
7400 */
7401function MapCache(entries) {
7402 var index = -1,
7403 length = entries == null ? 0 : entries.length;
7404
7405 this.clear();
7406 while (++index < length) {
7407 var entry = entries[index];
7408 this.set(entry[0], entry[1]);
7409 }
7410}
7411
7412// Add methods to `MapCache`.
7413MapCache.prototype.clear = _mapCacheClear;
7414MapCache.prototype['delete'] = _mapCacheDelete;
7415MapCache.prototype.get = _mapCacheGet;
7416MapCache.prototype.has = _mapCacheHas;
7417MapCache.prototype.set = _mapCacheSet;
7418
7419var _MapCache = MapCache;
7420
7421/** Error message constants. */
7422var FUNC_ERROR_TEXT = 'Expected a function';
7423
7424/**
7425 * Creates a function that memoizes the result of `func`. If `resolver` is
7426 * provided, it determines the cache key for storing the result based on the
7427 * arguments provided to the memoized function. By default, the first argument
7428 * provided to the memoized function is used as the map cache key. The `func`
7429 * is invoked with the `this` binding of the memoized function.
7430 *
7431 * **Note:** The cache is exposed as the `cache` property on the memoized
7432 * function. Its creation may be customized by replacing the `_.memoize.Cache`
7433 * constructor with one whose instances implement the
7434 * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
7435 * method interface of `clear`, `delete`, `get`, `has`, and `set`.
7436 *
7437 * @static
7438 * @memberOf _
7439 * @since 0.1.0
7440 * @category Function
7441 * @param {Function} func The function to have its output memoized.
7442 * @param {Function} [resolver] The function to resolve the cache key.
7443 * @returns {Function} Returns the new memoized function.
7444 * @example
7445 *
7446 * var object = { 'a': 1, 'b': 2 };
7447 * var other = { 'c': 3, 'd': 4 };
7448 *
7449 * var values = _.memoize(_.values);
7450 * values(object);
7451 * // => [1, 2]
7452 *
7453 * values(other);
7454 * // => [3, 4]
7455 *
7456 * object.a = 2;
7457 * values(object);
7458 * // => [1, 2]
7459 *
7460 * // Modify the result cache.
7461 * values.cache.set(object, ['a', 'b']);
7462 * values(object);
7463 * // => ['a', 'b']
7464 *
7465 * // Replace `_.memoize.Cache`.
7466 * _.memoize.Cache = WeakMap;
7467 */
7468function memoize(func, resolver) {
7469 if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
7470 throw new TypeError(FUNC_ERROR_TEXT);
7471 }
7472 var memoized = function() {
7473 var args = arguments,
7474 key = resolver ? resolver.apply(this, args) : args[0],
7475 cache = memoized.cache;
7476
7477 if (cache.has(key)) {
7478 return cache.get(key);
7479 }
7480 var result = func.apply(this, args);
7481 memoized.cache = cache.set(key, result) || cache;
7482 return result;
7483 };
7484 memoized.cache = new (memoize.Cache || _MapCache);
7485 return memoized;
7486}
7487
7488// Expose `MapCache`.
7489memoize.Cache = _MapCache;
7490
7491var memoize_1 = memoize;
7492
7493/** Used as the maximum memoize cache size. */
7494var MAX_MEMOIZE_SIZE = 500;
7495
7496/**
7497 * A specialized version of `_.memoize` which clears the memoized function's
7498 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
7499 *
7500 * @private
7501 * @param {Function} func The function to have its output memoized.
7502 * @returns {Function} Returns the new memoized function.
7503 */
7504function memoizeCapped(func) {
7505 var result = memoize_1(func, function(key) {
7506 if (cache.size === MAX_MEMOIZE_SIZE) {
7507 cache.clear();
7508 }
7509 return key;
7510 });
7511
7512 var cache = result.cache;
7513 return result;
7514}
7515
7516var _memoizeCapped = memoizeCapped;
7517
7518/** Used to match property names within property paths. */
7519var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
7520
7521/** Used to match backslashes in property paths. */
7522var reEscapeChar = /\\(\\)?/g;
7523
7524/**
7525 * Converts `string` to a property path array.
7526 *
7527 * @private
7528 * @param {string} string The string to convert.
7529 * @returns {Array} Returns the property path array.
7530 */
7531var stringToPath = _memoizeCapped(function(string) {
7532 var result = [];
7533 if (string.charCodeAt(0) === 46 /* . */) {
7534 result.push('');
7535 }
7536 string.replace(rePropName, function(match, number, quote, subString) {
7537 result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
7538 });
7539 return result;
7540});
7541
7542var _stringToPath = stringToPath;
7543
7544/**
7545 * A specialized version of `_.map` for arrays without support for iteratee
7546 * shorthands.
7547 *
7548 * @private
7549 * @param {Array} [array] The array to iterate over.
7550 * @param {Function} iteratee The function invoked per iteration.
7551 * @returns {Array} Returns the new mapped array.
7552 */
7553function arrayMap(array, iteratee) {
7554 var index = -1,
7555 length = array == null ? 0 : array.length,
7556 result = Array(length);
7557
7558 while (++index < length) {
7559 result[index] = iteratee(array[index], index, array);
7560 }
7561 return result;
7562}
7563
7564var _arrayMap = arrayMap;
7565
7566/** Used as references for various `Number` constants. */
7567var INFINITY$1 = 1 / 0;
7568
7569/** Used to convert symbols to primitives and strings. */
7570var symbolProto = _Symbol ? _Symbol.prototype : undefined,
7571 symbolToString = symbolProto ? symbolProto.toString : undefined;
7572
7573/**
7574 * The base implementation of `_.toString` which doesn't convert nullish
7575 * values to empty strings.
7576 *
7577 * @private
7578 * @param {*} value The value to process.
7579 * @returns {string} Returns the string.
7580 */
7581function baseToString(value) {
7582 // Exit early for strings to avoid a performance hit in some environments.
7583 if (typeof value == 'string') {
7584 return value;
7585 }
7586 if (isArray_1(value)) {
7587 // Recursively convert values (susceptible to call stack limits).
7588 return _arrayMap(value, baseToString) + '';
7589 }
7590 if (isSymbol_1(value)) {
7591 return symbolToString ? symbolToString.call(value) : '';
7592 }
7593 var result = (value + '');
7594 return (result == '0' && (1 / value) == -INFINITY$1) ? '-0' : result;
7595}
7596
7597var _baseToString = baseToString;
7598
7599/**
7600 * Converts `value` to a string. An empty string is returned for `null`
7601 * and `undefined` values. The sign of `-0` is preserved.
7602 *
7603 * @static
7604 * @memberOf _
7605 * @since 4.0.0
7606 * @category Lang
7607 * @param {*} value The value to convert.
7608 * @returns {string} Returns the converted string.
7609 * @example
7610 *
7611 * _.toString(null);
7612 * // => ''
7613 *
7614 * _.toString(-0);
7615 * // => '-0'
7616 *
7617 * _.toString([1, 2, 3]);
7618 * // => '1,2,3'
7619 */
7620function toString$1(value) {
7621 return value == null ? '' : _baseToString(value);
7622}
7623
7624var toString_1 = toString$1;
7625
7626/**
7627 * Casts `value` to a path array if it's not one.
7628 *
7629 * @private
7630 * @param {*} value The value to inspect.
7631 * @param {Object} [object] The object to query keys on.
7632 * @returns {Array} Returns the cast property path array.
7633 */
7634function castPath(value, object) {
7635 if (isArray_1(value)) {
7636 return value;
7637 }
7638 return _isKey(value, object) ? [value] : _stringToPath(toString_1(value));
7639}
7640
7641var _castPath = castPath;
7642
7643/** Used as references for various `Number` constants. */
7644var INFINITY = 1 / 0;
7645
7646/**
7647 * Converts `value` to a string key if it's not a string or symbol.
7648 *
7649 * @private
7650 * @param {*} value The value to inspect.
7651 * @returns {string|symbol} Returns the key.
7652 */
7653function toKey(value) {
7654 if (typeof value == 'string' || isSymbol_1(value)) {
7655 return value;
7656 }
7657 var result = (value + '');
7658 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
7659}
7660
7661var _toKey = toKey;
7662
7663/**
7664 * The base implementation of `_.get` without support for default values.
7665 *
7666 * @private
7667 * @param {Object} object The object to query.
7668 * @param {Array|string} path The path of the property to get.
7669 * @returns {*} Returns the resolved value.
7670 */
7671function baseGet(object, path) {
7672 path = _castPath(path, object);
7673
7674 var index = 0,
7675 length = path.length;
7676
7677 while (object != null && index < length) {
7678 object = object[_toKey(path[index++])];
7679 }
7680 return (index && index == length) ? object : undefined;
7681}
7682
7683var _baseGet = baseGet;
7684
7685/**
7686 * Gets the value at `path` of `object`. If the resolved value is
7687 * `undefined`, the `defaultValue` is returned in its place.
7688 *
7689 * @static
7690 * @memberOf _
7691 * @since 3.7.0
7692 * @category Object
7693 * @param {Object} object The object to query.
7694 * @param {Array|string} path The path of the property to get.
7695 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
7696 * @returns {*} Returns the resolved value.
7697 * @example
7698 *
7699 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
7700 *
7701 * _.get(object, 'a[0].b.c');
7702 * // => 3
7703 *
7704 * _.get(object, ['a', '0', 'b', 'c']);
7705 * // => 3
7706 *
7707 * _.get(object, 'a.b.c', 'default');
7708 * // => 'default'
7709 */
7710function get(object, path, defaultValue) {
7711 var result = object == null ? undefined : _baseGet(object, path);
7712 return result === undefined ? defaultValue : result;
7713}
7714
7715var get_1 = get;
7716
7717var defineProperty = (function() {
7718 try {
7719 var func = _getNative(Object, 'defineProperty');
7720 func({}, '', {});
7721 return func;
7722 } catch (e) {}
7723}());
7724
7725var _defineProperty = defineProperty;
7726
7727/**
7728 * The base implementation of `assignValue` and `assignMergeValue` without
7729 * value checks.
7730 *
7731 * @private
7732 * @param {Object} object The object to modify.
7733 * @param {string} key The key of the property to assign.
7734 * @param {*} value The value to assign.
7735 */
7736function baseAssignValue(object, key, value) {
7737 if (key == '__proto__' && _defineProperty) {
7738 _defineProperty(object, key, {
7739 'configurable': true,
7740 'enumerable': true,
7741 'value': value,
7742 'writable': true
7743 });
7744 } else {
7745 object[key] = value;
7746 }
7747}
7748
7749var _baseAssignValue = baseAssignValue;
7750
7751/** Used for built-in method references. */
7752var objectProto = Object.prototype;
7753
7754/** Used to check objects for own properties. */
7755var hasOwnProperty = objectProto.hasOwnProperty;
7756
7757/**
7758 * Assigns `value` to `key` of `object` if the existing value is not equivalent
7759 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7760 * for equality comparisons.
7761 *
7762 * @private
7763 * @param {Object} object The object to modify.
7764 * @param {string} key The key of the property to assign.
7765 * @param {*} value The value to assign.
7766 */
7767function assignValue(object, key, value) {
7768 var objValue = object[key];
7769 if (!(hasOwnProperty.call(object, key) && eq_1(objValue, value)) ||
7770 (value === undefined && !(key in object))) {
7771 _baseAssignValue(object, key, value);
7772 }
7773}
7774
7775var _assignValue = assignValue;
7776
7777/** Used as references for various `Number` constants. */
7778var MAX_SAFE_INTEGER = 9007199254740991;
7779
7780/** Used to detect unsigned integer values. */
7781var reIsUint = /^(?:0|[1-9]\d*)$/;
7782
7783/**
7784 * Checks if `value` is a valid array-like index.
7785 *
7786 * @private
7787 * @param {*} value The value to check.
7788 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
7789 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
7790 */
7791function isIndex(value, length) {
7792 var type = typeof value;
7793 length = length == null ? MAX_SAFE_INTEGER : length;
7794
7795 return !!length &&
7796 (type == 'number' ||
7797 (type != 'symbol' && reIsUint.test(value))) &&
7798 (value > -1 && value % 1 == 0 && value < length);
7799}
7800
7801var _isIndex = isIndex;
7802
7803/**
7804 * The base implementation of `_.set`.
7805 *
7806 * @private
7807 * @param {Object} object The object to modify.
7808 * @param {Array|string} path The path of the property to set.
7809 * @param {*} value The value to set.
7810 * @param {Function} [customizer] The function to customize path creation.
7811 * @returns {Object} Returns `object`.
7812 */
7813function baseSet(object, path, value, customizer) {
7814 if (!isObject_1(object)) {
7815 return object;
7816 }
7817 path = _castPath(path, object);
7818
7819 var index = -1,
7820 length = path.length,
7821 lastIndex = length - 1,
7822 nested = object;
7823
7824 while (nested != null && ++index < length) {
7825 var key = _toKey(path[index]),
7826 newValue = value;
7827
7828 if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
7829 return object;
7830 }
7831
7832 if (index != lastIndex) {
7833 var objValue = nested[key];
7834 newValue = customizer ? customizer(objValue, key, nested) : undefined;
7835 if (newValue === undefined) {
7836 newValue = isObject_1(objValue)
7837 ? objValue
7838 : (_isIndex(path[index + 1]) ? [] : {});
7839 }
7840 }
7841 _assignValue(nested, key, newValue);
7842 nested = nested[key];
7843 }
7844 return object;
7845}
7846
7847var _baseSet = baseSet;
7848
7849/**
7850 * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
7851 * it's created. Arrays are created for missing index properties while objects
7852 * are created for all other missing properties. Use `_.setWith` to customize
7853 * `path` creation.
7854 *
7855 * **Note:** This method mutates `object`.
7856 *
7857 * @static
7858 * @memberOf _
7859 * @since 3.7.0
7860 * @category Object
7861 * @param {Object} object The object to modify.
7862 * @param {Array|string} path The path of the property to set.
7863 * @param {*} value The value to set.
7864 * @returns {Object} Returns `object`.
7865 * @example
7866 *
7867 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
7868 *
7869 * _.set(object, 'a[0].b.c', 4);
7870 * console.log(object.a[0].b.c);
7871 * // => 4
7872 *
7873 * _.set(object, ['x', '0', 'y', 'z'], 5);
7874 * console.log(object.x[0].y.z);
7875 * // => 5
7876 */
7877function set(object, path, value) {
7878 return object == null ? object : _baseSet(object, path, value);
7879}
7880
7881var set_1 = set;
7882
7883/**
7884 * Copies the values of `source` to `array`.
7885 *
7886 * @private
7887 * @param {Array} source The array to copy values from.
7888 * @param {Array} [array=[]] The array to copy values to.
7889 * @returns {Array} Returns `array`.
7890 */
7891function copyArray(source, array) {
7892 var index = -1,
7893 length = source.length;
7894
7895 array || (array = Array(length));
7896 while (++index < length) {
7897 array[index] = source[index];
7898 }
7899 return array;
7900}
7901
7902var _copyArray = copyArray;
7903
7904/**
7905 * Converts `value` to a property path array.
7906 *
7907 * @static
7908 * @memberOf _
7909 * @since 4.0.0
7910 * @category Util
7911 * @param {*} value The value to convert.
7912 * @returns {Array} Returns the new property path array.
7913 * @example
7914 *
7915 * _.toPath('a.b.c');
7916 * // => ['a', 'b', 'c']
7917 *
7918 * _.toPath('a[0].b.c');
7919 * // => ['a', '0', 'b', 'c']
7920 */
7921function toPath(value) {
7922 if (isArray_1(value)) {
7923 return _arrayMap(value, _toKey);
7924 }
7925 return isSymbol_1(value) ? [value] : _copyArray(_stringToPath(toString_1(value)));
7926}
7927
7928var toPath_1 = toPath;
7929
7930var define$2 = {
7931 // access data field
7932 data: function data(params) {
7933 var defaults = {
7934 field: 'data',
7935 bindingEvent: 'data',
7936 allowBinding: false,
7937 allowSetting: false,
7938 allowGetting: false,
7939 settingEvent: 'data',
7940 settingTriggersEvent: false,
7941 triggerFnName: 'trigger',
7942 immutableKeys: {},
7943 // key => true if immutable
7944 updateStyle: false,
7945 beforeGet: function beforeGet(self) {},
7946 beforeSet: function beforeSet(self, obj) {},
7947 onSet: function onSet(self) {},
7948 canSet: function canSet(self) {
7949 return true;
7950 }
7951 };
7952 params = extend({}, defaults, params);
7953 return function dataImpl(name, value) {
7954 var p = params;
7955 var self = this;
7956 var selfIsArrayLike = self.length !== undefined;
7957 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7958 var single = selfIsArrayLike ? self[0] : self;
7959
7960 // .data('foo', ...)
7961 if (string(name)) {
7962 // set or get property
7963 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
7964 var path = isPathLike && toPath_1(name);
7965
7966 // .data('foo')
7967 if (p.allowGetting && value === undefined) {
7968 // get
7969
7970 var ret;
7971 if (single) {
7972 p.beforeGet(single);
7973
7974 // check if it's path and a field with the same name doesn't exist
7975 if (path && single._private[p.field][name] === undefined) {
7976 ret = get_1(single._private[p.field], path);
7977 } else {
7978 ret = single._private[p.field][name];
7979 }
7980 }
7981 return ret;
7982
7983 // .data('foo', 'bar')
7984 } else if (p.allowSetting && value !== undefined) {
7985 // set
7986 var valid = !p.immutableKeys[name];
7987 if (valid) {
7988 var change = _defineProperty$1({}, name, value);
7989 p.beforeSet(self, change);
7990 for (var i = 0, l = all.length; i < l; i++) {
7991 var ele = all[i];
7992 if (p.canSet(ele)) {
7993 if (path && single._private[p.field][name] === undefined) {
7994 set_1(ele._private[p.field], path, value);
7995 } else {
7996 ele._private[p.field][name] = value;
7997 }
7998 }
7999 }
8000
8001 // update mappers if asked
8002 if (p.updateStyle) {
8003 self.updateStyle();
8004 }
8005
8006 // call onSet callback
8007 p.onSet(self);
8008 if (p.settingTriggersEvent) {
8009 self[p.triggerFnName](p.settingEvent);
8010 }
8011 }
8012 }
8013
8014 // .data({ 'foo': 'bar' })
8015 } else if (p.allowSetting && plainObject(name)) {
8016 // extend
8017 var obj = name;
8018 var k, v;
8019 var keys = Object.keys(obj);
8020 p.beforeSet(self, obj);
8021 for (var _i = 0; _i < keys.length; _i++) {
8022 k = keys[_i];
8023 v = obj[k];
8024 var _valid = !p.immutableKeys[k];
8025 if (_valid) {
8026 for (var j = 0; j < all.length; j++) {
8027 var _ele = all[j];
8028 if (p.canSet(_ele)) {
8029 _ele._private[p.field][k] = v;
8030 }
8031 }
8032 }
8033 }
8034
8035 // update mappers if asked
8036 if (p.updateStyle) {
8037 self.updateStyle();
8038 }
8039
8040 // call onSet callback
8041 p.onSet(self);
8042 if (p.settingTriggersEvent) {
8043 self[p.triggerFnName](p.settingEvent);
8044 }
8045
8046 // .data(function(){ ... })
8047 } else if (p.allowBinding && fn$6(name)) {
8048 // bind to event
8049 var fn = name;
8050 self.on(p.bindingEvent, fn);
8051
8052 // .data()
8053 } else if (p.allowGetting && name === undefined) {
8054 // get whole object
8055 var _ret;
8056 if (single) {
8057 p.beforeGet(single);
8058 _ret = single._private[p.field];
8059 }
8060 return _ret;
8061 }
8062 return self; // maintain chainability
8063 }; // function
8064 },
8065
8066 // data
8067
8068 // remove data field
8069 removeData: function removeData(params) {
8070 var defaults = {
8071 field: 'data',
8072 event: 'data',
8073 triggerFnName: 'trigger',
8074 triggerEvent: false,
8075 immutableKeys: {} // key => true if immutable
8076 };
8077
8078 params = extend({}, defaults, params);
8079 return function removeDataImpl(names) {
8080 var p = params;
8081 var self = this;
8082 var selfIsArrayLike = self.length !== undefined;
8083 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
8084
8085 // .removeData('foo bar')
8086 if (string(names)) {
8087 // then get the list of keys, and delete them
8088 var keys = names.split(/\s+/);
8089 var l = keys.length;
8090 for (var i = 0; i < l; i++) {
8091 // delete each non-empty key
8092 var key = keys[i];
8093 if (emptyString(key)) {
8094 continue;
8095 }
8096 var valid = !p.immutableKeys[key]; // not valid if immutable
8097 if (valid) {
8098 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
8099 all[i_a]._private[p.field][key] = undefined;
8100 }
8101 }
8102 }
8103 if (p.triggerEvent) {
8104 self[p.triggerFnName](p.event);
8105 }
8106
8107 // .removeData()
8108 } else if (names === undefined) {
8109 // then delete all keys
8110
8111 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
8112 var _privateFields = all[_i_a]._private[p.field];
8113 var _keys = Object.keys(_privateFields);
8114 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
8115 var _key = _keys[_i2];
8116 var validKeyToDelete = !p.immutableKeys[_key];
8117 if (validKeyToDelete) {
8118 _privateFields[_key] = undefined;
8119 }
8120 }
8121 }
8122 if (p.triggerEvent) {
8123 self[p.triggerFnName](p.event);
8124 }
8125 }
8126 return self; // maintain chaining
8127 }; // function
8128 } // removeData
8129}; // define
8130
8131var define$1 = {
8132 eventAliasesOn: function eventAliasesOn(proto) {
8133 var p = proto;
8134 p.addListener = p.listen = p.bind = p.on;
8135 p.unlisten = p.unbind = p.off = p.removeListener;
8136 p.trigger = p.emit;
8137
8138 // this is just a wrapper alias of .on()
8139 p.pon = p.promiseOn = function (events, selector) {
8140 var self = this;
8141 var args = Array.prototype.slice.call(arguments, 0);
8142 return new Promise$1(function (resolve, reject) {
8143 var callback = function callback(e) {
8144 self.off.apply(self, offArgs);
8145 resolve(e);
8146 };
8147 var onArgs = args.concat([callback]);
8148 var offArgs = onArgs.concat([]);
8149 self.on.apply(self, onArgs);
8150 });
8151 };
8152 }
8153}; // define
8154
8155// use this module to cherry pick functions into your prototype
8156var define = {};
8157[define$3, define$2, define$1].forEach(function (m) {
8158 extend(define, m);
8159});
8160
8161var elesfn$i = {
8162 animate: define.animate(),
8163 animation: define.animation(),
8164 animated: define.animated(),
8165 clearQueue: define.clearQueue(),
8166 delay: define.delay(),
8167 delayAnimation: define.delayAnimation(),
8168 stop: define.stop()
8169};
8170
8171var elesfn$h = {
8172 classes: function classes(_classes) {
8173 var self = this;
8174 if (_classes === undefined) {
8175 var ret = [];
8176 self[0]._private.classes.forEach(function (cls) {
8177 return ret.push(cls);
8178 });
8179 return ret;
8180 } else if (!array(_classes)) {
8181 // extract classes from string
8182 _classes = (_classes || '').match(/\S+/g) || [];
8183 }
8184 var changed = [];
8185 var classesSet = new Set$1(_classes);
8186
8187 // check and update each ele
8188 for (var j = 0; j < self.length; j++) {
8189 var ele = self[j];
8190 var _p = ele._private;
8191 var eleClasses = _p.classes;
8192 var changedEle = false;
8193
8194 // check if ele has all of the passed classes
8195 for (var i = 0; i < _classes.length; i++) {
8196 var cls = _classes[i];
8197 var eleHasClass = eleClasses.has(cls);
8198 if (!eleHasClass) {
8199 changedEle = true;
8200 break;
8201 }
8202 }
8203
8204 // check if ele has classes outside of those passed
8205 if (!changedEle) {
8206 changedEle = eleClasses.size !== _classes.length;
8207 }
8208 if (changedEle) {
8209 _p.classes = classesSet;
8210 changed.push(ele);
8211 }
8212 }
8213
8214 // trigger update style on those eles that had class changes
8215 if (changed.length > 0) {
8216 this.spawn(changed).updateStyle().emit('class');
8217 }
8218 return self;
8219 },
8220 addClass: function addClass(classes) {
8221 return this.toggleClass(classes, true);
8222 },
8223 hasClass: function hasClass(className) {
8224 var ele = this[0];
8225 return ele != null && ele._private.classes.has(className);
8226 },
8227 toggleClass: function toggleClass(classes, toggle) {
8228 if (!array(classes)) {
8229 // extract classes from string
8230 classes = classes.match(/\S+/g) || [];
8231 }
8232 var self = this;
8233 var toggleUndefd = toggle === undefined;
8234 var changed = []; // eles who had classes changed
8235
8236 for (var i = 0, il = self.length; i < il; i++) {
8237 var ele = self[i];
8238 var eleClasses = ele._private.classes;
8239 var changedEle = false;
8240 for (var j = 0; j < classes.length; j++) {
8241 var cls = classes[j];
8242 var hasClass = eleClasses.has(cls);
8243 var changedNow = false;
8244 if (toggle || toggleUndefd && !hasClass) {
8245 eleClasses.add(cls);
8246 changedNow = true;
8247 } else if (!toggle || toggleUndefd && hasClass) {
8248 eleClasses["delete"](cls);
8249 changedNow = true;
8250 }
8251 if (!changedEle && changedNow) {
8252 changed.push(ele);
8253 changedEle = true;
8254 }
8255 } // for j classes
8256 } // for i eles
8257
8258 // trigger update style on those eles that had class changes
8259 if (changed.length > 0) {
8260 this.spawn(changed).updateStyle().emit('class');
8261 }
8262 return self;
8263 },
8264 removeClass: function removeClass(classes) {
8265 return this.toggleClass(classes, false);
8266 },
8267 flashClass: function flashClass(classes, duration) {
8268 var self = this;
8269 if (duration == null) {
8270 duration = 250;
8271 } else if (duration === 0) {
8272 return self; // nothing to do really
8273 }
8274
8275 self.addClass(classes);
8276 setTimeout(function () {
8277 self.removeClass(classes);
8278 }, duration);
8279 return self;
8280 }
8281};
8282elesfn$h.className = elesfn$h.classNames = elesfn$h.classes;
8283
8284// tokens in the query language
8285var tokens = {
8286 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
8287 // chars we need to escape in let names, etc
8288 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
8289 // binary comparison op (used in data selectors)
8290 boolOp: '\\?|\\!|\\^',
8291 // boolean (unary) operators (used in data selectors)
8292 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
8293 // string literals (used in data selectors) -- doublequotes | singlequotes
8294 number: number,
8295 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
8296 meta: 'degree|indegree|outdegree',
8297 // allowed metadata fields (i.e. allowed functions to use from Collection)
8298 separator: '\\s*,\\s*',
8299 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
8300 descendant: '\\s+',
8301 child: '\\s+>\\s+',
8302 subject: '\\$',
8303 group: 'node|edge|\\*',
8304 directedEdge: '\\s+->\\s+',
8305 undirectedEdge: '\\s+<->\\s+'
8306};
8307tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
8308tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
8309tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
8310tokens.id = tokens.variable; // an element id (follows variable conventions)
8311
8312(function () {
8313 var ops, op, i;
8314
8315 // add @ variants to comparatorOp
8316 ops = tokens.comparatorOp.split('|');
8317 for (i = 0; i < ops.length; i++) {
8318 op = ops[i];
8319 tokens.comparatorOp += '|@' + op;
8320 }
8321
8322 // add ! variants to comparatorOp
8323 ops = tokens.comparatorOp.split('|');
8324 for (i = 0; i < ops.length; i++) {
8325 op = ops[i];
8326 if (op.indexOf('!') >= 0) {
8327 continue;
8328 } // skip ops that explicitly contain !
8329 if (op === '=') {
8330 continue;
8331 } // skip = b/c != is explicitly defined
8332
8333 tokens.comparatorOp += '|\\!' + op;
8334 }
8335})();
8336
8337/**
8338 * Make a new query object
8339 *
8340 * @prop type {Type} The type enum (int) of the query
8341 * @prop checks List of checks to make against an ele to test for a match
8342 */
8343var newQuery = function newQuery() {
8344 return {
8345 checks: []
8346 };
8347};
8348
8349/**
8350 * A check type enum-like object. Uses integer values for fast match() lookup.
8351 * The ordering does not matter as long as the ints are unique.
8352 */
8353var Type = {
8354 /** E.g. node */
8355 GROUP: 0,
8356 /** A collection of elements */
8357 COLLECTION: 1,
8358 /** A filter(ele) function */
8359 FILTER: 2,
8360 /** E.g. [foo > 1] */
8361 DATA_COMPARE: 3,
8362 /** E.g. [foo] */
8363 DATA_EXIST: 4,
8364 /** E.g. [?foo] */
8365 DATA_BOOL: 5,
8366 /** E.g. [[degree > 2]] */
8367 META_COMPARE: 6,
8368 /** E.g. :selected */
8369 STATE: 7,
8370 /** E.g. #foo */
8371 ID: 8,
8372 /** E.g. .foo */
8373 CLASS: 9,
8374 /** E.g. #foo <-> #bar */
8375 UNDIRECTED_EDGE: 10,
8376 /** E.g. #foo -> #bar */
8377 DIRECTED_EDGE: 11,
8378 /** E.g. $#foo -> #bar */
8379 NODE_SOURCE: 12,
8380 /** E.g. #foo -> $#bar */
8381 NODE_TARGET: 13,
8382 /** E.g. $#foo <-> #bar */
8383 NODE_NEIGHBOR: 14,
8384 /** E.g. #foo > #bar */
8385 CHILD: 15,
8386 /** E.g. #foo #bar */
8387 DESCENDANT: 16,
8388 /** E.g. $#foo > #bar */
8389 PARENT: 17,
8390 /** E.g. $#foo #bar */
8391 ANCESTOR: 18,
8392 /** E.g. #foo > $bar > #baz */
8393 COMPOUND_SPLIT: 19,
8394 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
8395 TRUE: 20
8396};
8397
8398var stateSelectors = [{
8399 selector: ':selected',
8400 matches: function matches(ele) {
8401 return ele.selected();
8402 }
8403}, {
8404 selector: ':unselected',
8405 matches: function matches(ele) {
8406 return !ele.selected();
8407 }
8408}, {
8409 selector: ':selectable',
8410 matches: function matches(ele) {
8411 return ele.selectable();
8412 }
8413}, {
8414 selector: ':unselectable',
8415 matches: function matches(ele) {
8416 return !ele.selectable();
8417 }
8418}, {
8419 selector: ':locked',
8420 matches: function matches(ele) {
8421 return ele.locked();
8422 }
8423}, {
8424 selector: ':unlocked',
8425 matches: function matches(ele) {
8426 return !ele.locked();
8427 }
8428}, {
8429 selector: ':visible',
8430 matches: function matches(ele) {
8431 return ele.visible();
8432 }
8433}, {
8434 selector: ':hidden',
8435 matches: function matches(ele) {
8436 return !ele.visible();
8437 }
8438}, {
8439 selector: ':transparent',
8440 matches: function matches(ele) {
8441 return ele.transparent();
8442 }
8443}, {
8444 selector: ':grabbed',
8445 matches: function matches(ele) {
8446 return ele.grabbed();
8447 }
8448}, {
8449 selector: ':free',
8450 matches: function matches(ele) {
8451 return !ele.grabbed();
8452 }
8453}, {
8454 selector: ':removed',
8455 matches: function matches(ele) {
8456 return ele.removed();
8457 }
8458}, {
8459 selector: ':inside',
8460 matches: function matches(ele) {
8461 return !ele.removed();
8462 }
8463}, {
8464 selector: ':grabbable',
8465 matches: function matches(ele) {
8466 return ele.grabbable();
8467 }
8468}, {
8469 selector: ':ungrabbable',
8470 matches: function matches(ele) {
8471 return !ele.grabbable();
8472 }
8473}, {
8474 selector: ':animated',
8475 matches: function matches(ele) {
8476 return ele.animated();
8477 }
8478}, {
8479 selector: ':unanimated',
8480 matches: function matches(ele) {
8481 return !ele.animated();
8482 }
8483}, {
8484 selector: ':parent',
8485 matches: function matches(ele) {
8486 return ele.isParent();
8487 }
8488}, {
8489 selector: ':childless',
8490 matches: function matches(ele) {
8491 return ele.isChildless();
8492 }
8493}, {
8494 selector: ':child',
8495 matches: function matches(ele) {
8496 return ele.isChild();
8497 }
8498}, {
8499 selector: ':orphan',
8500 matches: function matches(ele) {
8501 return ele.isOrphan();
8502 }
8503}, {
8504 selector: ':nonorphan',
8505 matches: function matches(ele) {
8506 return ele.isChild();
8507 }
8508}, {
8509 selector: ':compound',
8510 matches: function matches(ele) {
8511 if (ele.isNode()) {
8512 return ele.isParent();
8513 } else {
8514 return ele.source().isParent() || ele.target().isParent();
8515 }
8516 }
8517}, {
8518 selector: ':loop',
8519 matches: function matches(ele) {
8520 return ele.isLoop();
8521 }
8522}, {
8523 selector: ':simple',
8524 matches: function matches(ele) {
8525 return ele.isSimple();
8526 }
8527}, {
8528 selector: ':active',
8529 matches: function matches(ele) {
8530 return ele.active();
8531 }
8532}, {
8533 selector: ':inactive',
8534 matches: function matches(ele) {
8535 return !ele.active();
8536 }
8537}, {
8538 selector: ':backgrounding',
8539 matches: function matches(ele) {
8540 return ele.backgrounding();
8541 }
8542}, {
8543 selector: ':nonbackgrounding',
8544 matches: function matches(ele) {
8545 return !ele.backgrounding();
8546 }
8547}].sort(function (a, b) {
8548 // n.b. selectors that are starting substrings of others must have the longer ones first
8549 return descending(a.selector, b.selector);
8550});
8551var lookup = function () {
8552 var selToFn = {};
8553 var s;
8554 for (var i = 0; i < stateSelectors.length; i++) {
8555 s = stateSelectors[i];
8556 selToFn[s.selector] = s.matches;
8557 }
8558 return selToFn;
8559}();
8560var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
8561 return lookup[sel](ele);
8562};
8563var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
8564 return s.selector;
8565}).join('|') + ')';
8566
8567// when a token like a variable has escaped meta characters, we need to clean the backslashes out
8568// so that values get compared properly in Selector.filter()
8569var cleanMetaChars = function cleanMetaChars(str) {
8570 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
8571 return $1;
8572 });
8573};
8574var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
8575 selector[selector.length - 1] = replacementQuery;
8576};
8577
8578// NOTE: add new expression syntax here to have it recognised by the parser;
8579// - a query contains all adjacent (i.e. no separator in between) expressions;
8580// - the current query is stored in selector[i]
8581// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
8582var exprs = [{
8583 name: 'group',
8584 // just used for identifying when debugging
8585 query: true,
8586 regex: '(' + tokens.group + ')',
8587 populate: function populate(selector, query, _ref) {
8588 var _ref2 = _slicedToArray(_ref, 1),
8589 group = _ref2[0];
8590 query.checks.push({
8591 type: Type.GROUP,
8592 value: group === '*' ? group : group + 's'
8593 });
8594 }
8595}, {
8596 name: 'state',
8597 query: true,
8598 regex: stateSelectorRegex,
8599 populate: function populate(selector, query, _ref3) {
8600 var _ref4 = _slicedToArray(_ref3, 1),
8601 state = _ref4[0];
8602 query.checks.push({
8603 type: Type.STATE,
8604 value: state
8605 });
8606 }
8607}, {
8608 name: 'id',
8609 query: true,
8610 regex: '\\#(' + tokens.id + ')',
8611 populate: function populate(selector, query, _ref5) {
8612 var _ref6 = _slicedToArray(_ref5, 1),
8613 id = _ref6[0];
8614 query.checks.push({
8615 type: Type.ID,
8616 value: cleanMetaChars(id)
8617 });
8618 }
8619}, {
8620 name: 'className',
8621 query: true,
8622 regex: '\\.(' + tokens.className + ')',
8623 populate: function populate(selector, query, _ref7) {
8624 var _ref8 = _slicedToArray(_ref7, 1),
8625 className = _ref8[0];
8626 query.checks.push({
8627 type: Type.CLASS,
8628 value: cleanMetaChars(className)
8629 });
8630 }
8631}, {
8632 name: 'dataExists',
8633 query: true,
8634 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
8635 populate: function populate(selector, query, _ref9) {
8636 var _ref10 = _slicedToArray(_ref9, 1),
8637 variable = _ref10[0];
8638 query.checks.push({
8639 type: Type.DATA_EXIST,
8640 field: cleanMetaChars(variable)
8641 });
8642 }
8643}, {
8644 name: 'dataCompare',
8645 query: true,
8646 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
8647 populate: function populate(selector, query, _ref11) {
8648 var _ref12 = _slicedToArray(_ref11, 3),
8649 variable = _ref12[0],
8650 comparatorOp = _ref12[1],
8651 value = _ref12[2];
8652 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
8653 if (valueIsString) {
8654 value = value.substring(1, value.length - 1);
8655 } else {
8656 value = parseFloat(value);
8657 }
8658 query.checks.push({
8659 type: Type.DATA_COMPARE,
8660 field: cleanMetaChars(variable),
8661 operator: comparatorOp,
8662 value: value
8663 });
8664 }
8665}, {
8666 name: 'dataBool',
8667 query: true,
8668 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
8669 populate: function populate(selector, query, _ref13) {
8670 var _ref14 = _slicedToArray(_ref13, 2),
8671 boolOp = _ref14[0],
8672 variable = _ref14[1];
8673 query.checks.push({
8674 type: Type.DATA_BOOL,
8675 field: cleanMetaChars(variable),
8676 operator: boolOp
8677 });
8678 }
8679}, {
8680 name: 'metaCompare',
8681 query: true,
8682 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
8683 populate: function populate(selector, query, _ref15) {
8684 var _ref16 = _slicedToArray(_ref15, 3),
8685 meta = _ref16[0],
8686 comparatorOp = _ref16[1],
8687 number = _ref16[2];
8688 query.checks.push({
8689 type: Type.META_COMPARE,
8690 field: cleanMetaChars(meta),
8691 operator: comparatorOp,
8692 value: parseFloat(number)
8693 });
8694 }
8695}, {
8696 name: 'nextQuery',
8697 separator: true,
8698 regex: tokens.separator,
8699 populate: function populate(selector, query) {
8700 var currentSubject = selector.currentSubject;
8701 var edgeCount = selector.edgeCount;
8702 var compoundCount = selector.compoundCount;
8703 var lastQ = selector[selector.length - 1];
8704 if (currentSubject != null) {
8705 lastQ.subject = currentSubject;
8706 selector.currentSubject = null;
8707 }
8708 lastQ.edgeCount = edgeCount;
8709 lastQ.compoundCount = compoundCount;
8710 selector.edgeCount = 0;
8711 selector.compoundCount = 0;
8712
8713 // go on to next query
8714 var nextQuery = selector[selector.length++] = newQuery();
8715 return nextQuery; // this is the new query to be filled by the following exprs
8716 }
8717}, {
8718 name: 'directedEdge',
8719 separator: true,
8720 regex: tokens.directedEdge,
8721 populate: function populate(selector, query) {
8722 if (selector.currentSubject == null) {
8723 // undirected edge
8724 var edgeQuery = newQuery();
8725 var source = query;
8726 var target = newQuery();
8727 edgeQuery.checks.push({
8728 type: Type.DIRECTED_EDGE,
8729 source: source,
8730 target: target
8731 });
8732
8733 // the query in the selector should be the edge rather than the source
8734 replaceLastQuery(selector, query, edgeQuery);
8735 selector.edgeCount++;
8736
8737 // we're now populating the target query with expressions that follow
8738 return target;
8739 } else {
8740 // source/target
8741 var srcTgtQ = newQuery();
8742 var _source = query;
8743 var _target = newQuery();
8744 srcTgtQ.checks.push({
8745 type: Type.NODE_SOURCE,
8746 source: _source,
8747 target: _target
8748 });
8749
8750 // the query in the selector should be the neighbourhood rather than the node
8751 replaceLastQuery(selector, query, srcTgtQ);
8752 selector.edgeCount++;
8753 return _target; // now populating the target with the following expressions
8754 }
8755 }
8756}, {
8757 name: 'undirectedEdge',
8758 separator: true,
8759 regex: tokens.undirectedEdge,
8760 populate: function populate(selector, query) {
8761 if (selector.currentSubject == null) {
8762 // undirected edge
8763 var edgeQuery = newQuery();
8764 var source = query;
8765 var target = newQuery();
8766 edgeQuery.checks.push({
8767 type: Type.UNDIRECTED_EDGE,
8768 nodes: [source, target]
8769 });
8770
8771 // the query in the selector should be the edge rather than the source
8772 replaceLastQuery(selector, query, edgeQuery);
8773 selector.edgeCount++;
8774
8775 // we're now populating the target query with expressions that follow
8776 return target;
8777 } else {
8778 // neighbourhood
8779 var nhoodQ = newQuery();
8780 var node = query;
8781 var neighbor = newQuery();
8782 nhoodQ.checks.push({
8783 type: Type.NODE_NEIGHBOR,
8784 node: node,
8785 neighbor: neighbor
8786 });
8787
8788 // the query in the selector should be the neighbourhood rather than the node
8789 replaceLastQuery(selector, query, nhoodQ);
8790 return neighbor; // now populating the neighbor with following expressions
8791 }
8792 }
8793}, {
8794 name: 'child',
8795 separator: true,
8796 regex: tokens.child,
8797 populate: function populate(selector, query) {
8798 if (selector.currentSubject == null) {
8799 // default: child query
8800 var parentChildQuery = newQuery();
8801 var child = newQuery();
8802 var parent = selector[selector.length - 1];
8803 parentChildQuery.checks.push({
8804 type: Type.CHILD,
8805 parent: parent,
8806 child: child
8807 });
8808
8809 // the query in the selector should be the '>' itself
8810 replaceLastQuery(selector, query, parentChildQuery);
8811 selector.compoundCount++;
8812
8813 // we're now populating the child query with expressions that follow
8814 return child;
8815 } else if (selector.currentSubject === query) {
8816 // compound split query
8817 var compound = newQuery();
8818 var left = selector[selector.length - 1];
8819 var right = newQuery();
8820 var subject = newQuery();
8821 var _child = newQuery();
8822 var _parent = newQuery();
8823
8824 // set up the root compound q
8825 compound.checks.push({
8826 type: Type.COMPOUND_SPLIT,
8827 left: left,
8828 right: right,
8829 subject: subject
8830 });
8831
8832 // populate the subject and replace the q at the old spot (within left) with TRUE
8833 subject.checks = query.checks; // take the checks from the left
8834 query.checks = [{
8835 type: Type.TRUE
8836 }]; // checks under left refs the subject implicitly
8837
8838 // set up the right q
8839 _parent.checks.push({
8840 type: Type.TRUE
8841 }); // parent implicitly refs the subject
8842 right.checks.push({
8843 type: Type.PARENT,
8844 // type is swapped on right side queries
8845 parent: _parent,
8846 child: _child // empty for now
8847 });
8848
8849 replaceLastQuery(selector, left, compound);
8850
8851 // update the ref since we moved things around for `query`
8852 selector.currentSubject = subject;
8853 selector.compoundCount++;
8854 return _child; // now populating the right side's child
8855 } else {
8856 // parent query
8857 // info for parent query
8858 var _parent2 = newQuery();
8859 var _child2 = newQuery();
8860 var pcQChecks = [{
8861 type: Type.PARENT,
8862 parent: _parent2,
8863 child: _child2
8864 }];
8865
8866 // the parent-child query takes the place of the query previously being populated
8867 _parent2.checks = query.checks; // the previous query contains the checks for the parent
8868 query.checks = pcQChecks; // pc query takes over
8869
8870 selector.compoundCount++;
8871 return _child2; // we're now populating the child
8872 }
8873 }
8874}, {
8875 name: 'descendant',
8876 separator: true,
8877 regex: tokens.descendant,
8878 populate: function populate(selector, query) {
8879 if (selector.currentSubject == null) {
8880 // default: descendant query
8881 var ancChQuery = newQuery();
8882 var descendant = newQuery();
8883 var ancestor = selector[selector.length - 1];
8884 ancChQuery.checks.push({
8885 type: Type.DESCENDANT,
8886 ancestor: ancestor,
8887 descendant: descendant
8888 });
8889
8890 // the query in the selector should be the '>' itself
8891 replaceLastQuery(selector, query, ancChQuery);
8892 selector.compoundCount++;
8893
8894 // we're now populating the descendant query with expressions that follow
8895 return descendant;
8896 } else if (selector.currentSubject === query) {
8897 // compound split query
8898 var compound = newQuery();
8899 var left = selector[selector.length - 1];
8900 var right = newQuery();
8901 var subject = newQuery();
8902 var _descendant = newQuery();
8903 var _ancestor = newQuery();
8904
8905 // set up the root compound q
8906 compound.checks.push({
8907 type: Type.COMPOUND_SPLIT,
8908 left: left,
8909 right: right,
8910 subject: subject
8911 });
8912
8913 // populate the subject and replace the q at the old spot (within left) with TRUE
8914 subject.checks = query.checks; // take the checks from the left
8915 query.checks = [{
8916 type: Type.TRUE
8917 }]; // checks under left refs the subject implicitly
8918
8919 // set up the right q
8920 _ancestor.checks.push({
8921 type: Type.TRUE
8922 }); // ancestor implicitly refs the subject
8923 right.checks.push({
8924 type: Type.ANCESTOR,
8925 // type is swapped on right side queries
8926 ancestor: _ancestor,
8927 descendant: _descendant // empty for now
8928 });
8929
8930 replaceLastQuery(selector, left, compound);
8931
8932 // update the ref since we moved things around for `query`
8933 selector.currentSubject = subject;
8934 selector.compoundCount++;
8935 return _descendant; // now populating the right side's descendant
8936 } else {
8937 // ancestor query
8938 // info for parent query
8939 var _ancestor2 = newQuery();
8940 var _descendant2 = newQuery();
8941 var adQChecks = [{
8942 type: Type.ANCESTOR,
8943 ancestor: _ancestor2,
8944 descendant: _descendant2
8945 }];
8946
8947 // the parent-child query takes the place of the query previously being populated
8948 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
8949 query.checks = adQChecks; // pc query takes over
8950
8951 selector.compoundCount++;
8952 return _descendant2; // we're now populating the child
8953 }
8954 }
8955}, {
8956 name: 'subject',
8957 modifier: true,
8958 regex: tokens.subject,
8959 populate: function populate(selector, query) {
8960 if (selector.currentSubject != null && selector.currentSubject !== query) {
8961 warn('Redefinition of subject in selector `' + selector.toString() + '`');
8962 return false;
8963 }
8964 selector.currentSubject = query;
8965 var topQ = selector[selector.length - 1];
8966 var topChk = topQ.checks[0];
8967 var topType = topChk == null ? null : topChk.type;
8968 if (topType === Type.DIRECTED_EDGE) {
8969 // directed edge with subject on the target
8970
8971 // change to target node check
8972 topChk.type = Type.NODE_TARGET;
8973 } else if (topType === Type.UNDIRECTED_EDGE) {
8974 // undirected edge with subject on the second node
8975
8976 // change to neighbor check
8977 topChk.type = Type.NODE_NEIGHBOR;
8978 topChk.node = topChk.nodes[1]; // second node is subject
8979 topChk.neighbor = topChk.nodes[0];
8980
8981 // clean up unused fields for new type
8982 topChk.nodes = null;
8983 }
8984 }
8985}];
8986exprs.forEach(function (e) {
8987 return e.regexObj = new RegExp('^' + e.regex);
8988});
8989
8990/**
8991 * Of all the expressions, find the first match in the remaining text.
8992 * @param {string} remaining The remaining text to parse
8993 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
8994 */
8995var consumeExpr = function consumeExpr(remaining) {
8996 var expr;
8997 var match;
8998 var name;
8999 for (var j = 0; j < exprs.length; j++) {
9000 var e = exprs[j];
9001 var n = e.name;
9002 var m = remaining.match(e.regexObj);
9003 if (m != null) {
9004 match = m;
9005 expr = e;
9006 name = n;
9007 var consumed = m[0];
9008 remaining = remaining.substring(consumed.length);
9009 break; // we've consumed one expr, so we can return now
9010 }
9011 }
9012
9013 return {
9014 expr: expr,
9015 match: match,
9016 name: name,
9017 remaining: remaining
9018 };
9019};
9020
9021/**
9022 * Consume all the leading whitespace
9023 * @param {string} remaining The text to consume
9024 * @returns The text with the leading whitespace removed
9025 */
9026var consumeWhitespace = function consumeWhitespace(remaining) {
9027 var match = remaining.match(/^\s+/);
9028 if (match) {
9029 var consumed = match[0];
9030 remaining = remaining.substring(consumed.length);
9031 }
9032 return remaining;
9033};
9034
9035/**
9036 * Parse the string and store the parsed representation in the Selector.
9037 * @param {string} selector The selector string
9038 * @returns `true` if the selector was successfully parsed, `false` otherwise
9039 */
9040var parse = function parse(selector) {
9041 var self = this;
9042 var remaining = self.inputText = selector;
9043 var currentQuery = self[0] = newQuery();
9044 self.length = 1;
9045 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
9046
9047 for (;;) {
9048 var exprInfo = consumeExpr(remaining);
9049 if (exprInfo.expr == null) {
9050 warn('The selector `' + selector + '`is invalid');
9051 return false;
9052 } else {
9053 var args = exprInfo.match.slice(1);
9054
9055 // let the token populate the selector object in currentQuery
9056 var ret = exprInfo.expr.populate(self, currentQuery, args);
9057 if (ret === false) {
9058 return false; // exit if population failed
9059 } else if (ret != null) {
9060 currentQuery = ret; // change the current query to be filled if the expr specifies
9061 }
9062 }
9063
9064 remaining = exprInfo.remaining;
9065
9066 // we're done when there's nothing left to parse
9067 if (remaining.match(/^\s*$/)) {
9068 break;
9069 }
9070 }
9071 var lastQ = self[self.length - 1];
9072 if (self.currentSubject != null) {
9073 lastQ.subject = self.currentSubject;
9074 }
9075 lastQ.edgeCount = self.edgeCount;
9076 lastQ.compoundCount = self.compoundCount;
9077 for (var i = 0; i < self.length; i++) {
9078 var q = self[i];
9079
9080 // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
9081 if (q.compoundCount > 0 && q.edgeCount > 0) {
9082 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
9083 return false;
9084 }
9085 if (q.edgeCount > 1) {
9086 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
9087 return false;
9088 } else if (q.edgeCount === 1) {
9089 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.');
9090 }
9091 }
9092 return true; // success
9093};
9094
9095/**
9096 * Get the selector represented as a string. This value uses default formatting,
9097 * so things like spacing may differ from the input text passed to the constructor.
9098 * @returns {string} The selector string
9099 */
9100var toString = function toString() {
9101 if (this.toStringCache != null) {
9102 return this.toStringCache;
9103 }
9104 var clean = function clean(obj) {
9105 if (obj == null) {
9106 return '';
9107 } else {
9108 return obj;
9109 }
9110 };
9111 var cleanVal = function cleanVal(val) {
9112 if (string(val)) {
9113 return '"' + val + '"';
9114 } else {
9115 return clean(val);
9116 }
9117 };
9118 var space = function space(val) {
9119 return ' ' + val + ' ';
9120 };
9121 var checkToString = function checkToString(check, subject) {
9122 var type = check.type,
9123 value = check.value;
9124 switch (type) {
9125 case Type.GROUP:
9126 {
9127 var group = clean(value);
9128 return group.substring(0, group.length - 1);
9129 }
9130 case Type.DATA_COMPARE:
9131 {
9132 var field = check.field,
9133 operator = check.operator;
9134 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
9135 }
9136 case Type.DATA_BOOL:
9137 {
9138 var _operator = check.operator,
9139 _field = check.field;
9140 return '[' + clean(_operator) + _field + ']';
9141 }
9142 case Type.DATA_EXIST:
9143 {
9144 var _field2 = check.field;
9145 return '[' + _field2 + ']';
9146 }
9147 case Type.META_COMPARE:
9148 {
9149 var _operator2 = check.operator,
9150 _field3 = check.field;
9151 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
9152 }
9153 case Type.STATE:
9154 {
9155 return value;
9156 }
9157 case Type.ID:
9158 {
9159 return '#' + value;
9160 }
9161 case Type.CLASS:
9162 {
9163 return '.' + value;
9164 }
9165 case Type.PARENT:
9166 case Type.CHILD:
9167 {
9168 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
9169 }
9170 case Type.ANCESTOR:
9171 case Type.DESCENDANT:
9172 {
9173 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
9174 }
9175 case Type.COMPOUND_SPLIT:
9176 {
9177 var lhs = queryToString(check.left, subject);
9178 var sub = queryToString(check.subject, subject);
9179 var rhs = queryToString(check.right, subject);
9180 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
9181 }
9182 case Type.TRUE:
9183 {
9184 return '';
9185 }
9186 }
9187 };
9188 var queryToString = function queryToString(query, subject) {
9189 return query.checks.reduce(function (str, chk, i) {
9190 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
9191 }, '');
9192 };
9193 var str = '';
9194 for (var i = 0; i < this.length; i++) {
9195 var query = this[i];
9196 str += queryToString(query, query.subject);
9197 if (this.length > 1 && i < this.length - 1) {
9198 str += ', ';
9199 }
9200 }
9201 this.toStringCache = str;
9202 return str;
9203};
9204var parse$1 = {
9205 parse: parse,
9206 toString: toString
9207};
9208
9209var valCmp = function valCmp(fieldVal, operator, value) {
9210 var matches;
9211 var isFieldStr = string(fieldVal);
9212 var isFieldNum = number$1(fieldVal);
9213 var isValStr = string(value);
9214 var fieldStr, valStr;
9215 var caseInsensitive = false;
9216 var notExpr = false;
9217 var isIneqCmp = false;
9218 if (operator.indexOf('!') >= 0) {
9219 operator = operator.replace('!', '');
9220 notExpr = true;
9221 }
9222 if (operator.indexOf('@') >= 0) {
9223 operator = operator.replace('@', '');
9224 caseInsensitive = true;
9225 }
9226 if (isFieldStr || isValStr || caseInsensitive) {
9227 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
9228 valStr = '' + value;
9229 }
9230
9231 // if we're doing a case insensitive comparison, then we're using a STRING comparison
9232 // even if we're comparing numbers
9233 if (caseInsensitive) {
9234 fieldVal = fieldStr = fieldStr.toLowerCase();
9235 value = valStr = valStr.toLowerCase();
9236 }
9237 switch (operator) {
9238 case '*=':
9239 matches = fieldStr.indexOf(valStr) >= 0;
9240 break;
9241 case '$=':
9242 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
9243 break;
9244 case '^=':
9245 matches = fieldStr.indexOf(valStr) === 0;
9246 break;
9247 case '=':
9248 matches = fieldVal === value;
9249 break;
9250 case '>':
9251 isIneqCmp = true;
9252 matches = fieldVal > value;
9253 break;
9254 case '>=':
9255 isIneqCmp = true;
9256 matches = fieldVal >= value;
9257 break;
9258 case '<':
9259 isIneqCmp = true;
9260 matches = fieldVal < value;
9261 break;
9262 case '<=':
9263 isIneqCmp = true;
9264 matches = fieldVal <= value;
9265 break;
9266 default:
9267 matches = false;
9268 break;
9269 }
9270
9271 // apply the not op, but null vals for inequalities should always stay non-matching
9272 if (notExpr && (fieldVal != null || !isIneqCmp)) {
9273 matches = !matches;
9274 }
9275 return matches;
9276};
9277var boolCmp = function boolCmp(fieldVal, operator) {
9278 switch (operator) {
9279 case '?':
9280 return fieldVal ? true : false;
9281 case '!':
9282 return fieldVal ? false : true;
9283 case '^':
9284 return fieldVal === undefined;
9285 }
9286};
9287var existCmp = function existCmp(fieldVal) {
9288 return fieldVal !== undefined;
9289};
9290var data$1 = function data(ele, field) {
9291 return ele.data(field);
9292};
9293var meta = function meta(ele, field) {
9294 return ele[field]();
9295};
9296
9297/** A lookup of `match(check, ele)` functions by `Type` int */
9298var match = [];
9299
9300/**
9301 * Returns whether the query matches for the element
9302 * @param query The `{ type, value, ... }` query object
9303 * @param ele The element to compare against
9304*/
9305var matches$1 = function matches(query, ele) {
9306 return query.checks.every(function (chk) {
9307 return match[chk.type](chk, ele);
9308 });
9309};
9310match[Type.GROUP] = function (check, ele) {
9311 var group = check.value;
9312 return group === '*' || group === ele.group();
9313};
9314match[Type.STATE] = function (check, ele) {
9315 var stateSelector = check.value;
9316 return stateSelectorMatches(stateSelector, ele);
9317};
9318match[Type.ID] = function (check, ele) {
9319 var id = check.value;
9320 return ele.id() === id;
9321};
9322match[Type.CLASS] = function (check, ele) {
9323 var cls = check.value;
9324 return ele.hasClass(cls);
9325};
9326match[Type.META_COMPARE] = function (check, ele) {
9327 var field = check.field,
9328 operator = check.operator,
9329 value = check.value;
9330 return valCmp(meta(ele, field), operator, value);
9331};
9332match[Type.DATA_COMPARE] = function (check, ele) {
9333 var field = check.field,
9334 operator = check.operator,
9335 value = check.value;
9336 return valCmp(data$1(ele, field), operator, value);
9337};
9338match[Type.DATA_BOOL] = function (check, ele) {
9339 var field = check.field,
9340 operator = check.operator;
9341 return boolCmp(data$1(ele, field), operator);
9342};
9343match[Type.DATA_EXIST] = function (check, ele) {
9344 var field = check.field;
9345 check.operator;
9346 return existCmp(data$1(ele, field));
9347};
9348match[Type.UNDIRECTED_EDGE] = function (check, ele) {
9349 var qA = check.nodes[0];
9350 var qB = check.nodes[1];
9351 var src = ele.source();
9352 var tgt = ele.target();
9353 return matches$1(qA, src) && matches$1(qB, tgt) || matches$1(qB, src) && matches$1(qA, tgt);
9354};
9355match[Type.NODE_NEIGHBOR] = function (check, ele) {
9356 return matches$1(check.node, ele) && ele.neighborhood().some(function (n) {
9357 return n.isNode() && matches$1(check.neighbor, n);
9358 });
9359};
9360match[Type.DIRECTED_EDGE] = function (check, ele) {
9361 return matches$1(check.source, ele.source()) && matches$1(check.target, ele.target());
9362};
9363match[Type.NODE_SOURCE] = function (check, ele) {
9364 return matches$1(check.source, ele) && ele.outgoers().some(function (n) {
9365 return n.isNode() && matches$1(check.target, n);
9366 });
9367};
9368match[Type.NODE_TARGET] = function (check, ele) {
9369 return matches$1(check.target, ele) && ele.incomers().some(function (n) {
9370 return n.isNode() && matches$1(check.source, n);
9371 });
9372};
9373match[Type.CHILD] = function (check, ele) {
9374 return matches$1(check.child, ele) && matches$1(check.parent, ele.parent());
9375};
9376match[Type.PARENT] = function (check, ele) {
9377 return matches$1(check.parent, ele) && ele.children().some(function (c) {
9378 return matches$1(check.child, c);
9379 });
9380};
9381match[Type.DESCENDANT] = function (check, ele) {
9382 return matches$1(check.descendant, ele) && ele.ancestors().some(function (a) {
9383 return matches$1(check.ancestor, a);
9384 });
9385};
9386match[Type.ANCESTOR] = function (check, ele) {
9387 return matches$1(check.ancestor, ele) && ele.descendants().some(function (d) {
9388 return matches$1(check.descendant, d);
9389 });
9390};
9391match[Type.COMPOUND_SPLIT] = function (check, ele) {
9392 return matches$1(check.subject, ele) && matches$1(check.left, ele) && matches$1(check.right, ele);
9393};
9394match[Type.TRUE] = function () {
9395 return true;
9396};
9397match[Type.COLLECTION] = function (check, ele) {
9398 var collection = check.value;
9399 return collection.has(ele);
9400};
9401match[Type.FILTER] = function (check, ele) {
9402 var filter = check.value;
9403 return filter(ele);
9404};
9405
9406// filter an existing collection
9407var filter = function filter(collection) {
9408 var self = this;
9409
9410 // for 1 id #foo queries, just get the element
9411 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
9412 return collection.getElementById(self[0].checks[0].value).collection();
9413 }
9414 var selectorFunction = function selectorFunction(element) {
9415 for (var j = 0; j < self.length; j++) {
9416 var query = self[j];
9417 if (matches$1(query, element)) {
9418 return true;
9419 }
9420 }
9421 return false;
9422 };
9423 if (self.text() == null) {
9424 selectorFunction = function selectorFunction() {
9425 return true;
9426 };
9427 }
9428 return collection.filter(selectorFunction);
9429}; // filter
9430
9431// does selector match a single element?
9432var matches = function matches(ele) {
9433 var self = this;
9434 for (var j = 0; j < self.length; j++) {
9435 var query = self[j];
9436 if (matches$1(query, ele)) {
9437 return true;
9438 }
9439 }
9440 return false;
9441}; // matches
9442
9443var matching = {
9444 matches: matches,
9445 filter: filter
9446};
9447
9448var Selector = function Selector(selector) {
9449 this.inputText = selector;
9450 this.currentSubject = null;
9451 this.compoundCount = 0;
9452 this.edgeCount = 0;
9453 this.length = 0;
9454 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
9455 this.addQuery({
9456 checks: [{
9457 type: Type.COLLECTION,
9458 value: selector.collection()
9459 }]
9460 });
9461 } else if (fn$6(selector)) {
9462 this.addQuery({
9463 checks: [{
9464 type: Type.FILTER,
9465 value: selector
9466 }]
9467 });
9468 } else if (string(selector)) {
9469 if (!this.parse(selector)) {
9470 this.invalid = true;
9471 }
9472 } else {
9473 error('A selector must be created from a string; found ');
9474 }
9475};
9476var selfn = Selector.prototype;
9477[parse$1, matching].forEach(function (p) {
9478 return extend(selfn, p);
9479});
9480selfn.text = function () {
9481 return this.inputText;
9482};
9483selfn.size = function () {
9484 return this.length;
9485};
9486selfn.eq = function (i) {
9487 return this[i];
9488};
9489selfn.sameText = function (otherSel) {
9490 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
9491};
9492selfn.addQuery = function (q) {
9493 this[this.length++] = q;
9494};
9495selfn.selector = selfn.toString;
9496
9497var elesfn$g = {
9498 allAre: function allAre(selector) {
9499 var selObj = new Selector(selector);
9500 return this.every(function (ele) {
9501 return selObj.matches(ele);
9502 });
9503 },
9504 is: function is(selector) {
9505 var selObj = new Selector(selector);
9506 return this.some(function (ele) {
9507 return selObj.matches(ele);
9508 });
9509 },
9510 some: function some(fn, thisArg) {
9511 for (var i = 0; i < this.length; i++) {
9512 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9513 if (ret) {
9514 return true;
9515 }
9516 }
9517 return false;
9518 },
9519 every: function every(fn, thisArg) {
9520 for (var i = 0; i < this.length; i++) {
9521 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9522 if (!ret) {
9523 return false;
9524 }
9525 }
9526 return true;
9527 },
9528 same: function same(collection) {
9529 // cheap collection ref check
9530 if (this === collection) {
9531 return true;
9532 }
9533 collection = this.cy().collection(collection);
9534 var thisLength = this.length;
9535 var collectionLength = collection.length;
9536
9537 // cheap length check
9538 if (thisLength !== collectionLength) {
9539 return false;
9540 }
9541
9542 // cheap element ref check
9543 if (thisLength === 1) {
9544 return this[0] === collection[0];
9545 }
9546 return this.every(function (ele) {
9547 return collection.hasElementWithId(ele.id());
9548 });
9549 },
9550 anySame: function anySame(collection) {
9551 collection = this.cy().collection(collection);
9552 return this.some(function (ele) {
9553 return collection.hasElementWithId(ele.id());
9554 });
9555 },
9556 allAreNeighbors: function allAreNeighbors(collection) {
9557 collection = this.cy().collection(collection);
9558 var nhood = this.neighborhood();
9559 return collection.every(function (ele) {
9560 return nhood.hasElementWithId(ele.id());
9561 });
9562 },
9563 contains: function contains(collection) {
9564 collection = this.cy().collection(collection);
9565 var self = this;
9566 return collection.every(function (ele) {
9567 return self.hasElementWithId(ele.id());
9568 });
9569 }
9570};
9571elesfn$g.allAreNeighbours = elesfn$g.allAreNeighbors;
9572elesfn$g.has = elesfn$g.contains;
9573elesfn$g.equal = elesfn$g.equals = elesfn$g.same;
9574
9575var cache = function cache(fn, name) {
9576 return function traversalCache(arg1, arg2, arg3, arg4) {
9577 var selectorOrEles = arg1;
9578 var eles = this;
9579 var key;
9580 if (selectorOrEles == null) {
9581 key = '';
9582 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
9583 key = selectorOrEles.id();
9584 }
9585 if (eles.length === 1 && key) {
9586 var _p = eles[0]._private;
9587 var tch = _p.traversalCache = _p.traversalCache || {};
9588 var ch = tch[name] = tch[name] || [];
9589 var hash = hashString(key);
9590 var cacheHit = ch[hash];
9591 if (cacheHit) {
9592 return cacheHit;
9593 } else {
9594 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
9595 }
9596 } else {
9597 return fn.call(eles, arg1, arg2, arg3, arg4);
9598 }
9599 };
9600};
9601
9602var elesfn$f = {
9603 parent: function parent(selector) {
9604 var parents = [];
9605
9606 // optimisation for single ele call
9607 if (this.length === 1) {
9608 var parent = this[0]._private.parent;
9609 if (parent) {
9610 return parent;
9611 }
9612 }
9613 for (var i = 0; i < this.length; i++) {
9614 var ele = this[i];
9615 var _parent = ele._private.parent;
9616 if (_parent) {
9617 parents.push(_parent);
9618 }
9619 }
9620 return this.spawn(parents, true).filter(selector);
9621 },
9622 parents: function parents(selector) {
9623 var parents = [];
9624 var eles = this.parent();
9625 while (eles.nonempty()) {
9626 for (var i = 0; i < eles.length; i++) {
9627 var ele = eles[i];
9628 parents.push(ele);
9629 }
9630 eles = eles.parent();
9631 }
9632 return this.spawn(parents, true).filter(selector);
9633 },
9634 commonAncestors: function commonAncestors(selector) {
9635 var ancestors;
9636 for (var i = 0; i < this.length; i++) {
9637 var ele = this[i];
9638 var parents = ele.parents();
9639 ancestors = ancestors || parents;
9640 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
9641 }
9642
9643 return ancestors.filter(selector);
9644 },
9645 orphans: function orphans(selector) {
9646 return this.stdFilter(function (ele) {
9647 return ele.isOrphan();
9648 }).filter(selector);
9649 },
9650 nonorphans: function nonorphans(selector) {
9651 return this.stdFilter(function (ele) {
9652 return ele.isChild();
9653 }).filter(selector);
9654 },
9655 children: cache(function (selector) {
9656 var children = [];
9657 for (var i = 0; i < this.length; i++) {
9658 var ele = this[i];
9659 var eleChildren = ele._private.children;
9660 for (var j = 0; j < eleChildren.length; j++) {
9661 children.push(eleChildren[j]);
9662 }
9663 }
9664 return this.spawn(children, true).filter(selector);
9665 }, 'children'),
9666 siblings: function siblings(selector) {
9667 return this.parent().children().not(this).filter(selector);
9668 },
9669 isParent: function isParent() {
9670 var ele = this[0];
9671 if (ele) {
9672 return ele.isNode() && ele._private.children.length !== 0;
9673 }
9674 },
9675 isChildless: function isChildless() {
9676 var ele = this[0];
9677 if (ele) {
9678 return ele.isNode() && ele._private.children.length === 0;
9679 }
9680 },
9681 isChild: function isChild() {
9682 var ele = this[0];
9683 if (ele) {
9684 return ele.isNode() && ele._private.parent != null;
9685 }
9686 },
9687 isOrphan: function isOrphan() {
9688 var ele = this[0];
9689 if (ele) {
9690 return ele.isNode() && ele._private.parent == null;
9691 }
9692 },
9693 descendants: function descendants(selector) {
9694 var elements = [];
9695 function add(eles) {
9696 for (var i = 0; i < eles.length; i++) {
9697 var ele = eles[i];
9698 elements.push(ele);
9699 if (ele.children().nonempty()) {
9700 add(ele.children());
9701 }
9702 }
9703 }
9704 add(this.children());
9705 return this.spawn(elements, true).filter(selector);
9706 }
9707};
9708function forEachCompound(eles, fn, includeSelf, recursiveStep) {
9709 var q = [];
9710 var did = new Set$1();
9711 var cy = eles.cy();
9712 var hasCompounds = cy.hasCompoundNodes();
9713 for (var i = 0; i < eles.length; i++) {
9714 var ele = eles[i];
9715 if (includeSelf) {
9716 q.push(ele);
9717 } else if (hasCompounds) {
9718 recursiveStep(q, did, ele);
9719 }
9720 }
9721 while (q.length > 0) {
9722 var _ele = q.shift();
9723 fn(_ele);
9724 did.add(_ele.id());
9725 if (hasCompounds) {
9726 recursiveStep(q, did, _ele);
9727 }
9728 }
9729 return eles;
9730}
9731function addChildren(q, did, ele) {
9732 if (ele.isParent()) {
9733 var children = ele._private.children;
9734 for (var i = 0; i < children.length; i++) {
9735 var child = children[i];
9736 if (!did.has(child.id())) {
9737 q.push(child);
9738 }
9739 }
9740 }
9741}
9742
9743// very efficient version of eles.add( eles.descendants() ).forEach()
9744// for internal use
9745elesfn$f.forEachDown = function (fn) {
9746 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9747 return forEachCompound(this, fn, includeSelf, addChildren);
9748};
9749function addParent(q, did, ele) {
9750 if (ele.isChild()) {
9751 var parent = ele._private.parent;
9752 if (!did.has(parent.id())) {
9753 q.push(parent);
9754 }
9755 }
9756}
9757elesfn$f.forEachUp = function (fn) {
9758 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9759 return forEachCompound(this, fn, includeSelf, addParent);
9760};
9761function addParentAndChildren(q, did, ele) {
9762 addParent(q, did, ele);
9763 addChildren(q, did, ele);
9764}
9765elesfn$f.forEachUpAndDown = function (fn) {
9766 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9767 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
9768};
9769
9770// aliases
9771elesfn$f.ancestors = elesfn$f.parents;
9772
9773var fn$5, elesfn$e;
9774fn$5 = elesfn$e = {
9775 data: define.data({
9776 field: 'data',
9777 bindingEvent: 'data',
9778 allowBinding: true,
9779 allowSetting: true,
9780 settingEvent: 'data',
9781 settingTriggersEvent: true,
9782 triggerFnName: 'trigger',
9783 allowGetting: true,
9784 immutableKeys: {
9785 'id': true,
9786 'source': true,
9787 'target': true,
9788 'parent': true
9789 },
9790 updateStyle: true
9791 }),
9792 removeData: define.removeData({
9793 field: 'data',
9794 event: 'data',
9795 triggerFnName: 'trigger',
9796 triggerEvent: true,
9797 immutableKeys: {
9798 'id': true,
9799 'source': true,
9800 'target': true,
9801 'parent': true
9802 },
9803 updateStyle: true
9804 }),
9805 scratch: define.data({
9806 field: 'scratch',
9807 bindingEvent: 'scratch',
9808 allowBinding: true,
9809 allowSetting: true,
9810 settingEvent: 'scratch',
9811 settingTriggersEvent: true,
9812 triggerFnName: 'trigger',
9813 allowGetting: true,
9814 updateStyle: true
9815 }),
9816 removeScratch: define.removeData({
9817 field: 'scratch',
9818 event: 'scratch',
9819 triggerFnName: 'trigger',
9820 triggerEvent: true,
9821 updateStyle: true
9822 }),
9823 rscratch: define.data({
9824 field: 'rscratch',
9825 allowBinding: false,
9826 allowSetting: true,
9827 settingTriggersEvent: false,
9828 allowGetting: true
9829 }),
9830 removeRscratch: define.removeData({
9831 field: 'rscratch',
9832 triggerEvent: false
9833 }),
9834 id: function id() {
9835 var ele = this[0];
9836 if (ele) {
9837 return ele._private.data.id;
9838 }
9839 }
9840};
9841
9842// aliases
9843fn$5.attr = fn$5.data;
9844fn$5.removeAttr = fn$5.removeData;
9845var data = elesfn$e;
9846
9847var elesfn$d = {};
9848function defineDegreeFunction(callback) {
9849 return function (includeLoops) {
9850 var self = this;
9851 if (includeLoops === undefined) {
9852 includeLoops = true;
9853 }
9854 if (self.length === 0) {
9855 return;
9856 }
9857 if (self.isNode() && !self.removed()) {
9858 var degree = 0;
9859 var node = self[0];
9860 var connectedEdges = node._private.edges;
9861 for (var i = 0; i < connectedEdges.length; i++) {
9862 var edge = connectedEdges[i];
9863 if (!includeLoops && edge.isLoop()) {
9864 continue;
9865 }
9866 degree += callback(node, edge);
9867 }
9868 return degree;
9869 } else {
9870 return;
9871 }
9872 };
9873}
9874extend(elesfn$d, {
9875 degree: defineDegreeFunction(function (node, edge) {
9876 if (edge.source().same(edge.target())) {
9877 return 2;
9878 } else {
9879 return 1;
9880 }
9881 }),
9882 indegree: defineDegreeFunction(function (node, edge) {
9883 if (edge.target().same(node)) {
9884 return 1;
9885 } else {
9886 return 0;
9887 }
9888 }),
9889 outdegree: defineDegreeFunction(function (node, edge) {
9890 if (edge.source().same(node)) {
9891 return 1;
9892 } else {
9893 return 0;
9894 }
9895 })
9896});
9897function defineDegreeBoundsFunction(degreeFn, callback) {
9898 return function (includeLoops) {
9899 var ret;
9900 var nodes = this.nodes();
9901 for (var i = 0; i < nodes.length; i++) {
9902 var ele = nodes[i];
9903 var degree = ele[degreeFn](includeLoops);
9904 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
9905 ret = degree;
9906 }
9907 }
9908 return ret;
9909 };
9910}
9911extend(elesfn$d, {
9912 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
9913 return degree < min;
9914 }),
9915 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
9916 return degree > max;
9917 }),
9918 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
9919 return degree < min;
9920 }),
9921 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
9922 return degree > max;
9923 }),
9924 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
9925 return degree < min;
9926 }),
9927 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
9928 return degree > max;
9929 })
9930});
9931extend(elesfn$d, {
9932 totalDegree: function totalDegree(includeLoops) {
9933 var total = 0;
9934 var nodes = this.nodes();
9935 for (var i = 0; i < nodes.length; i++) {
9936 total += nodes[i].degree(includeLoops);
9937 }
9938 return total;
9939 }
9940});
9941
9942var fn$4, elesfn$c;
9943var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
9944 for (var i = 0; i < eles.length; i++) {
9945 var ele = eles[i];
9946 if (!ele.locked()) {
9947 var oldPos = ele._private.position;
9948 var delta = {
9949 x: newPos.x != null ? newPos.x - oldPos.x : 0,
9950 y: newPos.y != null ? newPos.y - oldPos.y : 0
9951 };
9952 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
9953 ele.children().shift(delta, silent);
9954 }
9955 ele.dirtyBoundingBoxCache();
9956 }
9957 }
9958};
9959var positionDef = {
9960 field: 'position',
9961 bindingEvent: 'position',
9962 allowBinding: true,
9963 allowSetting: true,
9964 settingEvent: 'position',
9965 settingTriggersEvent: true,
9966 triggerFnName: 'emitAndNotify',
9967 allowGetting: true,
9968 validKeys: ['x', 'y'],
9969 beforeGet: function beforeGet(ele) {
9970 ele.updateCompoundBounds();
9971 },
9972 beforeSet: function beforeSet(eles, newPos) {
9973 beforePositionSet(eles, newPos, false);
9974 },
9975 onSet: function onSet(eles) {
9976 eles.dirtyCompoundBoundsCache();
9977 },
9978 canSet: function canSet(ele) {
9979 return !ele.locked();
9980 }
9981};
9982fn$4 = elesfn$c = {
9983 position: define.data(positionDef),
9984 // position but no notification to renderer
9985 silentPosition: define.data(extend({}, positionDef, {
9986 allowBinding: false,
9987 allowSetting: true,
9988 settingTriggersEvent: false,
9989 allowGetting: false,
9990 beforeSet: function beforeSet(eles, newPos) {
9991 beforePositionSet(eles, newPos, true);
9992 },
9993 onSet: function onSet(eles) {
9994 eles.dirtyCompoundBoundsCache();
9995 }
9996 })),
9997 positions: function positions(pos, silent) {
9998 if (plainObject(pos)) {
9999 if (silent) {
10000 this.silentPosition(pos);
10001 } else {
10002 this.position(pos);
10003 }
10004 } else if (fn$6(pos)) {
10005 var _fn = pos;
10006 var cy = this.cy();
10007 cy.startBatch();
10008 for (var i = 0; i < this.length; i++) {
10009 var ele = this[i];
10010 var _pos = void 0;
10011 if (_pos = _fn(ele, i)) {
10012 if (silent) {
10013 ele.silentPosition(_pos);
10014 } else {
10015 ele.position(_pos);
10016 }
10017 }
10018 }
10019 cy.endBatch();
10020 }
10021 return this; // chaining
10022 },
10023
10024 silentPositions: function silentPositions(pos) {
10025 return this.positions(pos, true);
10026 },
10027 shift: function shift(dim, val, silent) {
10028 var delta;
10029 if (plainObject(dim)) {
10030 delta = {
10031 x: number$1(dim.x) ? dim.x : 0,
10032 y: number$1(dim.y) ? dim.y : 0
10033 };
10034 silent = val;
10035 } else if (string(dim) && number$1(val)) {
10036 delta = {
10037 x: 0,
10038 y: 0
10039 };
10040 delta[dim] = val;
10041 }
10042 if (delta != null) {
10043 var cy = this.cy();
10044 cy.startBatch();
10045 for (var i = 0; i < this.length; i++) {
10046 var ele = this[i];
10047
10048 // exclude any node that is a descendant of the calling collection
10049 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
10050 continue;
10051 }
10052 var pos = ele.position();
10053 var newPos = {
10054 x: pos.x + delta.x,
10055 y: pos.y + delta.y
10056 };
10057 if (silent) {
10058 ele.silentPosition(newPos);
10059 } else {
10060 ele.position(newPos);
10061 }
10062 }
10063 cy.endBatch();
10064 }
10065 return this;
10066 },
10067 silentShift: function silentShift(dim, val) {
10068 if (plainObject(dim)) {
10069 this.shift(dim, true);
10070 } else if (string(dim) && number$1(val)) {
10071 this.shift(dim, val, true);
10072 }
10073 return this;
10074 },
10075 // get/set the rendered (i.e. on screen) positon of the element
10076 renderedPosition: function renderedPosition(dim, val) {
10077 var ele = this[0];
10078 var cy = this.cy();
10079 var zoom = cy.zoom();
10080 var pan = cy.pan();
10081 var rpos = plainObject(dim) ? dim : undefined;
10082 var setting = rpos !== undefined || val !== undefined && string(dim);
10083 if (ele && ele.isNode()) {
10084 // must have an element and must be a node to return position
10085 if (setting) {
10086 for (var i = 0; i < this.length; i++) {
10087 var _ele = this[i];
10088 if (val !== undefined) {
10089 // set one dimension
10090 _ele.position(dim, (val - pan[dim]) / zoom);
10091 } else if (rpos !== undefined) {
10092 // set whole position
10093 _ele.position(renderedToModelPosition(rpos, zoom, pan));
10094 }
10095 }
10096 } else {
10097 // getting
10098 var pos = ele.position();
10099 rpos = modelToRenderedPosition(pos, zoom, pan);
10100 if (dim === undefined) {
10101 // then return the whole rendered position
10102 return rpos;
10103 } else {
10104 // then return the specified dimension
10105 return rpos[dim];
10106 }
10107 }
10108 } else if (!setting) {
10109 return undefined; // for empty collection case
10110 }
10111
10112 return this; // chaining
10113 },
10114
10115 // get/set the position relative to the parent
10116 relativePosition: function relativePosition(dim, val) {
10117 var ele = this[0];
10118 var cy = this.cy();
10119 var ppos = plainObject(dim) ? dim : undefined;
10120 var setting = ppos !== undefined || val !== undefined && string(dim);
10121 var hasCompoundNodes = cy.hasCompoundNodes();
10122 if (ele && ele.isNode()) {
10123 // must have an element and must be a node to return position
10124 if (setting) {
10125 for (var i = 0; i < this.length; i++) {
10126 var _ele2 = this[i];
10127 var parent = hasCompoundNodes ? _ele2.parent() : null;
10128 var hasParent = parent && parent.length > 0;
10129 var relativeToParent = hasParent;
10130 if (hasParent) {
10131 parent = parent[0];
10132 }
10133 var origin = relativeToParent ? parent.position() : {
10134 x: 0,
10135 y: 0
10136 };
10137 if (val !== undefined) {
10138 // set one dimension
10139 _ele2.position(dim, val + origin[dim]);
10140 } else if (ppos !== undefined) {
10141 // set whole position
10142 _ele2.position({
10143 x: ppos.x + origin.x,
10144 y: ppos.y + origin.y
10145 });
10146 }
10147 }
10148 } else {
10149 // getting
10150 var pos = ele.position();
10151 var _parent = hasCompoundNodes ? ele.parent() : null;
10152 var _hasParent = _parent && _parent.length > 0;
10153 var _relativeToParent = _hasParent;
10154 if (_hasParent) {
10155 _parent = _parent[0];
10156 }
10157 var _origin = _relativeToParent ? _parent.position() : {
10158 x: 0,
10159 y: 0
10160 };
10161 ppos = {
10162 x: pos.x - _origin.x,
10163 y: pos.y - _origin.y
10164 };
10165 if (dim === undefined) {
10166 // then return the whole rendered position
10167 return ppos;
10168 } else {
10169 // then return the specified dimension
10170 return ppos[dim];
10171 }
10172 }
10173 } else if (!setting) {
10174 return undefined; // for empty collection case
10175 }
10176
10177 return this; // chaining
10178 }
10179};
10180
10181// aliases
10182fn$4.modelPosition = fn$4.point = fn$4.position;
10183fn$4.modelPositions = fn$4.points = fn$4.positions;
10184fn$4.renderedPoint = fn$4.renderedPosition;
10185fn$4.relativePoint = fn$4.relativePosition;
10186var position = elesfn$c;
10187
10188var fn$3, elesfn$b;
10189fn$3 = elesfn$b = {};
10190elesfn$b.renderedBoundingBox = function (options) {
10191 var bb = this.boundingBox(options);
10192 var cy = this.cy();
10193 var zoom = cy.zoom();
10194 var pan = cy.pan();
10195 var x1 = bb.x1 * zoom + pan.x;
10196 var x2 = bb.x2 * zoom + pan.x;
10197 var y1 = bb.y1 * zoom + pan.y;
10198 var y2 = bb.y2 * zoom + pan.y;
10199 return {
10200 x1: x1,
10201 x2: x2,
10202 y1: y1,
10203 y2: y2,
10204 w: x2 - x1,
10205 h: y2 - y1
10206 };
10207};
10208elesfn$b.dirtyCompoundBoundsCache = function () {
10209 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
10210 var cy = this.cy();
10211 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
10212 return this;
10213 }
10214 this.forEachUp(function (ele) {
10215 if (ele.isParent()) {
10216 var _p = ele._private;
10217 _p.compoundBoundsClean = false;
10218 _p.bbCache = null;
10219 if (!silent) {
10220 ele.emitAndNotify('bounds');
10221 }
10222 }
10223 });
10224 return this;
10225};
10226elesfn$b.updateCompoundBounds = function () {
10227 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
10228 var cy = this.cy();
10229
10230 // not possible to do on non-compound graphs or with the style disabled
10231 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
10232 return this;
10233 }
10234
10235 // save cycles when batching -- but bounds will be stale (or not exist yet)
10236 if (!force && cy.batching()) {
10237 return this;
10238 }
10239 function update(parent) {
10240 if (!parent.isParent()) {
10241 return;
10242 }
10243 var _p = parent._private;
10244 var children = parent.children();
10245 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
10246 var min = {
10247 width: {
10248 val: parent.pstyle('min-width').pfValue,
10249 left: parent.pstyle('min-width-bias-left'),
10250 right: parent.pstyle('min-width-bias-right')
10251 },
10252 height: {
10253 val: parent.pstyle('min-height').pfValue,
10254 top: parent.pstyle('min-height-bias-top'),
10255 bottom: parent.pstyle('min-height-bias-bottom')
10256 }
10257 };
10258 var bb = children.boundingBox({
10259 includeLabels: includeLabels,
10260 includeOverlays: false,
10261 // updating the compound bounds happens outside of the regular
10262 // cache cycle (i.e. before fired events)
10263 useCache: false
10264 });
10265 var pos = _p.position;
10266
10267 // if children take up zero area then keep position and fall back on stylesheet w/h
10268 if (bb.w === 0 || bb.h === 0) {
10269 bb = {
10270 w: parent.pstyle('width').pfValue,
10271 h: parent.pstyle('height').pfValue
10272 };
10273 bb.x1 = pos.x - bb.w / 2;
10274 bb.x2 = pos.x + bb.w / 2;
10275 bb.y1 = pos.y - bb.h / 2;
10276 bb.y2 = pos.y + bb.h / 2;
10277 }
10278 function computeBiasValues(propDiff, propBias, propBiasComplement) {
10279 var biasDiff = 0;
10280 var biasComplementDiff = 0;
10281 var biasTotal = propBias + propBiasComplement;
10282 if (propDiff > 0 && biasTotal > 0) {
10283 biasDiff = propBias / biasTotal * propDiff;
10284 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
10285 }
10286 return {
10287 biasDiff: biasDiff,
10288 biasComplementDiff: biasComplementDiff
10289 };
10290 }
10291 function computePaddingValues(width, height, paddingObject, relativeTo) {
10292 // Assuming percentage is number from 0 to 1
10293 if (paddingObject.units === '%') {
10294 switch (relativeTo) {
10295 case 'width':
10296 return width > 0 ? paddingObject.pfValue * width : 0;
10297 case 'height':
10298 return height > 0 ? paddingObject.pfValue * height : 0;
10299 case 'average':
10300 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
10301 case 'min':
10302 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
10303 case 'max':
10304 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
10305 default:
10306 return 0;
10307 }
10308 } else if (paddingObject.units === 'px') {
10309 return paddingObject.pfValue;
10310 } else {
10311 return 0;
10312 }
10313 }
10314 var leftVal = min.width.left.value;
10315 if (min.width.left.units === 'px' && min.width.val > 0) {
10316 leftVal = leftVal * 100 / min.width.val;
10317 }
10318 var rightVal = min.width.right.value;
10319 if (min.width.right.units === 'px' && min.width.val > 0) {
10320 rightVal = rightVal * 100 / min.width.val;
10321 }
10322 var topVal = min.height.top.value;
10323 if (min.height.top.units === 'px' && min.height.val > 0) {
10324 topVal = topVal * 100 / min.height.val;
10325 }
10326 var bottomVal = min.height.bottom.value;
10327 if (min.height.bottom.units === 'px' && min.height.val > 0) {
10328 bottomVal = bottomVal * 100 / min.height.val;
10329 }
10330 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
10331 var diffLeft = widthBiasDiffs.biasDiff;
10332 var diffRight = widthBiasDiffs.biasComplementDiff;
10333 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
10334 var diffTop = heightBiasDiffs.biasDiff;
10335 var diffBottom = heightBiasDiffs.biasComplementDiff;
10336 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
10337 _p.autoWidth = Math.max(bb.w, min.width.val);
10338 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
10339 _p.autoHeight = Math.max(bb.h, min.height.val);
10340 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
10341 }
10342 for (var i = 0; i < this.length; i++) {
10343 var ele = this[i];
10344 var _p = ele._private;
10345 if (!_p.compoundBoundsClean || force) {
10346 update(ele);
10347 if (!cy.batching()) {
10348 _p.compoundBoundsClean = true;
10349 }
10350 }
10351 }
10352 return this;
10353};
10354var noninf = function noninf(x) {
10355 if (x === Infinity || x === -Infinity) {
10356 return 0;
10357 }
10358 return x;
10359};
10360var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
10361 // don't update with zero area boxes
10362 if (x2 - x1 === 0 || y2 - y1 === 0) {
10363 return;
10364 }
10365
10366 // don't update with null dim
10367 if (x1 == null || y1 == null || x2 == null || y2 == null) {
10368 return;
10369 }
10370 b.x1 = x1 < b.x1 ? x1 : b.x1;
10371 b.x2 = x2 > b.x2 ? x2 : b.x2;
10372 b.y1 = y1 < b.y1 ? y1 : b.y1;
10373 b.y2 = y2 > b.y2 ? y2 : b.y2;
10374 b.w = b.x2 - b.x1;
10375 b.h = b.y2 - b.y1;
10376};
10377var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
10378 if (b2 == null) {
10379 return b;
10380 }
10381 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
10382};
10383var prefixedProperty = function prefixedProperty(obj, field, prefix) {
10384 return getPrefixedProperty(obj, field, prefix);
10385};
10386var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
10387 if (ele.cy().headless()) {
10388 return;
10389 }
10390 var _p = ele._private;
10391 var rstyle = _p.rstyle;
10392 var halfArW = rstyle.arrowWidth / 2;
10393 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
10394 var x;
10395 var y;
10396 if (arrowType !== 'none') {
10397 if (prefix === 'source') {
10398 x = rstyle.srcX;
10399 y = rstyle.srcY;
10400 } else if (prefix === 'target') {
10401 x = rstyle.tgtX;
10402 y = rstyle.tgtY;
10403 } else {
10404 x = rstyle.midX;
10405 y = rstyle.midY;
10406 }
10407
10408 // always store the individual arrow bounds
10409 var bbs = _p.arrowBounds = _p.arrowBounds || {};
10410 var bb = bbs[prefix] = bbs[prefix] || {};
10411 bb.x1 = x - halfArW;
10412 bb.y1 = y - halfArW;
10413 bb.x2 = x + halfArW;
10414 bb.y2 = y + halfArW;
10415 bb.w = bb.x2 - bb.x1;
10416 bb.h = bb.y2 - bb.y1;
10417 expandBoundingBox(bb, 1);
10418 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
10419 }
10420};
10421var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
10422 if (ele.cy().headless()) {
10423 return;
10424 }
10425 var prefixDash;
10426 if (prefix) {
10427 prefixDash = prefix + '-';
10428 } else {
10429 prefixDash = '';
10430 }
10431 var _p = ele._private;
10432 var rstyle = _p.rstyle;
10433 var label = ele.pstyle(prefixDash + 'label').strValue;
10434 if (label) {
10435 var halign = ele.pstyle('text-halign');
10436 var valign = ele.pstyle('text-valign');
10437 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
10438 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
10439 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
10440 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
10441 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
10442 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
10443 var isEdge = ele.isEdge();
10444 var rotation = ele.pstyle(prefixDash + 'text-rotation');
10445 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
10446 var borderWidth = ele.pstyle('text-border-width').pfValue;
10447 var halfBorderWidth = borderWidth / 2;
10448 var padding = ele.pstyle('text-background-padding').pfValue;
10449 var marginOfError = 2; // expand to work around browser dimension inaccuracies
10450
10451 var lh = labelHeight;
10452 var lw = labelWidth;
10453 var lw_2 = lw / 2;
10454 var lh_2 = lh / 2;
10455 var lx1, lx2, ly1, ly2;
10456 if (isEdge) {
10457 lx1 = labelX - lw_2;
10458 lx2 = labelX + lw_2;
10459 ly1 = labelY - lh_2;
10460 ly2 = labelY + lh_2;
10461 } else {
10462 switch (halign.value) {
10463 case 'left':
10464 lx1 = labelX - lw;
10465 lx2 = labelX;
10466 break;
10467 case 'center':
10468 lx1 = labelX - lw_2;
10469 lx2 = labelX + lw_2;
10470 break;
10471 case 'right':
10472 lx1 = labelX;
10473 lx2 = labelX + lw;
10474 break;
10475 }
10476 switch (valign.value) {
10477 case 'top':
10478 ly1 = labelY - lh;
10479 ly2 = labelY;
10480 break;
10481 case 'center':
10482 ly1 = labelY - lh_2;
10483 ly2 = labelY + lh_2;
10484 break;
10485 case 'bottom':
10486 ly1 = labelY;
10487 ly2 = labelY + lh;
10488 break;
10489 }
10490 }
10491
10492 // shift by margin and expand by outline and border
10493 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10494 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10495 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10496 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10497
10498 // always store the unrotated label bounds separately
10499 var bbPrefix = prefix || 'main';
10500 var bbs = _p.labelBounds;
10501 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
10502 bb.x1 = lx1;
10503 bb.y1 = ly1;
10504 bb.x2 = lx2;
10505 bb.y2 = ly2;
10506 bb.w = lx2 - lx1;
10507 bb.h = ly2 - ly1;
10508 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
10509 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
10510 if (isAutorotate || isPfValue) {
10511 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
10512 var cos = Math.cos(theta);
10513 var sin = Math.sin(theta);
10514
10515 // rotation point (default value for center-center)
10516 var xo = (lx1 + lx2) / 2;
10517 var yo = (ly1 + ly2) / 2;
10518 if (!isEdge) {
10519 switch (halign.value) {
10520 case 'left':
10521 xo = lx2;
10522 break;
10523 case 'right':
10524 xo = lx1;
10525 break;
10526 }
10527 switch (valign.value) {
10528 case 'top':
10529 yo = ly2;
10530 break;
10531 case 'bottom':
10532 yo = ly1;
10533 break;
10534 }
10535 }
10536 var rotate = function rotate(x, y) {
10537 x = x - xo;
10538 y = y - yo;
10539 return {
10540 x: x * cos - y * sin + xo,
10541 y: x * sin + y * cos + yo
10542 };
10543 };
10544 var px1y1 = rotate(lx1, ly1);
10545 var px1y2 = rotate(lx1, ly2);
10546 var px2y1 = rotate(lx2, ly1);
10547 var px2y2 = rotate(lx2, ly2);
10548 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10549 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10550 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10551 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10552 }
10553 var bbPrefixRot = bbPrefix + 'Rot';
10554 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
10555 bbRot.x1 = lx1;
10556 bbRot.y1 = ly1;
10557 bbRot.x2 = lx2;
10558 bbRot.y2 = ly2;
10559 bbRot.w = lx2 - lx1;
10560 bbRot.h = ly2 - ly1;
10561 updateBounds(bounds, lx1, ly1, lx2, ly2);
10562 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
10563 }
10564 return bounds;
10565};
10566var updateBoundsFromOutline = function updateBoundsFromOutline(bounds, ele) {
10567 if (ele.cy().headless()) {
10568 return;
10569 }
10570 var outlineOpacity = ele.pstyle('outline-opacity').value;
10571 var outlineWidth = ele.pstyle('outline-width').value;
10572 if (outlineOpacity > 0 && outlineWidth > 0) {
10573 var outlineOffset = ele.pstyle('outline-offset').value;
10574 var nodeShape = ele.pstyle('shape').value;
10575 var outlineSize = outlineWidth + outlineOffset;
10576 var scaleX = (bounds.w + outlineSize * 2) / bounds.w;
10577 var scaleY = (bounds.h + outlineSize * 2) / bounds.h;
10578 var xOffset = 0;
10579 var yOffset = 0;
10580 if (["diamond", "pentagon", "round-triangle"].includes(nodeShape)) {
10581 scaleX = (bounds.w + outlineSize * 2.4) / bounds.w;
10582 yOffset = -outlineSize / 3.6;
10583 } else if (["concave-hexagon", "rhomboid", "right-rhomboid"].includes(nodeShape)) {
10584 scaleX = (bounds.w + outlineSize * 2.4) / bounds.w;
10585 } else if (nodeShape === "star") {
10586 scaleX = (bounds.w + outlineSize * 2.8) / bounds.w;
10587 scaleY = (bounds.h + outlineSize * 2.6) / bounds.h;
10588 yOffset = -outlineSize / 3.8;
10589 } else if (nodeShape === "triangle") {
10590 scaleX = (bounds.w + outlineSize * 2.8) / bounds.w;
10591 scaleY = (bounds.h + outlineSize * 2.4) / bounds.h;
10592 yOffset = -outlineSize / 1.4;
10593 } else if (nodeShape === "vee") {
10594 scaleX = (bounds.w + outlineSize * 4.4) / bounds.w;
10595 scaleY = (bounds.h + outlineSize * 3.8) / bounds.h;
10596 yOffset = -outlineSize * .5;
10597 }
10598 var hDelta = bounds.h * scaleY - bounds.h;
10599 var wDelta = bounds.w * scaleX - bounds.w;
10600 expandBoundingBoxSides(bounds, [Math.ceil(hDelta / 2), Math.ceil(wDelta / 2)]);
10601 if (xOffset != 0 || yOffset !== 0) {
10602 var oBounds = shiftBoundingBox(bounds, xOffset, yOffset);
10603 updateBoundingBox(bounds, oBounds);
10604 }
10605 }
10606};
10607
10608// get the bounding box of the elements (in raw model position)
10609var boundingBoxImpl = function boundingBoxImpl(ele, options) {
10610 var cy = ele._private.cy;
10611 var styleEnabled = cy.styleEnabled();
10612 var headless = cy.headless();
10613 var bounds = makeBoundingBox();
10614 var _p = ele._private;
10615 var isNode = ele.isNode();
10616 var isEdge = ele.isEdge();
10617 var ex1, ex2, ey1, ey2; // extrema of body / lines
10618 var x, y; // node pos
10619 var rstyle = _p.rstyle;
10620 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0];
10621
10622 // must use `display` prop only, as reading `compound.width()` causes recursion
10623 // (other factors like width values will be considered later in this function anyway)
10624 var isDisplayed = function isDisplayed(ele) {
10625 return ele.pstyle('display').value !== 'none';
10626 };
10627 var displayed = !styleEnabled || isDisplayed(ele)
10628
10629 // must take into account connected nodes b/c of implicit edge hiding on display:none node
10630 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
10631 if (displayed) {
10632 // displayed suffices, since we will find zero area eles anyway
10633 var overlayOpacity = 0;
10634 var overlayPadding = 0;
10635 if (styleEnabled && options.includeOverlays) {
10636 overlayOpacity = ele.pstyle('overlay-opacity').value;
10637 if (overlayOpacity !== 0) {
10638 overlayPadding = ele.pstyle('overlay-padding').value;
10639 }
10640 }
10641 var underlayOpacity = 0;
10642 var underlayPadding = 0;
10643 if (styleEnabled && options.includeUnderlays) {
10644 underlayOpacity = ele.pstyle('underlay-opacity').value;
10645 if (underlayOpacity !== 0) {
10646 underlayPadding = ele.pstyle('underlay-padding').value;
10647 }
10648 }
10649 var padding = Math.max(overlayPadding, underlayPadding);
10650 var w = 0;
10651 var wHalf = 0;
10652 if (styleEnabled) {
10653 w = ele.pstyle('width').pfValue;
10654 wHalf = w / 2;
10655 }
10656 if (isNode && options.includeNodes) {
10657 var pos = ele.position();
10658 x = pos.x;
10659 y = pos.y;
10660 var _w = ele.outerWidth();
10661 var halfW = _w / 2;
10662 var h = ele.outerHeight();
10663 var halfH = h / 2;
10664
10665 // handle node dimensions
10666 /////////////////////////
10667
10668 ex1 = x - halfW;
10669 ex2 = x + halfW;
10670 ey1 = y - halfH;
10671 ey2 = y + halfH;
10672 updateBounds(bounds, ex1, ey1, ex2, ey2);
10673 if (styleEnabled && options.includeOutlines) {
10674 updateBoundsFromOutline(bounds, ele);
10675 }
10676 } else if (isEdge && options.includeEdges) {
10677 if (styleEnabled && !headless) {
10678 var curveStyle = ele.pstyle('curve-style').strValue;
10679
10680 // handle edge dimensions (rough box estimate)
10681 //////////////////////////////////////////////
10682
10683 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10684 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10685 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10686 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10687
10688 // take into account edge width
10689 ex1 -= wHalf;
10690 ex2 += wHalf;
10691 ey1 -= wHalf;
10692 ey2 += wHalf;
10693 updateBounds(bounds, ex1, ey1, ex2, ey2);
10694
10695 // precise edges
10696 ////////////////
10697
10698 if (curveStyle === 'haystack') {
10699 var hpts = rstyle.haystackPts;
10700 if (hpts && hpts.length === 2) {
10701 ex1 = hpts[0].x;
10702 ey1 = hpts[0].y;
10703 ex2 = hpts[1].x;
10704 ey2 = hpts[1].y;
10705 if (ex1 > ex2) {
10706 var temp = ex1;
10707 ex1 = ex2;
10708 ex2 = temp;
10709 }
10710 if (ey1 > ey2) {
10711 var _temp = ey1;
10712 ey1 = ey2;
10713 ey2 = _temp;
10714 }
10715 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
10716 }
10717 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle.endsWith('segments') || curveStyle.endsWith('taxi')) {
10718 var pts;
10719 switch (curveStyle) {
10720 case 'bezier':
10721 case 'unbundled-bezier':
10722 pts = rstyle.bezierPts;
10723 break;
10724 case 'segments':
10725 case 'taxi':
10726 case 'round-segments':
10727 case 'round-taxi':
10728 pts = rstyle.linePts;
10729 break;
10730 }
10731 if (pts != null) {
10732 for (var j = 0; j < pts.length; j++) {
10733 var pt = pts[j];
10734 ex1 = pt.x - wHalf;
10735 ex2 = pt.x + wHalf;
10736 ey1 = pt.y - wHalf;
10737 ey2 = pt.y + wHalf;
10738 updateBounds(bounds, ex1, ey1, ex2, ey2);
10739 }
10740 }
10741 } // bezier-like or segment-like edge
10742 } else {
10743 // headless or style disabled
10744
10745 // fallback on source and target positions
10746 //////////////////////////////////////////
10747
10748 var n1 = ele.source();
10749 var n1pos = n1.position();
10750 var n2 = ele.target();
10751 var n2pos = n2.position();
10752 ex1 = n1pos.x;
10753 ex2 = n2pos.x;
10754 ey1 = n1pos.y;
10755 ey2 = n2pos.y;
10756 if (ex1 > ex2) {
10757 var _temp2 = ex1;
10758 ex1 = ex2;
10759 ex2 = _temp2;
10760 }
10761 if (ey1 > ey2) {
10762 var _temp3 = ey1;
10763 ey1 = ey2;
10764 ey2 = _temp3;
10765 }
10766
10767 // take into account edge width
10768 ex1 -= wHalf;
10769 ex2 += wHalf;
10770 ey1 -= wHalf;
10771 ey2 += wHalf;
10772 updateBounds(bounds, ex1, ey1, ex2, ey2);
10773 } // headless or style disabled
10774 } // edges
10775
10776 // handle edge arrow size
10777 /////////////////////////
10778
10779 if (styleEnabled && options.includeEdges && isEdge) {
10780 updateBoundsFromArrow(bounds, ele, 'mid-source');
10781 updateBoundsFromArrow(bounds, ele, 'mid-target');
10782 updateBoundsFromArrow(bounds, ele, 'source');
10783 updateBoundsFromArrow(bounds, ele, 'target');
10784 }
10785
10786 // ghost
10787 ////////
10788
10789 if (styleEnabled) {
10790 var ghost = ele.pstyle('ghost').value === 'yes';
10791 if (ghost) {
10792 var gx = ele.pstyle('ghost-offset-x').pfValue;
10793 var gy = ele.pstyle('ghost-offset-y').pfValue;
10794 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
10795 }
10796 }
10797
10798 // always store the body bounds separately from the labels
10799 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
10800 assignBoundingBox(bbBody, bounds);
10801 expandBoundingBoxSides(bbBody, manualExpansion);
10802 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
10803
10804 // overlay
10805 //////////
10806
10807 if (styleEnabled) {
10808 ex1 = bounds.x1;
10809 ex2 = bounds.x2;
10810 ey1 = bounds.y1;
10811 ey2 = bounds.y2;
10812 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
10813 }
10814
10815 // always store the body bounds separately from the labels
10816 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
10817 assignBoundingBox(bbOverlay, bounds);
10818 expandBoundingBoxSides(bbOverlay, manualExpansion);
10819 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
10820
10821 // handle label dimensions
10822 //////////////////////////
10823
10824 var bbLabels = _p.labelBounds = _p.labelBounds || {};
10825 if (bbLabels.all != null) {
10826 clearBoundingBox(bbLabels.all);
10827 } else {
10828 bbLabels.all = makeBoundingBox();
10829 }
10830 if (styleEnabled && options.includeLabels) {
10831 if (options.includeMainLabels) {
10832 updateBoundsFromLabel(bounds, ele, null);
10833 }
10834 if (isEdge) {
10835 if (options.includeSourceLabels) {
10836 updateBoundsFromLabel(bounds, ele, 'source');
10837 }
10838 if (options.includeTargetLabels) {
10839 updateBoundsFromLabel(bounds, ele, 'target');
10840 }
10841 }
10842 } // style enabled for labels
10843 } // if displayed
10844
10845 bounds.x1 = noninf(bounds.x1);
10846 bounds.y1 = noninf(bounds.y1);
10847 bounds.x2 = noninf(bounds.x2);
10848 bounds.y2 = noninf(bounds.y2);
10849 bounds.w = noninf(bounds.x2 - bounds.x1);
10850 bounds.h = noninf(bounds.y2 - bounds.y1);
10851 if (bounds.w > 0 && bounds.h > 0 && displayed) {
10852 expandBoundingBoxSides(bounds, manualExpansion);
10853
10854 // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
10855 expandBoundingBox(bounds, 1);
10856 }
10857 return bounds;
10858};
10859var getKey = function getKey(opts) {
10860 var i = 0;
10861 var tf = function tf(val) {
10862 return (val ? 1 : 0) << i++;
10863 };
10864 var key = 0;
10865 key += tf(opts.incudeNodes);
10866 key += tf(opts.includeEdges);
10867 key += tf(opts.includeLabels);
10868 key += tf(opts.includeMainLabels);
10869 key += tf(opts.includeSourceLabels);
10870 key += tf(opts.includeTargetLabels);
10871 key += tf(opts.includeOverlays);
10872 key += tf(opts.includeOutlines);
10873 return key;
10874};
10875var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
10876 if (ele.isEdge()) {
10877 var p1 = ele.source().position();
10878 var p2 = ele.target().position();
10879 var r = function r(x) {
10880 return Math.round(x);
10881 };
10882 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
10883 } else {
10884 return 0;
10885 }
10886};
10887var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
10888 var _p = ele._private;
10889 var bb;
10890 var isEdge = ele.isEdge();
10891 var key = opts == null ? defBbOptsKey : getKey(opts);
10892 var usingDefOpts = key === defBbOptsKey;
10893 var currPosKey = getBoundingBoxPosKey(ele);
10894 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10895 var useCache = opts.useCache && isPosKeySame;
10896 var isDirty = function isDirty(ele) {
10897 return ele._private.bbCache == null || ele._private.styleDirty;
10898 };
10899 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
10900 if (needRecalc) {
10901 if (!isPosKeySame) {
10902 ele.recalculateRenderedStyle(useCache);
10903 }
10904 bb = boundingBoxImpl(ele, defBbOpts);
10905 _p.bbCache = bb;
10906 _p.bbCachePosKey = currPosKey;
10907 } else {
10908 bb = _p.bbCache;
10909 }
10910
10911 // not using def opts => need to build up bb from combination of sub bbs
10912 if (!usingDefOpts) {
10913 var isNode = ele.isNode();
10914 bb = makeBoundingBox();
10915 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
10916 if (opts.includeOverlays) {
10917 updateBoundsFromBox(bb, _p.overlayBounds);
10918 } else {
10919 updateBoundsFromBox(bb, _p.bodyBounds);
10920 }
10921 }
10922 if (opts.includeLabels) {
10923 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
10924 updateBoundsFromBox(bb, _p.labelBounds.all);
10925 } else {
10926 if (opts.includeMainLabels) {
10927 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
10928 }
10929 if (opts.includeSourceLabels) {
10930 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
10931 }
10932 if (opts.includeTargetLabels) {
10933 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
10934 }
10935 }
10936 }
10937 bb.w = bb.x2 - bb.x1;
10938 bb.h = bb.y2 - bb.y1;
10939 }
10940 return bb;
10941};
10942var defBbOpts = {
10943 includeNodes: true,
10944 includeEdges: true,
10945 includeLabels: true,
10946 includeMainLabels: true,
10947 includeSourceLabels: true,
10948 includeTargetLabels: true,
10949 includeOverlays: true,
10950 includeUnderlays: true,
10951 includeOutlines: true,
10952 useCache: true
10953};
10954var defBbOptsKey = getKey(defBbOpts);
10955var filledBbOpts = defaults$g(defBbOpts);
10956elesfn$b.boundingBox = function (options) {
10957 var bounds;
10958
10959 // the main usecase is ele.boundingBox() for a single element with no/def options
10960 // specified s.t. the cache is used, so check for this case to make it faster by
10961 // avoiding the overhead of the rest of the function
10962 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
10963 if (options === undefined) {
10964 options = defBbOpts;
10965 } else {
10966 options = filledBbOpts(options);
10967 }
10968 bounds = cachedBoundingBoxImpl(this[0], options);
10969 } else {
10970 bounds = makeBoundingBox();
10971 options = options || defBbOpts;
10972 var opts = filledBbOpts(options);
10973 var eles = this;
10974 var cy = eles.cy();
10975 var styleEnabled = cy.styleEnabled();
10976 if (styleEnabled) {
10977 for (var i = 0; i < eles.length; i++) {
10978 var ele = eles[i];
10979 var _p = ele._private;
10980 var currPosKey = getBoundingBoxPosKey(ele);
10981 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10982 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
10983 ele.recalculateRenderedStyle(useCache);
10984 }
10985 }
10986 this.updateCompoundBounds(!options.useCache);
10987 for (var _i = 0; _i < eles.length; _i++) {
10988 var _ele = eles[_i];
10989 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
10990 }
10991 }
10992 bounds.x1 = noninf(bounds.x1);
10993 bounds.y1 = noninf(bounds.y1);
10994 bounds.x2 = noninf(bounds.x2);
10995 bounds.y2 = noninf(bounds.y2);
10996 bounds.w = noninf(bounds.x2 - bounds.x1);
10997 bounds.h = noninf(bounds.y2 - bounds.y1);
10998 return bounds;
10999};
11000elesfn$b.dirtyBoundingBoxCache = function () {
11001 for (var i = 0; i < this.length; i++) {
11002 var _p = this[i]._private;
11003 _p.bbCache = null;
11004 _p.bbCachePosKey = null;
11005 _p.bodyBounds = null;
11006 _p.overlayBounds = null;
11007 _p.labelBounds.all = null;
11008 _p.labelBounds.source = null;
11009 _p.labelBounds.target = null;
11010 _p.labelBounds.main = null;
11011 _p.labelBounds.sourceRot = null;
11012 _p.labelBounds.targetRot = null;
11013 _p.labelBounds.mainRot = null;
11014 _p.arrowBounds.source = null;
11015 _p.arrowBounds.target = null;
11016 _p.arrowBounds['mid-source'] = null;
11017 _p.arrowBounds['mid-target'] = null;
11018 }
11019 this.emitAndNotify('bounds');
11020 return this;
11021};
11022
11023// private helper to get bounding box for custom node positions
11024// - good for perf in certain cases but currently requires dirtying the rendered style
11025// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
11026// - try to use for only things like discrete layouts where the node position would change anyway
11027elesfn$b.boundingBoxAt = function (fn) {
11028 var nodes = this.nodes();
11029 var cy = this.cy();
11030 var hasCompoundNodes = cy.hasCompoundNodes();
11031 var parents = cy.collection();
11032 if (hasCompoundNodes) {
11033 parents = nodes.filter(function (node) {
11034 return node.isParent();
11035 });
11036 nodes = nodes.not(parents);
11037 }
11038 if (plainObject(fn)) {
11039 var obj = fn;
11040 fn = function fn() {
11041 return obj;
11042 };
11043 }
11044 var storeOldPos = function storeOldPos(node, i) {
11045 return node._private.bbAtOldPos = fn(node, i);
11046 };
11047 var getOldPos = function getOldPos(node) {
11048 return node._private.bbAtOldPos;
11049 };
11050 cy.startBatch();
11051 nodes.forEach(storeOldPos).silentPositions(fn);
11052 if (hasCompoundNodes) {
11053 parents.dirtyCompoundBoundsCache();
11054 parents.dirtyBoundingBoxCache();
11055 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
11056 }
11057
11058 var bb = copyBoundingBox(this.boundingBox({
11059 useCache: false
11060 }));
11061 nodes.silentPositions(getOldPos);
11062 if (hasCompoundNodes) {
11063 parents.dirtyCompoundBoundsCache();
11064 parents.dirtyBoundingBoxCache();
11065 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
11066 }
11067
11068 cy.endBatch();
11069 return bb;
11070};
11071fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
11072fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
11073var bounds = elesfn$b;
11074
11075var fn$2, elesfn$a;
11076fn$2 = elesfn$a = {};
11077var defineDimFns = function defineDimFns(opts) {
11078 opts.uppercaseName = capitalize(opts.name);
11079 opts.autoName = 'auto' + opts.uppercaseName;
11080 opts.labelName = 'label' + opts.uppercaseName;
11081 opts.outerName = 'outer' + opts.uppercaseName;
11082 opts.uppercaseOuterName = capitalize(opts.outerName);
11083 fn$2[opts.name] = function dimImpl() {
11084 var ele = this[0];
11085 var _p = ele._private;
11086 var cy = _p.cy;
11087 var styleEnabled = cy._private.styleEnabled;
11088 if (ele) {
11089 if (styleEnabled) {
11090 if (ele.isParent()) {
11091 ele.updateCompoundBounds();
11092 return _p[opts.autoName] || 0;
11093 }
11094 var d = ele.pstyle(opts.name);
11095 switch (d.strValue) {
11096 case 'label':
11097 ele.recalculateRenderedStyle();
11098 return _p.rstyle[opts.labelName] || 0;
11099 default:
11100 return d.pfValue;
11101 }
11102 } else {
11103 return 1;
11104 }
11105 }
11106 };
11107 fn$2['outer' + opts.uppercaseName] = function outerDimImpl() {
11108 var ele = this[0];
11109 var _p = ele._private;
11110 var cy = _p.cy;
11111 var styleEnabled = cy._private.styleEnabled;
11112 if (ele) {
11113 if (styleEnabled) {
11114 var dim = ele[opts.name]();
11115 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
11116 var padding = 2 * ele.padding();
11117 return dim + border + padding;
11118 } else {
11119 return 1;
11120 }
11121 }
11122 };
11123 fn$2['rendered' + opts.uppercaseName] = function renderedDimImpl() {
11124 var ele = this[0];
11125 if (ele) {
11126 var d = ele[opts.name]();
11127 return d * this.cy().zoom();
11128 }
11129 };
11130 fn$2['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
11131 var ele = this[0];
11132 if (ele) {
11133 var od = ele[opts.outerName]();
11134 return od * this.cy().zoom();
11135 }
11136 };
11137};
11138defineDimFns({
11139 name: 'width'
11140});
11141defineDimFns({
11142 name: 'height'
11143});
11144elesfn$a.padding = function () {
11145 var ele = this[0];
11146 var _p = ele._private;
11147 if (ele.isParent()) {
11148 ele.updateCompoundBounds();
11149 if (_p.autoPadding !== undefined) {
11150 return _p.autoPadding;
11151 } else {
11152 return ele.pstyle('padding').pfValue;
11153 }
11154 } else {
11155 return ele.pstyle('padding').pfValue;
11156 }
11157};
11158elesfn$a.paddedHeight = function () {
11159 var ele = this[0];
11160 return ele.height() + 2 * ele.padding();
11161};
11162elesfn$a.paddedWidth = function () {
11163 var ele = this[0];
11164 return ele.width() + 2 * ele.padding();
11165};
11166var widthHeight = elesfn$a;
11167
11168var ifEdge = function ifEdge(ele, getValue) {
11169 if (ele.isEdge()) {
11170 return getValue(ele);
11171 }
11172};
11173var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
11174 if (ele.isEdge()) {
11175 var cy = ele.cy();
11176 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
11177 }
11178};
11179var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
11180 if (ele.isEdge()) {
11181 var cy = ele.cy();
11182 var pan = cy.pan();
11183 var zoom = cy.zoom();
11184 return getPoints(ele).map(function (p) {
11185 return modelToRenderedPosition(p, zoom, pan);
11186 });
11187 }
11188};
11189var controlPoints = function controlPoints(ele) {
11190 return ele.renderer().getControlPoints(ele);
11191};
11192var segmentPoints = function segmentPoints(ele) {
11193 return ele.renderer().getSegmentPoints(ele);
11194};
11195var sourceEndpoint = function sourceEndpoint(ele) {
11196 return ele.renderer().getSourceEndpoint(ele);
11197};
11198var targetEndpoint = function targetEndpoint(ele) {
11199 return ele.renderer().getTargetEndpoint(ele);
11200};
11201var midpoint = function midpoint(ele) {
11202 return ele.renderer().getEdgeMidpoint(ele);
11203};
11204var pts = {
11205 controlPoints: {
11206 get: controlPoints,
11207 mult: true
11208 },
11209 segmentPoints: {
11210 get: segmentPoints,
11211 mult: true
11212 },
11213 sourceEndpoint: {
11214 get: sourceEndpoint
11215 },
11216 targetEndpoint: {
11217 get: targetEndpoint
11218 },
11219 midpoint: {
11220 get: midpoint
11221 }
11222};
11223var renderedName = function renderedName(name) {
11224 return 'rendered' + name[0].toUpperCase() + name.substr(1);
11225};
11226var edgePoints = Object.keys(pts).reduce(function (obj, name) {
11227 var spec = pts[name];
11228 var rName = renderedName(name);
11229 obj[name] = function () {
11230 return ifEdge(this, spec.get);
11231 };
11232 if (spec.mult) {
11233 obj[rName] = function () {
11234 return ifEdgeRenderedPositions(this, spec.get);
11235 };
11236 } else {
11237 obj[rName] = function () {
11238 return ifEdgeRenderedPosition(this, spec.get);
11239 };
11240 }
11241 return obj;
11242}, {});
11243
11244var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
11245
11246/*!
11247Event object based on jQuery events, MIT license
11248
11249https://jquery.org/license/
11250https://tldrlegal.com/license/mit-license
11251https://github.com/jquery/jquery/blob/master/src/event.js
11252*/
11253
11254var Event = function Event(src, props) {
11255 this.recycle(src, props);
11256};
11257function returnFalse() {
11258 return false;
11259}
11260function returnTrue() {
11261 return true;
11262}
11263
11264// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
11265Event.prototype = {
11266 instanceString: function instanceString() {
11267 return 'event';
11268 },
11269 recycle: function recycle(src, props) {
11270 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
11271 if (src != null && src.preventDefault) {
11272 // Browser Event object
11273 this.type = src.type;
11274
11275 // Events bubbling up the document may have been marked as prevented
11276 // by a handler lower down the tree; reflect the correct value.
11277 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
11278 } else if (src != null && src.type) {
11279 // Plain object containing all event details
11280 props = src;
11281 } else {
11282 // Event string
11283 this.type = src;
11284 }
11285
11286 // Put explicitly provided properties onto the event object
11287 if (props != null) {
11288 // more efficient to manually copy fields we use
11289 this.originalEvent = props.originalEvent;
11290 this.type = props.type != null ? props.type : this.type;
11291 this.cy = props.cy;
11292 this.target = props.target;
11293 this.position = props.position;
11294 this.renderedPosition = props.renderedPosition;
11295 this.namespace = props.namespace;
11296 this.layout = props.layout;
11297 }
11298 if (this.cy != null && this.position != null && this.renderedPosition == null) {
11299 // create a rendered position based on the passed position
11300 var pos = this.position;
11301 var zoom = this.cy.zoom();
11302 var pan = this.cy.pan();
11303 this.renderedPosition = {
11304 x: pos.x * zoom + pan.x,
11305 y: pos.y * zoom + pan.y
11306 };
11307 }
11308
11309 // Create a timestamp if incoming event doesn't have one
11310 this.timeStamp = src && src.timeStamp || Date.now();
11311 },
11312 preventDefault: function preventDefault() {
11313 this.isDefaultPrevented = returnTrue;
11314 var e = this.originalEvent;
11315 if (!e) {
11316 return;
11317 }
11318
11319 // if preventDefault exists run it on the original event
11320 if (e.preventDefault) {
11321 e.preventDefault();
11322 }
11323 },
11324 stopPropagation: function stopPropagation() {
11325 this.isPropagationStopped = returnTrue;
11326 var e = this.originalEvent;
11327 if (!e) {
11328 return;
11329 }
11330
11331 // if stopPropagation exists run it on the original event
11332 if (e.stopPropagation) {
11333 e.stopPropagation();
11334 }
11335 },
11336 stopImmediatePropagation: function stopImmediatePropagation() {
11337 this.isImmediatePropagationStopped = returnTrue;
11338 this.stopPropagation();
11339 },
11340 isDefaultPrevented: returnFalse,
11341 isPropagationStopped: returnFalse,
11342 isImmediatePropagationStopped: returnFalse
11343};
11344
11345var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
11346var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
11347
11348var defaults$8 = {
11349 qualifierCompare: function qualifierCompare(q1, q2) {
11350 return q1 === q2;
11351 },
11352 eventMatches: function eventMatches( /*context, listener, eventObj*/
11353 ) {
11354 return true;
11355 },
11356 addEventFields: function addEventFields( /*context, evt*/
11357 ) {},
11358 callbackContext: function callbackContext(context /*, listener, eventObj*/) {
11359 return context;
11360 },
11361 beforeEmit: function beforeEmit( /* context, listener, eventObj */
11362 ) {},
11363 afterEmit: function afterEmit( /* context, listener, eventObj */
11364 ) {},
11365 bubble: function bubble( /*context*/
11366 ) {
11367 return false;
11368 },
11369 parent: function parent( /*context*/
11370 ) {
11371 return null;
11372 },
11373 context: null
11374};
11375var defaultsKeys = Object.keys(defaults$8);
11376var emptyOpts = {};
11377function Emitter() {
11378 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
11379 var context = arguments.length > 1 ? arguments[1] : undefined;
11380 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
11381 for (var i = 0; i < defaultsKeys.length; i++) {
11382 var key = defaultsKeys[i];
11383 this[key] = opts[key] || defaults$8[key];
11384 }
11385 this.context = context || this.context;
11386 this.listeners = [];
11387 this.emitting = 0;
11388}
11389var p = Emitter.prototype;
11390var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
11391 if (fn$6(qualifier)) {
11392 callback = qualifier;
11393 qualifier = null;
11394 }
11395 if (confOverrides) {
11396 if (conf == null) {
11397 conf = confOverrides;
11398 } else {
11399 conf = extend({}, conf, confOverrides);
11400 }
11401 }
11402 var eventList = array(events) ? events : events.split(/\s+/);
11403 for (var i = 0; i < eventList.length; i++) {
11404 var evt = eventList[i];
11405 if (emptyString(evt)) {
11406 continue;
11407 }
11408 var match = evt.match(eventRegex); // type[.namespace]
11409
11410 if (match) {
11411 var type = match[1];
11412 var namespace = match[2] ? match[2] : null;
11413 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
11414 if (ret === false) {
11415 break;
11416 } // allow exiting early
11417 }
11418 }
11419};
11420
11421var makeEventObj = function makeEventObj(self, obj) {
11422 self.addEventFields(self.context, obj);
11423 return new Event(obj.type, obj);
11424};
11425var forEachEventObj = function forEachEventObj(self, handler, events) {
11426 if (event(events)) {
11427 handler(self, events);
11428 return;
11429 } else if (plainObject(events)) {
11430 handler(self, makeEventObj(self, events));
11431 return;
11432 }
11433 var eventList = array(events) ? events : events.split(/\s+/);
11434 for (var i = 0; i < eventList.length; i++) {
11435 var evt = eventList[i];
11436 if (emptyString(evt)) {
11437 continue;
11438 }
11439 var match = evt.match(eventRegex); // type[.namespace]
11440
11441 if (match) {
11442 var type = match[1];
11443 var namespace = match[2] ? match[2] : null;
11444 var eventObj = makeEventObj(self, {
11445 type: type,
11446 namespace: namespace,
11447 target: self.context
11448 });
11449 handler(self, eventObj);
11450 }
11451 }
11452};
11453p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
11454 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
11455 if (fn$6(callback)) {
11456 self.listeners.push({
11457 event: event,
11458 // full event string
11459 callback: callback,
11460 // callback to run
11461 type: type,
11462 // the event type (e.g. 'click')
11463 namespace: namespace,
11464 // the event namespace (e.g. ".foo")
11465 qualifier: qualifier,
11466 // a restriction on whether to match this emitter
11467 conf: conf // additional configuration
11468 });
11469 }
11470 }, events, qualifier, callback, conf, confOverrides);
11471 return this;
11472};
11473p.one = function (events, qualifier, callback, conf) {
11474 return this.on(events, qualifier, callback, conf, {
11475 one: true
11476 });
11477};
11478p.removeListener = p.off = function (events, qualifier, callback, conf) {
11479 var _this = this;
11480 if (this.emitting !== 0) {
11481 this.listeners = copyArray$1(this.listeners);
11482 }
11483 var listeners = this.listeners;
11484 var _loop = function _loop(i) {
11485 var listener = listeners[i];
11486 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback /*, conf*/) {
11487 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
11488 listeners.splice(i, 1);
11489 return false;
11490 }
11491 }, events, qualifier, callback, conf);
11492 };
11493 for (var i = listeners.length - 1; i >= 0; i--) {
11494 _loop(i);
11495 }
11496 return this;
11497};
11498p.removeAllListeners = function () {
11499 return this.removeListener('*');
11500};
11501p.emit = p.trigger = function (events, extraParams, manualCallback) {
11502 var listeners = this.listeners;
11503 var numListenersBeforeEmit = listeners.length;
11504 this.emitting++;
11505 if (!array(extraParams)) {
11506 extraParams = [extraParams];
11507 }
11508 forEachEventObj(this, function (self, eventObj) {
11509 if (manualCallback != null) {
11510 listeners = [{
11511 event: eventObj.event,
11512 type: eventObj.type,
11513 namespace: eventObj.namespace,
11514 callback: manualCallback
11515 }];
11516 numListenersBeforeEmit = listeners.length;
11517 }
11518 var _loop2 = function _loop2(i) {
11519 var listener = listeners[i];
11520 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
11521 var args = [eventObj];
11522 if (extraParams != null) {
11523 push(args, extraParams);
11524 }
11525 self.beforeEmit(self.context, listener, eventObj);
11526 if (listener.conf && listener.conf.one) {
11527 self.listeners = self.listeners.filter(function (l) {
11528 return l !== listener;
11529 });
11530 }
11531 var context = self.callbackContext(self.context, listener, eventObj);
11532 var ret = listener.callback.apply(context, args);
11533 self.afterEmit(self.context, listener, eventObj);
11534 if (ret === false) {
11535 eventObj.stopPropagation();
11536 eventObj.preventDefault();
11537 }
11538 } // if listener matches
11539 };
11540 for (var i = 0; i < numListenersBeforeEmit; i++) {
11541 _loop2(i);
11542 } // for listener
11543
11544 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
11545 self.parent(self.context).emit(eventObj, extraParams);
11546 }
11547 }, events);
11548 this.emitting--;
11549 return this;
11550};
11551
11552var emitterOptions$1 = {
11553 qualifierCompare: function qualifierCompare(selector1, selector2) {
11554 if (selector1 == null || selector2 == null) {
11555 return selector1 == null && selector2 == null;
11556 } else {
11557 return selector1.sameText(selector2);
11558 }
11559 },
11560 eventMatches: function eventMatches(ele, listener, eventObj) {
11561 var selector = listener.qualifier;
11562 if (selector != null) {
11563 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
11564 }
11565 return true;
11566 },
11567 addEventFields: function addEventFields(ele, evt) {
11568 evt.cy = ele.cy();
11569 evt.target = ele;
11570 },
11571 callbackContext: function callbackContext(ele, listener, eventObj) {
11572 return listener.qualifier != null ? eventObj.target : ele;
11573 },
11574 beforeEmit: function beforeEmit(context, listener /*, eventObj*/) {
11575 if (listener.conf && listener.conf.once) {
11576 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
11577 }
11578 },
11579 bubble: function bubble() {
11580 return true;
11581 },
11582 parent: function parent(ele) {
11583 return ele.isChild() ? ele.parent() : ele.cy();
11584 }
11585};
11586var argSelector$1 = function argSelector(arg) {
11587 if (string(arg)) {
11588 return new Selector(arg);
11589 } else {
11590 return arg;
11591 }
11592};
11593var elesfn$9 = {
11594 createEmitter: function createEmitter() {
11595 for (var i = 0; i < this.length; i++) {
11596 var ele = this[i];
11597 var _p = ele._private;
11598 if (!_p.emitter) {
11599 _p.emitter = new Emitter(emitterOptions$1, ele);
11600 }
11601 }
11602 return this;
11603 },
11604 emitter: function emitter() {
11605 return this._private.emitter;
11606 },
11607 on: function on(events, selector, callback) {
11608 var argSel = argSelector$1(selector);
11609 for (var i = 0; i < this.length; i++) {
11610 var ele = this[i];
11611 ele.emitter().on(events, argSel, callback);
11612 }
11613 return this;
11614 },
11615 removeListener: function removeListener(events, selector, callback) {
11616 var argSel = argSelector$1(selector);
11617 for (var i = 0; i < this.length; i++) {
11618 var ele = this[i];
11619 ele.emitter().removeListener(events, argSel, callback);
11620 }
11621 return this;
11622 },
11623 removeAllListeners: function removeAllListeners() {
11624 for (var i = 0; i < this.length; i++) {
11625 var ele = this[i];
11626 ele.emitter().removeAllListeners();
11627 }
11628 return this;
11629 },
11630 one: function one(events, selector, callback) {
11631 var argSel = argSelector$1(selector);
11632 for (var i = 0; i < this.length; i++) {
11633 var ele = this[i];
11634 ele.emitter().one(events, argSel, callback);
11635 }
11636 return this;
11637 },
11638 once: function once(events, selector, callback) {
11639 var argSel = argSelector$1(selector);
11640 for (var i = 0; i < this.length; i++) {
11641 var ele = this[i];
11642 ele.emitter().on(events, argSel, callback, {
11643 once: true,
11644 onceCollection: this
11645 });
11646 }
11647 },
11648 emit: function emit(events, extraParams) {
11649 for (var i = 0; i < this.length; i++) {
11650 var ele = this[i];
11651 ele.emitter().emit(events, extraParams);
11652 }
11653 return this;
11654 },
11655 emitAndNotify: function emitAndNotify(event, extraParams) {
11656 // for internal use only
11657 if (this.length === 0) {
11658 return;
11659 } // empty collections don't need to notify anything
11660
11661 // notify renderer
11662 this.cy().notify(event, this);
11663 this.emit(event, extraParams);
11664 return this;
11665 }
11666};
11667define.eventAliasesOn(elesfn$9);
11668
11669var elesfn$8 = {
11670 nodes: function nodes(selector) {
11671 return this.filter(function (ele) {
11672 return ele.isNode();
11673 }).filter(selector);
11674 },
11675 edges: function edges(selector) {
11676 return this.filter(function (ele) {
11677 return ele.isEdge();
11678 }).filter(selector);
11679 },
11680 // internal helper to get nodes and edges as separate collections with single iteration over elements
11681 byGroup: function byGroup() {
11682 var nodes = this.spawn();
11683 var edges = this.spawn();
11684 for (var i = 0; i < this.length; i++) {
11685 var ele = this[i];
11686 if (ele.isNode()) {
11687 nodes.push(ele);
11688 } else {
11689 edges.push(ele);
11690 }
11691 }
11692 return {
11693 nodes: nodes,
11694 edges: edges
11695 };
11696 },
11697 filter: function filter(_filter, thisArg) {
11698 if (_filter === undefined) {
11699 // check this first b/c it's the most common/performant case
11700 return this;
11701 } else if (string(_filter) || elementOrCollection(_filter)) {
11702 return new Selector(_filter).filter(this);
11703 } else if (fn$6(_filter)) {
11704 var filterEles = this.spawn();
11705 var eles = this;
11706 for (var i = 0; i < eles.length; i++) {
11707 var ele = eles[i];
11708 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
11709 if (include) {
11710 filterEles.push(ele);
11711 }
11712 }
11713 return filterEles;
11714 }
11715 return this.spawn(); // if not handled by above, give 'em an empty collection
11716 },
11717
11718 not: function not(toRemove) {
11719 if (!toRemove) {
11720 return this;
11721 } else {
11722 if (string(toRemove)) {
11723 toRemove = this.filter(toRemove);
11724 }
11725 var elements = this.spawn();
11726 for (var i = 0; i < this.length; i++) {
11727 var element = this[i];
11728 var remove = toRemove.has(element);
11729 if (!remove) {
11730 elements.push(element);
11731 }
11732 }
11733 return elements;
11734 }
11735 },
11736 absoluteComplement: function absoluteComplement() {
11737 var cy = this.cy();
11738 return cy.mutableElements().not(this);
11739 },
11740 intersect: function intersect(other) {
11741 // if a selector is specified, then filter by it instead
11742 if (string(other)) {
11743 var selector = other;
11744 return this.filter(selector);
11745 }
11746 var elements = this.spawn();
11747 var col1 = this;
11748 var col2 = other;
11749 var col1Smaller = this.length < other.length;
11750 var colS = col1Smaller ? col1 : col2;
11751 var colL = col1Smaller ? col2 : col1;
11752 for (var i = 0; i < colS.length; i++) {
11753 var ele = colS[i];
11754 if (colL.has(ele)) {
11755 elements.push(ele);
11756 }
11757 }
11758 return elements;
11759 },
11760 xor: function xor(other) {
11761 var cy = this._private.cy;
11762 if (string(other)) {
11763 other = cy.$(other);
11764 }
11765 var elements = this.spawn();
11766 var col1 = this;
11767 var col2 = other;
11768 var add = function add(col, other) {
11769 for (var i = 0; i < col.length; i++) {
11770 var ele = col[i];
11771 var id = ele._private.data.id;
11772 var inOther = other.hasElementWithId(id);
11773 if (!inOther) {
11774 elements.push(ele);
11775 }
11776 }
11777 };
11778 add(col1, col2);
11779 add(col2, col1);
11780 return elements;
11781 },
11782 diff: function diff(other) {
11783 var cy = this._private.cy;
11784 if (string(other)) {
11785 other = cy.$(other);
11786 }
11787 var left = this.spawn();
11788 var right = this.spawn();
11789 var both = this.spawn();
11790 var col1 = this;
11791 var col2 = other;
11792 var add = function add(col, other, retEles) {
11793 for (var i = 0; i < col.length; i++) {
11794 var ele = col[i];
11795 var id = ele._private.data.id;
11796 var inOther = other.hasElementWithId(id);
11797 if (inOther) {
11798 both.merge(ele);
11799 } else {
11800 retEles.push(ele);
11801 }
11802 }
11803 };
11804 add(col1, col2, left);
11805 add(col2, col1, right);
11806 return {
11807 left: left,
11808 right: right,
11809 both: both
11810 };
11811 },
11812 add: function add(toAdd) {
11813 var cy = this._private.cy;
11814 if (!toAdd) {
11815 return this;
11816 }
11817 if (string(toAdd)) {
11818 var selector = toAdd;
11819 toAdd = cy.mutableElements().filter(selector);
11820 }
11821 var elements = this.spawnSelf();
11822 for (var i = 0; i < toAdd.length; i++) {
11823 var ele = toAdd[i];
11824 var add = !this.has(ele);
11825 if (add) {
11826 elements.push(ele);
11827 }
11828 }
11829 return elements;
11830 },
11831 // in place merge on calling collection
11832 merge: function merge(toAdd) {
11833 var _p = this._private;
11834 var cy = _p.cy;
11835 if (!toAdd) {
11836 return this;
11837 }
11838 if (toAdd && string(toAdd)) {
11839 var selector = toAdd;
11840 toAdd = cy.mutableElements().filter(selector);
11841 }
11842 var map = _p.map;
11843 for (var i = 0; i < toAdd.length; i++) {
11844 var toAddEle = toAdd[i];
11845 var id = toAddEle._private.data.id;
11846 var add = !map.has(id);
11847 if (add) {
11848 var index = this.length++;
11849 this[index] = toAddEle;
11850 map.set(id, {
11851 ele: toAddEle,
11852 index: index
11853 });
11854 }
11855 }
11856 return this; // chaining
11857 },
11858
11859 unmergeAt: function unmergeAt(i) {
11860 var ele = this[i];
11861 var id = ele.id();
11862 var _p = this._private;
11863 var map = _p.map;
11864
11865 // remove ele
11866 this[i] = undefined;
11867 map["delete"](id);
11868 var unmergedLastEle = i === this.length - 1;
11869
11870 // replace empty spot with last ele in collection
11871 if (this.length > 1 && !unmergedLastEle) {
11872 var lastEleI = this.length - 1;
11873 var lastEle = this[lastEleI];
11874 var lastEleId = lastEle._private.data.id;
11875 this[lastEleI] = undefined;
11876 this[i] = lastEle;
11877 map.set(lastEleId, {
11878 ele: lastEle,
11879 index: i
11880 });
11881 }
11882
11883 // the collection is now 1 ele smaller
11884 this.length--;
11885 return this;
11886 },
11887 // remove single ele in place in calling collection
11888 unmergeOne: function unmergeOne(ele) {
11889 ele = ele[0];
11890 var _p = this._private;
11891 var id = ele._private.data.id;
11892 var map = _p.map;
11893 var entry = map.get(id);
11894 if (!entry) {
11895 return this; // no need to remove
11896 }
11897
11898 var i = entry.index;
11899 this.unmergeAt(i);
11900 return this;
11901 },
11902 // remove eles in place on calling collection
11903 unmerge: function unmerge(toRemove) {
11904 var cy = this._private.cy;
11905 if (!toRemove) {
11906 return this;
11907 }
11908 if (toRemove && string(toRemove)) {
11909 var selector = toRemove;
11910 toRemove = cy.mutableElements().filter(selector);
11911 }
11912 for (var i = 0; i < toRemove.length; i++) {
11913 this.unmergeOne(toRemove[i]);
11914 }
11915 return this; // chaining
11916 },
11917
11918 unmergeBy: function unmergeBy(toRmFn) {
11919 for (var i = this.length - 1; i >= 0; i--) {
11920 var ele = this[i];
11921 if (toRmFn(ele)) {
11922 this.unmergeAt(i);
11923 }
11924 }
11925 return this;
11926 },
11927 map: function map(mapFn, thisArg) {
11928 var arr = [];
11929 var eles = this;
11930 for (var i = 0; i < eles.length; i++) {
11931 var ele = eles[i];
11932 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11933 arr.push(ret);
11934 }
11935 return arr;
11936 },
11937 reduce: function reduce(fn, initialValue) {
11938 var val = initialValue;
11939 var eles = this;
11940 for (var i = 0; i < eles.length; i++) {
11941 val = fn(val, eles[i], i, eles);
11942 }
11943 return val;
11944 },
11945 max: function max(valFn, thisArg) {
11946 var max = -Infinity;
11947 var maxEle;
11948 var eles = this;
11949 for (var i = 0; i < eles.length; i++) {
11950 var ele = eles[i];
11951 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11952 if (val > max) {
11953 max = val;
11954 maxEle = ele;
11955 }
11956 }
11957 return {
11958 value: max,
11959 ele: maxEle
11960 };
11961 },
11962 min: function min(valFn, thisArg) {
11963 var min = Infinity;
11964 var minEle;
11965 var eles = this;
11966 for (var i = 0; i < eles.length; i++) {
11967 var ele = eles[i];
11968 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11969 if (val < min) {
11970 min = val;
11971 minEle = ele;
11972 }
11973 }
11974 return {
11975 value: min,
11976 ele: minEle
11977 };
11978 }
11979};
11980
11981// aliases
11982var fn$1 = elesfn$8;
11983fn$1['u'] = fn$1['|'] = fn$1['+'] = fn$1.union = fn$1.or = fn$1.add;
11984fn$1['\\'] = fn$1['!'] = fn$1['-'] = fn$1.difference = fn$1.relativeComplement = fn$1.subtract = fn$1.not;
11985fn$1['n'] = fn$1['&'] = fn$1['.'] = fn$1.and = fn$1.intersection = fn$1.intersect;
11986fn$1['^'] = fn$1['(+)'] = fn$1['(-)'] = fn$1.symmetricDifference = fn$1.symdiff = fn$1.xor;
11987fn$1.fnFilter = fn$1.filterFn = fn$1.stdFilter = fn$1.filter;
11988fn$1.complement = fn$1.abscomp = fn$1.absoluteComplement;
11989
11990var elesfn$7 = {
11991 isNode: function isNode() {
11992 return this.group() === 'nodes';
11993 },
11994 isEdge: function isEdge() {
11995 return this.group() === 'edges';
11996 },
11997 isLoop: function isLoop() {
11998 return this.isEdge() && this.source()[0] === this.target()[0];
11999 },
12000 isSimple: function isSimple() {
12001 return this.isEdge() && this.source()[0] !== this.target()[0];
12002 },
12003 group: function group() {
12004 var ele = this[0];
12005 if (ele) {
12006 return ele._private.group;
12007 }
12008 }
12009};
12010
12011/**
12012 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
12013 * and z-index (low to high). These styles affect how this applies:
12014 *
12015 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
12016 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
12017 * root to leaves of the compound graph. The last drawn is `top`.
12018 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
12019 * `manual` ignores this convention and draws based on the `z-index` value setting.
12020 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
12021 * `z-index` will be drawn on top of an element with a lower `z-index`.
12022 */
12023var zIndexSort = function zIndexSort(a, b) {
12024 var cy = a.cy();
12025 var hasCompoundNodes = cy.hasCompoundNodes();
12026 function getDepth(ele) {
12027 var style = ele.pstyle('z-compound-depth');
12028 if (style.value === 'auto') {
12029 return hasCompoundNodes ? ele.zDepth() : 0;
12030 } else if (style.value === 'bottom') {
12031 return -1;
12032 } else if (style.value === 'top') {
12033 return MAX_INT$1;
12034 }
12035 // 'orphan'
12036 return 0;
12037 }
12038 var depthDiff = getDepth(a) - getDepth(b);
12039 if (depthDiff !== 0) {
12040 return depthDiff;
12041 }
12042 function getEleDepth(ele) {
12043 var style = ele.pstyle('z-index-compare');
12044 if (style.value === 'auto') {
12045 return ele.isNode() ? 1 : 0;
12046 }
12047 // 'manual'
12048 return 0;
12049 }
12050 var eleDiff = getEleDepth(a) - getEleDepth(b);
12051 if (eleDiff !== 0) {
12052 return eleDiff;
12053 }
12054 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
12055 if (zDiff !== 0) {
12056 return zDiff;
12057 }
12058 // compare indices in the core (order added to graph w/ last on top)
12059 return a.poolIndex() - b.poolIndex();
12060};
12061
12062var elesfn$6 = {
12063 forEach: function forEach(fn, thisArg) {
12064 if (fn$6(fn)) {
12065 var N = this.length;
12066 for (var i = 0; i < N; i++) {
12067 var ele = this[i];
12068 var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this);
12069 if (ret === false) {
12070 break;
12071 } // exit each early on return false
12072 }
12073 }
12074
12075 return this;
12076 },
12077 toArray: function toArray() {
12078 var array = [];
12079 for (var i = 0; i < this.length; i++) {
12080 array.push(this[i]);
12081 }
12082 return array;
12083 },
12084 slice: function slice(start, end) {
12085 var array = [];
12086 var thisSize = this.length;
12087 if (end == null) {
12088 end = thisSize;
12089 }
12090 if (start == null) {
12091 start = 0;
12092 }
12093 if (start < 0) {
12094 start = thisSize + start;
12095 }
12096 if (end < 0) {
12097 end = thisSize + end;
12098 }
12099 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
12100 array.push(this[i]);
12101 }
12102 return this.spawn(array);
12103 },
12104 size: function size() {
12105 return this.length;
12106 },
12107 eq: function eq(i) {
12108 return this[i] || this.spawn();
12109 },
12110 first: function first() {
12111 return this[0] || this.spawn();
12112 },
12113 last: function last() {
12114 return this[this.length - 1] || this.spawn();
12115 },
12116 empty: function empty() {
12117 return this.length === 0;
12118 },
12119 nonempty: function nonempty() {
12120 return !this.empty();
12121 },
12122 sort: function sort(sortFn) {
12123 if (!fn$6(sortFn)) {
12124 return this;
12125 }
12126 var sorted = this.toArray().sort(sortFn);
12127 return this.spawn(sorted);
12128 },
12129 sortByZIndex: function sortByZIndex() {
12130 return this.sort(zIndexSort);
12131 },
12132 zDepth: function zDepth() {
12133 var ele = this[0];
12134 if (!ele) {
12135 return undefined;
12136 }
12137
12138 // let cy = ele.cy();
12139 var _p = ele._private;
12140 var group = _p.group;
12141 if (group === 'nodes') {
12142 var depth = _p.data.parent ? ele.parents().size() : 0;
12143 if (!ele.isParent()) {
12144 return MAX_INT$1 - 1; // childless nodes always on top
12145 }
12146
12147 return depth;
12148 } else {
12149 var src = _p.source;
12150 var tgt = _p.target;
12151 var srcDepth = src.zDepth();
12152 var tgtDepth = tgt.zDepth();
12153 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
12154 }
12155 }
12156};
12157
12158elesfn$6.each = elesfn$6.forEach;
12159var defineSymbolIterator = function defineSymbolIterator() {
12160 var typeofUndef = "undefined" ;
12161 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
12162
12163 if (isIteratorSupported) {
12164 elesfn$6[Symbol.iterator] = function () {
12165 var _this = this;
12166 // eslint-disable-line no-undef
12167 var entry = {
12168 value: undefined,
12169 done: false
12170 };
12171 var i = 0;
12172 var length = this.length;
12173 return _defineProperty$1({
12174 next: function next() {
12175 if (i < length) {
12176 entry.value = _this[i++];
12177 } else {
12178 entry.value = undefined;
12179 entry.done = true;
12180 }
12181 return entry;
12182 }
12183 }, Symbol.iterator, function () {
12184 // eslint-disable-line no-undef
12185 return this;
12186 });
12187 };
12188 }
12189};
12190defineSymbolIterator();
12191
12192var getLayoutDimensionOptions = defaults$g({
12193 nodeDimensionsIncludeLabels: false
12194});
12195var elesfn$5 = {
12196 // Calculates and returns node dimensions { x, y } based on options given
12197 layoutDimensions: function layoutDimensions(options) {
12198 options = getLayoutDimensionOptions(options);
12199 var dims;
12200 if (!this.takesUpSpace()) {
12201 dims = {
12202 w: 0,
12203 h: 0
12204 };
12205 } else if (options.nodeDimensionsIncludeLabels) {
12206 var bbDim = this.boundingBox();
12207 dims = {
12208 w: bbDim.w,
12209 h: bbDim.h
12210 };
12211 } else {
12212 dims = {
12213 w: this.outerWidth(),
12214 h: this.outerHeight()
12215 };
12216 }
12217
12218 // sanitise the dimensions for external layouts (avoid division by zero)
12219 if (dims.w === 0 || dims.h === 0) {
12220 dims.w = dims.h = 1;
12221 }
12222 return dims;
12223 },
12224 // using standard layout options, apply position function (w/ or w/o animation)
12225 layoutPositions: function layoutPositions(layout, options, fn) {
12226 var nodes = this.nodes().filter(function (n) {
12227 return !n.isParent();
12228 });
12229 var cy = this.cy();
12230 var layoutEles = options.eles; // nodes & edges
12231 var getMemoizeKey = function getMemoizeKey(node) {
12232 return node.id();
12233 };
12234 var fnMem = memoize$1(fn, getMemoizeKey); // memoized version of position function
12235
12236 layout.emit({
12237 type: 'layoutstart',
12238 layout: layout
12239 });
12240 layout.animations = [];
12241 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
12242 var center = {
12243 x: nodesBb.x1 + nodesBb.w / 2,
12244 y: nodesBb.y1 + nodesBb.h / 2
12245 };
12246 var spacingVector = {
12247 // scale from center of bounding box (not necessarily 0,0)
12248 x: (pos.x - center.x) * spacing,
12249 y: (pos.y - center.y) * spacing
12250 };
12251 return {
12252 x: center.x + spacingVector.x,
12253 y: center.y + spacingVector.y
12254 };
12255 };
12256 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
12257 var spacingBb = function spacingBb() {
12258 if (!useSpacingFactor) {
12259 return null;
12260 }
12261 var bb = makeBoundingBox();
12262 for (var i = 0; i < nodes.length; i++) {
12263 var node = nodes[i];
12264 var pos = fnMem(node, i);
12265 expandBoundingBoxByPoint(bb, pos.x, pos.y);
12266 }
12267 return bb;
12268 };
12269 var bb = spacingBb();
12270 var getFinalPos = memoize$1(function (node, i) {
12271 var newPos = fnMem(node, i);
12272 if (useSpacingFactor) {
12273 var spacing = Math.abs(options.spacingFactor);
12274 newPos = calculateSpacing(spacing, bb, newPos);
12275 }
12276 if (options.transform != null) {
12277 newPos = options.transform(node, newPos);
12278 }
12279 return newPos;
12280 }, getMemoizeKey);
12281 if (options.animate) {
12282 for (var i = 0; i < nodes.length; i++) {
12283 var node = nodes[i];
12284 var newPos = getFinalPos(node, i);
12285 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
12286 if (animateNode) {
12287 var ani = node.animation({
12288 position: newPos,
12289 duration: options.animationDuration,
12290 easing: options.animationEasing
12291 });
12292 layout.animations.push(ani);
12293 } else {
12294 node.position(newPos);
12295 }
12296 }
12297 if (options.fit) {
12298 var fitAni = cy.animation({
12299 fit: {
12300 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
12301 padding: options.padding
12302 },
12303 duration: options.animationDuration,
12304 easing: options.animationEasing
12305 });
12306 layout.animations.push(fitAni);
12307 } else if (options.zoom !== undefined && options.pan !== undefined) {
12308 var zoomPanAni = cy.animation({
12309 zoom: options.zoom,
12310 pan: options.pan,
12311 duration: options.animationDuration,
12312 easing: options.animationEasing
12313 });
12314 layout.animations.push(zoomPanAni);
12315 }
12316 layout.animations.forEach(function (ani) {
12317 return ani.play();
12318 });
12319 layout.one('layoutready', options.ready);
12320 layout.emit({
12321 type: 'layoutready',
12322 layout: layout
12323 });
12324 Promise$1.all(layout.animations.map(function (ani) {
12325 return ani.promise();
12326 })).then(function () {
12327 layout.one('layoutstop', options.stop);
12328 layout.emit({
12329 type: 'layoutstop',
12330 layout: layout
12331 });
12332 });
12333 } else {
12334 nodes.positions(getFinalPos);
12335 if (options.fit) {
12336 cy.fit(options.eles, options.padding);
12337 }
12338 if (options.zoom != null) {
12339 cy.zoom(options.zoom);
12340 }
12341 if (options.pan) {
12342 cy.pan(options.pan);
12343 }
12344 layout.one('layoutready', options.ready);
12345 layout.emit({
12346 type: 'layoutready',
12347 layout: layout
12348 });
12349 layout.one('layoutstop', options.stop);
12350 layout.emit({
12351 type: 'layoutstop',
12352 layout: layout
12353 });
12354 }
12355 return this; // chaining
12356 },
12357
12358 layout: function layout(options) {
12359 var cy = this.cy();
12360 return cy.makeLayout(extend({}, options, {
12361 eles: this
12362 }));
12363 }
12364};
12365
12366// aliases:
12367elesfn$5.createLayout = elesfn$5.makeLayout = elesfn$5.layout;
12368
12369function styleCache(key, fn, ele) {
12370 var _p = ele._private;
12371 var cache = _p.styleCache = _p.styleCache || [];
12372 var val;
12373 if ((val = cache[key]) != null) {
12374 return val;
12375 } else {
12376 val = cache[key] = fn(ele);
12377 return val;
12378 }
12379}
12380function cacheStyleFunction(key, fn) {
12381 key = hashString(key);
12382 return function cachedStyleFunction(ele) {
12383 return styleCache(key, fn, ele);
12384 };
12385}
12386function cachePrototypeStyleFunction(key, fn) {
12387 key = hashString(key);
12388 var selfFn = function selfFn(ele) {
12389 return fn.call(ele);
12390 };
12391 return function cachedPrototypeStyleFunction() {
12392 var ele = this[0];
12393 if (ele) {
12394 return styleCache(key, selfFn, ele);
12395 }
12396 };
12397}
12398var elesfn$4 = {
12399 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
12400 var cy = this.cy();
12401 var renderer = cy.renderer();
12402 var styleEnabled = cy.styleEnabled();
12403 if (renderer && styleEnabled) {
12404 renderer.recalculateRenderedStyle(this, useCache);
12405 }
12406 return this;
12407 },
12408 dirtyStyleCache: function dirtyStyleCache() {
12409 var cy = this.cy();
12410 var dirty = function dirty(ele) {
12411 return ele._private.styleCache = null;
12412 };
12413 if (cy.hasCompoundNodes()) {
12414 var eles;
12415 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12416 eles.merge(eles.connectedEdges());
12417 eles.forEach(dirty);
12418 } else {
12419 this.forEach(function (ele) {
12420 dirty(ele);
12421 ele.connectedEdges().forEach(dirty);
12422 });
12423 }
12424 return this;
12425 },
12426 // fully updates (recalculates) the style for the elements
12427 updateStyle: function updateStyle(notifyRenderer) {
12428 var cy = this._private.cy;
12429 if (!cy.styleEnabled()) {
12430 return this;
12431 }
12432 if (cy.batching()) {
12433 var bEles = cy._private.batchStyleEles;
12434 bEles.merge(this);
12435 return this; // chaining and exit early when batching
12436 }
12437
12438 var hasCompounds = cy.hasCompoundNodes();
12439 var updatedEles = this;
12440 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
12441 if (hasCompounds) {
12442 // then add everything up and down for compound selector checks
12443 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12444 }
12445
12446 // let changedEles = style.apply( updatedEles );
12447 var changedEles = updatedEles;
12448 if (notifyRenderer) {
12449 changedEles.emitAndNotify('style'); // let renderer know we changed style
12450 } else {
12451 changedEles.emit('style'); // just fire the event
12452 }
12453
12454 updatedEles.forEach(function (ele) {
12455 return ele._private.styleDirty = true;
12456 });
12457 return this; // chaining
12458 },
12459
12460 // private: clears dirty flag and recalculates style
12461 cleanStyle: function cleanStyle() {
12462 var cy = this.cy();
12463 if (!cy.styleEnabled()) {
12464 return;
12465 }
12466 for (var i = 0; i < this.length; i++) {
12467 var ele = this[i];
12468 if (ele._private.styleDirty) {
12469 // n.b. this flag should be set before apply() to avoid potential infinite recursion
12470 ele._private.styleDirty = false;
12471 cy.style().apply(ele);
12472 }
12473 }
12474 },
12475 // get the internal parsed style object for the specified property
12476 parsedStyle: function parsedStyle(property) {
12477 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12478 var ele = this[0];
12479 var cy = ele.cy();
12480 if (!cy.styleEnabled()) {
12481 return;
12482 }
12483 if (ele) {
12484 this.cleanStyle();
12485 var overriddenStyle = ele._private.style[property];
12486 if (overriddenStyle != null) {
12487 return overriddenStyle;
12488 } else if (includeNonDefault) {
12489 return cy.style().getDefaultProperty(property);
12490 } else {
12491 return null;
12492 }
12493 }
12494 },
12495 numericStyle: function numericStyle(property) {
12496 var ele = this[0];
12497 if (!ele.cy().styleEnabled()) {
12498 return;
12499 }
12500 if (ele) {
12501 var pstyle = ele.pstyle(property);
12502 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
12503 }
12504 },
12505 numericStyleUnits: function numericStyleUnits(property) {
12506 var ele = this[0];
12507 if (!ele.cy().styleEnabled()) {
12508 return;
12509 }
12510 if (ele) {
12511 return ele.pstyle(property).units;
12512 }
12513 },
12514 // get the specified css property as a rendered value (i.e. on-screen value)
12515 // or get the whole rendered style if no property specified (NB doesn't allow setting)
12516 renderedStyle: function renderedStyle(property) {
12517 var cy = this.cy();
12518 if (!cy.styleEnabled()) {
12519 return this;
12520 }
12521 var ele = this[0];
12522 if (ele) {
12523 return cy.style().getRenderedStyle(ele, property);
12524 }
12525 },
12526 // read the calculated css style of the element or override the style (via a bypass)
12527 style: function style(name, value) {
12528 var cy = this.cy();
12529 if (!cy.styleEnabled()) {
12530 return this;
12531 }
12532 var updateTransitions = false;
12533 var style = cy.style();
12534 if (plainObject(name)) {
12535 // then extend the bypass
12536 var props = name;
12537 style.applyBypass(this, props, updateTransitions);
12538 this.emitAndNotify('style'); // let the renderer know we've updated style
12539 } else if (string(name)) {
12540 if (value === undefined) {
12541 // then get the property from the style
12542 var ele = this[0];
12543 if (ele) {
12544 return style.getStylePropertyValue(ele, name);
12545 } else {
12546 // empty collection => can't get any value
12547 return;
12548 }
12549 } else {
12550 // then set the bypass with the property value
12551 style.applyBypass(this, name, value, updateTransitions);
12552 this.emitAndNotify('style'); // let the renderer know we've updated style
12553 }
12554 } else if (name === undefined) {
12555 var _ele = this[0];
12556 if (_ele) {
12557 return style.getRawStyle(_ele);
12558 } else {
12559 // empty collection => can't get any value
12560 return;
12561 }
12562 }
12563 return this; // chaining
12564 },
12565
12566 removeStyle: function removeStyle(names) {
12567 var cy = this.cy();
12568 if (!cy.styleEnabled()) {
12569 return this;
12570 }
12571 var updateTransitions = false;
12572 var style = cy.style();
12573 var eles = this;
12574 if (names === undefined) {
12575 for (var i = 0; i < eles.length; i++) {
12576 var ele = eles[i];
12577 style.removeAllBypasses(ele, updateTransitions);
12578 }
12579 } else {
12580 names = names.split(/\s+/);
12581 for (var _i = 0; _i < eles.length; _i++) {
12582 var _ele2 = eles[_i];
12583 style.removeBypasses(_ele2, names, updateTransitions);
12584 }
12585 }
12586 this.emitAndNotify('style'); // let the renderer know we've updated style
12587
12588 return this; // chaining
12589 },
12590
12591 show: function show() {
12592 this.css('display', 'element');
12593 return this; // chaining
12594 },
12595
12596 hide: function hide() {
12597 this.css('display', 'none');
12598 return this; // chaining
12599 },
12600
12601 effectiveOpacity: function effectiveOpacity() {
12602 var cy = this.cy();
12603 if (!cy.styleEnabled()) {
12604 return 1;
12605 }
12606 var hasCompoundNodes = cy.hasCompoundNodes();
12607 var ele = this[0];
12608 if (ele) {
12609 var _p = ele._private;
12610 var parentOpacity = ele.pstyle('opacity').value;
12611 if (!hasCompoundNodes) {
12612 return parentOpacity;
12613 }
12614 var parents = !_p.data.parent ? null : ele.parents();
12615 if (parents) {
12616 for (var i = 0; i < parents.length; i++) {
12617 var parent = parents[i];
12618 var opacity = parent.pstyle('opacity').value;
12619 parentOpacity = opacity * parentOpacity;
12620 }
12621 }
12622 return parentOpacity;
12623 }
12624 },
12625 transparent: function transparent() {
12626 var cy = this.cy();
12627 if (!cy.styleEnabled()) {
12628 return false;
12629 }
12630 var ele = this[0];
12631 var hasCompoundNodes = ele.cy().hasCompoundNodes();
12632 if (ele) {
12633 if (!hasCompoundNodes) {
12634 return ele.pstyle('opacity').value === 0;
12635 } else {
12636 return ele.effectiveOpacity() === 0;
12637 }
12638 }
12639 },
12640 backgrounding: function backgrounding() {
12641 var cy = this.cy();
12642 if (!cy.styleEnabled()) {
12643 return false;
12644 }
12645 var ele = this[0];
12646 return ele._private.backgrounding ? true : false;
12647 }
12648};
12649function checkCompound(ele, parentOk) {
12650 var _p = ele._private;
12651 var parents = _p.data.parent ? ele.parents() : null;
12652 if (parents) {
12653 for (var i = 0; i < parents.length; i++) {
12654 var parent = parents[i];
12655 if (!parentOk(parent)) {
12656 return false;
12657 }
12658 }
12659 }
12660 return true;
12661}
12662function defineDerivedStateFunction(specs) {
12663 var ok = specs.ok;
12664 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
12665 var parentOk = specs.parentOk || specs.ok;
12666 return function () {
12667 var cy = this.cy();
12668 if (!cy.styleEnabled()) {
12669 return true;
12670 }
12671 var ele = this[0];
12672 var hasCompoundNodes = cy.hasCompoundNodes();
12673 if (ele) {
12674 var _p = ele._private;
12675 if (!ok(ele)) {
12676 return false;
12677 }
12678 if (ele.isNode()) {
12679 return !hasCompoundNodes || checkCompound(ele, parentOk);
12680 } else {
12681 var src = _p.source;
12682 var tgt = _p.target;
12683 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
12684 }
12685 }
12686 };
12687}
12688var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
12689 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
12690});
12691elesfn$4.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
12692 ok: eleTakesUpSpace
12693}));
12694var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
12695 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
12696});
12697var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
12698 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
12699});
12700elesfn$4.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
12701 ok: eleInteractive,
12702 parentOk: parentInteractive,
12703 edgeOkViaNode: eleTakesUpSpace
12704}));
12705elesfn$4.noninteractive = function () {
12706 var ele = this[0];
12707 if (ele) {
12708 return !ele.interactive();
12709 }
12710};
12711var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
12712 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
12713});
12714var edgeVisibleViaNode = eleTakesUpSpace;
12715elesfn$4.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
12716 ok: eleVisible,
12717 edgeOkViaNode: edgeVisibleViaNode
12718}));
12719elesfn$4.hidden = function () {
12720 var ele = this[0];
12721 if (ele) {
12722 return !ele.visible();
12723 }
12724};
12725elesfn$4.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
12726 if (!this.cy().styleEnabled()) {
12727 return false;
12728 }
12729 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
12730});
12731elesfn$4.bypass = elesfn$4.css = elesfn$4.style;
12732elesfn$4.renderedCss = elesfn$4.renderedStyle;
12733elesfn$4.removeBypass = elesfn$4.removeCss = elesfn$4.removeStyle;
12734elesfn$4.pstyle = elesfn$4.parsedStyle;
12735
12736var elesfn$3 = {};
12737function defineSwitchFunction(params) {
12738 return function () {
12739 var args = arguments;
12740 var changedEles = [];
12741
12742 // e.g. cy.nodes().select( data, handler )
12743 if (args.length === 2) {
12744 var data = args[0];
12745 var handler = args[1];
12746 this.on(params.event, data, handler);
12747 }
12748
12749 // e.g. cy.nodes().select( handler )
12750 else if (args.length === 1 && fn$6(args[0])) {
12751 var _handler = args[0];
12752 this.on(params.event, _handler);
12753 }
12754
12755 // e.g. cy.nodes().select()
12756 // e.g. (private) cy.nodes().select(['tapselect'])
12757 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12758 var addlEvents = args.length === 1 ? args[0] : null;
12759 for (var i = 0; i < this.length; i++) {
12760 var ele = this[i];
12761 var able = !params.ableField || ele._private[params.ableField];
12762 var changed = ele._private[params.field] != params.value;
12763 if (params.overrideAble) {
12764 var overrideAble = params.overrideAble(ele);
12765 if (overrideAble !== undefined) {
12766 able = overrideAble;
12767 if (!overrideAble) {
12768 return this;
12769 } // to save cycles assume not able for all on override
12770 }
12771 }
12772
12773 if (able) {
12774 ele._private[params.field] = params.value;
12775 if (changed) {
12776 changedEles.push(ele);
12777 }
12778 }
12779 }
12780 var changedColl = this.spawn(changedEles);
12781 changedColl.updateStyle(); // change of state => possible change of style
12782 changedColl.emit(params.event);
12783 if (addlEvents) {
12784 changedColl.emit(addlEvents);
12785 }
12786 }
12787 return this;
12788 };
12789}
12790function defineSwitchSet(params) {
12791 elesfn$3[params.field] = function () {
12792 var ele = this[0];
12793 if (ele) {
12794 if (params.overrideField) {
12795 var val = params.overrideField(ele);
12796 if (val !== undefined) {
12797 return val;
12798 }
12799 }
12800 return ele._private[params.field];
12801 }
12802 };
12803 elesfn$3[params.on] = defineSwitchFunction({
12804 event: params.on,
12805 field: params.field,
12806 ableField: params.ableField,
12807 overrideAble: params.overrideAble,
12808 value: true
12809 });
12810 elesfn$3[params.off] = defineSwitchFunction({
12811 event: params.off,
12812 field: params.field,
12813 ableField: params.ableField,
12814 overrideAble: params.overrideAble,
12815 value: false
12816 });
12817}
12818defineSwitchSet({
12819 field: 'locked',
12820 overrideField: function overrideField(ele) {
12821 return ele.cy().autolock() ? true : undefined;
12822 },
12823 on: 'lock',
12824 off: 'unlock'
12825});
12826defineSwitchSet({
12827 field: 'grabbable',
12828 overrideField: function overrideField(ele) {
12829 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12830 },
12831 on: 'grabify',
12832 off: 'ungrabify'
12833});
12834defineSwitchSet({
12835 field: 'selected',
12836 ableField: 'selectable',
12837 overrideAble: function overrideAble(ele) {
12838 return ele.cy().autounselectify() ? false : undefined;
12839 },
12840 on: 'select',
12841 off: 'unselect'
12842});
12843defineSwitchSet({
12844 field: 'selectable',
12845 overrideField: function overrideField(ele) {
12846 return ele.cy().autounselectify() ? false : undefined;
12847 },
12848 on: 'selectify',
12849 off: 'unselectify'
12850});
12851elesfn$3.deselect = elesfn$3.unselect;
12852elesfn$3.grabbed = function () {
12853 var ele = this[0];
12854 if (ele) {
12855 return ele._private.grabbed;
12856 }
12857};
12858defineSwitchSet({
12859 field: 'active',
12860 on: 'activate',
12861 off: 'unactivate'
12862});
12863defineSwitchSet({
12864 field: 'pannable',
12865 on: 'panify',
12866 off: 'unpanify'
12867});
12868elesfn$3.inactive = function () {
12869 var ele = this[0];
12870 if (ele) {
12871 return !ele._private.active;
12872 }
12873};
12874
12875var elesfn$2 = {};
12876
12877// DAG functions
12878////////////////
12879
12880var defineDagExtremity = function defineDagExtremity(params) {
12881 return function dagExtremityImpl(selector) {
12882 var eles = this;
12883 var ret = [];
12884 for (var i = 0; i < eles.length; i++) {
12885 var ele = eles[i];
12886 if (!ele.isNode()) {
12887 continue;
12888 }
12889 var disqualified = false;
12890 var edges = ele.connectedEdges();
12891 for (var j = 0; j < edges.length; j++) {
12892 var edge = edges[j];
12893 var src = edge.source();
12894 var tgt = edge.target();
12895 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12896 disqualified = true;
12897 break;
12898 }
12899 }
12900 if (!disqualified) {
12901 ret.push(ele);
12902 }
12903 }
12904 return this.spawn(ret, true).filter(selector);
12905 };
12906};
12907var defineDagOneHop = function defineDagOneHop(params) {
12908 return function (selector) {
12909 var eles = this;
12910 var oEles = [];
12911 for (var i = 0; i < eles.length; i++) {
12912 var ele = eles[i];
12913 if (!ele.isNode()) {
12914 continue;
12915 }
12916 var edges = ele.connectedEdges();
12917 for (var j = 0; j < edges.length; j++) {
12918 var edge = edges[j];
12919 var src = edge.source();
12920 var tgt = edge.target();
12921 if (params.outgoing && src === ele) {
12922 oEles.push(edge);
12923 oEles.push(tgt);
12924 } else if (params.incoming && tgt === ele) {
12925 oEles.push(edge);
12926 oEles.push(src);
12927 }
12928 }
12929 }
12930 return this.spawn(oEles, true).filter(selector);
12931 };
12932};
12933var defineDagAllHops = function defineDagAllHops(params) {
12934 return function (selector) {
12935 var eles = this;
12936 var sEles = [];
12937 var sElesIds = {};
12938 for (;;) {
12939 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12940 if (next.length === 0) {
12941 break;
12942 } // done if none left
12943
12944 var newNext = false;
12945 for (var i = 0; i < next.length; i++) {
12946 var n = next[i];
12947 var nid = n.id();
12948 if (!sElesIds[nid]) {
12949 sElesIds[nid] = true;
12950 sEles.push(n);
12951 newNext = true;
12952 }
12953 }
12954 if (!newNext) {
12955 break;
12956 } // done if touched all outgoers already
12957
12958 eles = next;
12959 }
12960 return this.spawn(sEles, true).filter(selector);
12961 };
12962};
12963elesfn$2.clearTraversalCache = function () {
12964 for (var i = 0; i < this.length; i++) {
12965 this[i]._private.traversalCache = null;
12966 }
12967};
12968extend(elesfn$2, {
12969 // get the root nodes in the DAG
12970 roots: defineDagExtremity({
12971 noIncomingEdges: true
12972 }),
12973 // get the leaf nodes in the DAG
12974 leaves: defineDagExtremity({
12975 noOutgoingEdges: true
12976 }),
12977 // normally called children in graph theory
12978 // these nodes =edges=> outgoing nodes
12979 outgoers: cache(defineDagOneHop({
12980 outgoing: true
12981 }), 'outgoers'),
12982 // aka DAG descendants
12983 successors: defineDagAllHops({
12984 outgoing: true
12985 }),
12986 // normally called parents in graph theory
12987 // these nodes <=edges= incoming nodes
12988 incomers: cache(defineDagOneHop({
12989 incoming: true
12990 }), 'incomers'),
12991 // aka DAG ancestors
12992 predecessors: defineDagAllHops({
12993 incoming: true
12994 })
12995});
12996
12997// Neighbourhood functions
12998//////////////////////////
12999
13000extend(elesfn$2, {
13001 neighborhood: cache(function (selector) {
13002 var elements = [];
13003 var nodes = this.nodes();
13004 for (var i = 0; i < nodes.length; i++) {
13005 // for all nodes
13006 var node = nodes[i];
13007 var connectedEdges = node.connectedEdges();
13008
13009 // for each connected edge, add the edge and the other node
13010 for (var j = 0; j < connectedEdges.length; j++) {
13011 var edge = connectedEdges[j];
13012 var src = edge.source();
13013 var tgt = edge.target();
13014 var otherNode = node === src ? tgt : src;
13015
13016 // need check in case of loop
13017 if (otherNode.length > 0) {
13018 elements.push(otherNode[0]); // add node 1 hop away
13019 }
13020
13021 // add connected edge
13022 elements.push(edge[0]);
13023 }
13024 }
13025 return this.spawn(elements, true).filter(selector);
13026 }, 'neighborhood'),
13027 closedNeighborhood: function closedNeighborhood(selector) {
13028 return this.neighborhood().add(this).filter(selector);
13029 },
13030 openNeighborhood: function openNeighborhood(selector) {
13031 return this.neighborhood(selector);
13032 }
13033});
13034
13035// aliases
13036elesfn$2.neighbourhood = elesfn$2.neighborhood;
13037elesfn$2.closedNeighbourhood = elesfn$2.closedNeighborhood;
13038elesfn$2.openNeighbourhood = elesfn$2.openNeighborhood;
13039
13040// Edge functions
13041/////////////////
13042
13043extend(elesfn$2, {
13044 source: cache(function sourceImpl(selector) {
13045 var ele = this[0];
13046 var src;
13047 if (ele) {
13048 src = ele._private.source || ele.cy().collection();
13049 }
13050 return src && selector ? src.filter(selector) : src;
13051 }, 'source'),
13052 target: cache(function targetImpl(selector) {
13053 var ele = this[0];
13054 var tgt;
13055 if (ele) {
13056 tgt = ele._private.target || ele.cy().collection();
13057 }
13058 return tgt && selector ? tgt.filter(selector) : tgt;
13059 }, 'target'),
13060 sources: defineSourceFunction({
13061 attr: 'source'
13062 }),
13063 targets: defineSourceFunction({
13064 attr: 'target'
13065 })
13066});
13067function defineSourceFunction(params) {
13068 return function sourceImpl(selector) {
13069 var sources = [];
13070 for (var i = 0; i < this.length; i++) {
13071 var ele = this[i];
13072 var src = ele._private[params.attr];
13073 if (src) {
13074 sources.push(src);
13075 }
13076 }
13077 return this.spawn(sources, true).filter(selector);
13078 };
13079}
13080extend(elesfn$2, {
13081 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
13082 edgesTo: cache(defineEdgesWithFunction({
13083 thisIsSrc: true
13084 }), 'edgesTo')
13085});
13086function defineEdgesWithFunction(params) {
13087 return function edgesWithImpl(otherNodes) {
13088 var elements = [];
13089 var cy = this._private.cy;
13090 var p = params || {};
13091
13092 // get elements if a selector is specified
13093 if (string(otherNodes)) {
13094 otherNodes = cy.$(otherNodes);
13095 }
13096 for (var h = 0; h < otherNodes.length; h++) {
13097 var edges = otherNodes[h]._private.edges;
13098 for (var i = 0; i < edges.length; i++) {
13099 var edge = edges[i];
13100 var edgeData = edge._private.data;
13101 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
13102 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
13103 var edgeConnectsThisAndOther = thisToOther || otherToThis;
13104 if (!edgeConnectsThisAndOther) {
13105 continue;
13106 }
13107 if (p.thisIsSrc || p.thisIsTgt) {
13108 if (p.thisIsSrc && !thisToOther) {
13109 continue;
13110 }
13111 if (p.thisIsTgt && !otherToThis) {
13112 continue;
13113 }
13114 }
13115 elements.push(edge);
13116 }
13117 }
13118 return this.spawn(elements, true);
13119 };
13120}
13121extend(elesfn$2, {
13122 connectedEdges: cache(function (selector) {
13123 var retEles = [];
13124 var eles = this;
13125 for (var i = 0; i < eles.length; i++) {
13126 var node = eles[i];
13127 if (!node.isNode()) {
13128 continue;
13129 }
13130 var edges = node._private.edges;
13131 for (var j = 0; j < edges.length; j++) {
13132 var edge = edges[j];
13133 retEles.push(edge);
13134 }
13135 }
13136 return this.spawn(retEles, true).filter(selector);
13137 }, 'connectedEdges'),
13138 connectedNodes: cache(function (selector) {
13139 var retEles = [];
13140 var eles = this;
13141 for (var i = 0; i < eles.length; i++) {
13142 var edge = eles[i];
13143 if (!edge.isEdge()) {
13144 continue;
13145 }
13146 retEles.push(edge.source()[0]);
13147 retEles.push(edge.target()[0]);
13148 }
13149 return this.spawn(retEles, true).filter(selector);
13150 }, 'connectedNodes'),
13151 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
13152 codirectedEdges: cache(defineParallelEdgesFunction({
13153 codirected: true
13154 }), 'codirectedEdges')
13155});
13156function defineParallelEdgesFunction(params) {
13157 var defaults = {
13158 codirected: false
13159 };
13160 params = extend({}, defaults, params);
13161 return function parallelEdgesImpl(selector) {
13162 // micro-optimised for renderer
13163 var elements = [];
13164 var edges = this.edges();
13165 var p = params;
13166
13167 // look at all the edges in the collection
13168 for (var i = 0; i < edges.length; i++) {
13169 var edge1 = edges[i];
13170 var edge1_p = edge1._private;
13171 var src1 = edge1_p.source;
13172 var srcid1 = src1._private.data.id;
13173 var tgtid1 = edge1_p.data.target;
13174 var srcEdges1 = src1._private.edges;
13175
13176 // look at edges connected to the src node of this edge
13177 for (var j = 0; j < srcEdges1.length; j++) {
13178 var edge2 = srcEdges1[j];
13179 var edge2data = edge2._private.data;
13180 var tgtid2 = edge2data.target;
13181 var srcid2 = edge2data.source;
13182 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
13183 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
13184 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
13185 elements.push(edge2);
13186 }
13187 }
13188 }
13189 return this.spawn(elements, true).filter(selector);
13190 };
13191}
13192
13193// Misc functions
13194/////////////////
13195
13196extend(elesfn$2, {
13197 components: function components(root) {
13198 var self = this;
13199 var cy = self.cy();
13200 var visited = cy.collection();
13201 var unvisited = root == null ? self.nodes() : root.nodes();
13202 var components = [];
13203 if (root != null && unvisited.empty()) {
13204 // root may contain only edges
13205 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
13206 }
13207
13208 var visitInComponent = function visitInComponent(node, component) {
13209 visited.merge(node);
13210 unvisited.unmerge(node);
13211 component.merge(node);
13212 };
13213 if (unvisited.empty()) {
13214 return self.spawn();
13215 }
13216 var _loop = function _loop() {
13217 // each iteration yields a component
13218 var cmpt = cy.collection();
13219 components.push(cmpt);
13220 var root = unvisited[0];
13221 visitInComponent(root, cmpt);
13222 self.bfs({
13223 directed: false,
13224 roots: root,
13225 visit: function visit(v) {
13226 return visitInComponent(v, cmpt);
13227 }
13228 });
13229 cmpt.forEach(function (node) {
13230 node.connectedEdges().forEach(function (e) {
13231 // connectedEdges() usually cached
13232 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
13233 // has() is cheap
13234 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
13235 }
13236 });
13237 });
13238 };
13239 do {
13240 _loop();
13241 } while (unvisited.length > 0);
13242 return components;
13243 },
13244 component: function component() {
13245 var ele = this[0];
13246 return ele.cy().mutableElements().components(ele)[0];
13247 }
13248});
13249elesfn$2.componentsOf = elesfn$2.components;
13250
13251// represents a set of nodes, edges, or both together
13252var Collection = function Collection(cy, elements) {
13253 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13254 var removed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
13255 if (cy === undefined) {
13256 error('A collection must have a reference to the core');
13257 return;
13258 }
13259 var map = new Map$2();
13260 var createdElements = false;
13261 if (!elements) {
13262 elements = [];
13263 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
13264 createdElements = true;
13265
13266 // make elements from json and restore all at once later
13267 var eles = [];
13268 var elesIds = new Set$1();
13269 for (var i = 0, l = elements.length; i < l; i++) {
13270 var json = elements[i];
13271 if (json.data == null) {
13272 json.data = {};
13273 }
13274 var _data = json.data;
13275
13276 // make sure newly created elements have valid ids
13277 if (_data.id == null) {
13278 _data.id = uuid();
13279 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
13280 continue; // can't create element if prior id already exists
13281 }
13282
13283 var ele = new Element(cy, json, false);
13284 eles.push(ele);
13285 elesIds.add(_data.id);
13286 }
13287 elements = eles;
13288 }
13289 this.length = 0;
13290 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
13291 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
13292 if (element$1 == null) {
13293 continue;
13294 }
13295 var id = element$1._private.data.id;
13296 if (!unique || !map.has(id)) {
13297 if (unique) {
13298 map.set(id, {
13299 index: this.length,
13300 ele: element$1
13301 });
13302 }
13303 this[this.length] = element$1;
13304 this.length++;
13305 }
13306 }
13307 this._private = {
13308 eles: this,
13309 cy: cy,
13310 get map() {
13311 if (this.lazyMap == null) {
13312 this.rebuildMap();
13313 }
13314 return this.lazyMap;
13315 },
13316 set map(m) {
13317 this.lazyMap = m;
13318 },
13319 rebuildMap: function rebuildMap() {
13320 var m = this.lazyMap = new Map$2();
13321 var eles = this.eles;
13322 for (var _i2 = 0; _i2 < eles.length; _i2++) {
13323 var _ele = eles[_i2];
13324 m.set(_ele.id(), {
13325 index: _i2,
13326 ele: _ele
13327 });
13328 }
13329 }
13330 };
13331 if (unique) {
13332 this._private.map = map;
13333 }
13334
13335 // restore the elements if we created them from json
13336 if (createdElements && !removed) {
13337 this.restore();
13338 }
13339};
13340
13341// Functions
13342////////////////////////////////////////////////////////////////////////////////////////////////////
13343
13344// keep the prototypes in sync (an element has the same functions as a collection)
13345// and use elefn and elesfn as shorthands to the prototypes
13346var elesfn$1 = Element.prototype = Collection.prototype = Object.create(Array.prototype);
13347elesfn$1.instanceString = function () {
13348 return 'collection';
13349};
13350elesfn$1.spawn = function (eles, unique) {
13351 return new Collection(this.cy(), eles, unique);
13352};
13353elesfn$1.spawnSelf = function () {
13354 return this.spawn(this);
13355};
13356elesfn$1.cy = function () {
13357 return this._private.cy;
13358};
13359elesfn$1.renderer = function () {
13360 return this._private.cy.renderer();
13361};
13362elesfn$1.element = function () {
13363 return this[0];
13364};
13365elesfn$1.collection = function () {
13366 if (collection(this)) {
13367 return this;
13368 } else {
13369 // an element
13370 return new Collection(this._private.cy, [this]);
13371 }
13372};
13373elesfn$1.unique = function () {
13374 return new Collection(this._private.cy, this, true);
13375};
13376elesfn$1.hasElementWithId = function (id) {
13377 id = '' + id; // id must be string
13378
13379 return this._private.map.has(id);
13380};
13381elesfn$1.getElementById = function (id) {
13382 id = '' + id; // id must be string
13383
13384 var cy = this._private.cy;
13385 var entry = this._private.map.get(id);
13386 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
13387};
13388
13389elesfn$1.$id = elesfn$1.getElementById;
13390elesfn$1.poolIndex = function () {
13391 var cy = this._private.cy;
13392 var eles = cy._private.elements;
13393 var id = this[0]._private.data.id;
13394 return eles._private.map.get(id).index;
13395};
13396elesfn$1.indexOf = function (ele) {
13397 var id = ele[0]._private.data.id;
13398 return this._private.map.get(id).index;
13399};
13400elesfn$1.indexOfId = function (id) {
13401 id = '' + id; // id must be string
13402
13403 return this._private.map.get(id).index;
13404};
13405elesfn$1.json = function (obj) {
13406 var ele = this.element();
13407 var cy = this.cy();
13408 if (ele == null && obj) {
13409 return this;
13410 } // can't set to no eles
13411
13412 if (ele == null) {
13413 return undefined;
13414 } // can't get from no eles
13415
13416 var p = ele._private;
13417 if (plainObject(obj)) {
13418 // set
13419
13420 cy.startBatch();
13421 if (obj.data) {
13422 ele.data(obj.data);
13423 var _data2 = p.data;
13424 if (ele.isEdge()) {
13425 // source and target are immutable via data()
13426 var move = false;
13427 var spec = {};
13428 var src = obj.data.source;
13429 var tgt = obj.data.target;
13430 if (src != null && src != _data2.source) {
13431 spec.source = '' + src; // id must be string
13432 move = true;
13433 }
13434 if (tgt != null && tgt != _data2.target) {
13435 spec.target = '' + tgt; // id must be string
13436 move = true;
13437 }
13438 if (move) {
13439 ele = ele.move(spec);
13440 }
13441 } else {
13442 // parent is immutable via data()
13443 var newParentValSpecd = ('parent' in obj.data);
13444 var parent = obj.data.parent;
13445 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
13446 if (parent === undefined) {
13447 // can't set undefined imperatively, so use null
13448 parent = null;
13449 }
13450 if (parent != null) {
13451 parent = '' + parent; // id must be string
13452 }
13453
13454 ele = ele.move({
13455 parent: parent
13456 });
13457 }
13458 }
13459 }
13460 if (obj.position) {
13461 ele.position(obj.position);
13462 }
13463
13464 // ignore group -- immutable
13465
13466 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
13467 var obj_k = obj[k];
13468 if (obj_k != null && obj_k !== p[k]) {
13469 if (obj_k) {
13470 ele[trueFnName]();
13471 } else {
13472 ele[falseFnName]();
13473 }
13474 }
13475 };
13476 checkSwitch('removed', 'remove', 'restore');
13477 checkSwitch('selected', 'select', 'unselect');
13478 checkSwitch('selectable', 'selectify', 'unselectify');
13479 checkSwitch('locked', 'lock', 'unlock');
13480 checkSwitch('grabbable', 'grabify', 'ungrabify');
13481 checkSwitch('pannable', 'panify', 'unpanify');
13482 if (obj.classes != null) {
13483 ele.classes(obj.classes);
13484 }
13485 cy.endBatch();
13486 return this;
13487 } else if (obj === undefined) {
13488 // get
13489
13490 var json = {
13491 data: copy(p.data),
13492 position: copy(p.position),
13493 group: p.group,
13494 removed: p.removed,
13495 selected: p.selected,
13496 selectable: p.selectable,
13497 locked: p.locked,
13498 grabbable: p.grabbable,
13499 pannable: p.pannable,
13500 classes: null
13501 };
13502 json.classes = '';
13503 var i = 0;
13504 p.classes.forEach(function (cls) {
13505 return json.classes += i++ === 0 ? cls : ' ' + cls;
13506 });
13507 return json;
13508 }
13509};
13510elesfn$1.jsons = function () {
13511 var jsons = [];
13512 for (var i = 0; i < this.length; i++) {
13513 var ele = this[i];
13514 var json = ele.json();
13515 jsons.push(json);
13516 }
13517 return jsons;
13518};
13519elesfn$1.clone = function () {
13520 var cy = this.cy();
13521 var elesArr = [];
13522 for (var i = 0; i < this.length; i++) {
13523 var ele = this[i];
13524 var json = ele.json();
13525 var clone = new Element(cy, json, false); // NB no restore
13526
13527 elesArr.push(clone);
13528 }
13529 return new Collection(cy, elesArr);
13530};
13531elesfn$1.copy = elesfn$1.clone;
13532elesfn$1.restore = function () {
13533 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13534 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13535 var self = this;
13536 var cy = self.cy();
13537 var cy_p = cy._private;
13538
13539 // create arrays of nodes and edges, since we need to
13540 // restore the nodes first
13541 var nodes = [];
13542 var edges = [];
13543 var elements;
13544 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
13545 var ele = self[_i3];
13546 if (addToPool && !ele.removed()) {
13547 // don't need to handle this ele
13548 continue;
13549 }
13550
13551 // keep nodes first in the array and edges after
13552 if (ele.isNode()) {
13553 // put to front of array if node
13554 nodes.push(ele);
13555 } else {
13556 // put to end of array if edge
13557 edges.push(ele);
13558 }
13559 }
13560 elements = nodes.concat(edges);
13561 var i;
13562 var removeFromElements = function removeFromElements() {
13563 elements.splice(i, 1);
13564 i--;
13565 };
13566
13567 // now, restore each element
13568 for (i = 0; i < elements.length; i++) {
13569 var _ele2 = elements[i];
13570 var _private = _ele2._private;
13571 var _data3 = _private.data;
13572
13573 // the traversal cache should start fresh when ele is added
13574 _ele2.clearTraversalCache();
13575
13576 // set id and validate
13577 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
13578 _data3.id = uuid();
13579 } else if (number$1(_data3.id)) {
13580 _data3.id = '' + _data3.id; // now it's a string
13581 } else if (emptyString(_data3.id) || !string(_data3.id)) {
13582 error('Can not create element with invalid string ID `' + _data3.id + '`');
13583
13584 // can't create element if it has empty string as id or non-string id
13585 removeFromElements();
13586 continue;
13587 } else if (cy.hasElementWithId(_data3.id)) {
13588 error('Can not create second element with ID `' + _data3.id + '`');
13589
13590 // can't create element if one already has that id
13591 removeFromElements();
13592 continue;
13593 }
13594 var id = _data3.id; // id is finalised, now let's keep a ref
13595
13596 if (_ele2.isNode()) {
13597 // extra checks for nodes
13598 var pos = _private.position;
13599
13600 // make sure the nodes have a defined position
13601
13602 if (pos.x == null) {
13603 pos.x = 0;
13604 }
13605 if (pos.y == null) {
13606 pos.y = 0;
13607 }
13608 }
13609 if (_ele2.isEdge()) {
13610 // extra checks for edges
13611
13612 var edge = _ele2;
13613 var fields = ['source', 'target'];
13614 var fieldsLength = fields.length;
13615 var badSourceOrTarget = false;
13616 for (var j = 0; j < fieldsLength; j++) {
13617 var field = fields[j];
13618 var val = _data3[field];
13619 if (number$1(val)) {
13620 val = _data3[field] = '' + _data3[field]; // now string
13621 }
13622
13623 if (val == null || val === '') {
13624 // can't create if source or target is not defined properly
13625 error('Can not create edge `' + id + '` with unspecified ' + field);
13626 badSourceOrTarget = true;
13627 } else if (!cy.hasElementWithId(val)) {
13628 // can't create edge if one of its nodes doesn't exist
13629 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13630 badSourceOrTarget = true;
13631 }
13632 }
13633 if (badSourceOrTarget) {
13634 removeFromElements();
13635 continue;
13636 } // can't create this
13637
13638 var src = cy.getElementById(_data3.source);
13639 var tgt = cy.getElementById(_data3.target);
13640
13641 // only one edge in node if loop
13642 if (src.same(tgt)) {
13643 src._private.edges.push(edge);
13644 } else {
13645 src._private.edges.push(edge);
13646 tgt._private.edges.push(edge);
13647 }
13648 edge._private.source = src;
13649 edge._private.target = tgt;
13650 } // if is edge
13651
13652 // create mock ids / indexes maps for element so it can be used like collections
13653 _private.map = new Map$2();
13654 _private.map.set(id, {
13655 ele: _ele2,
13656 index: 0
13657 });
13658 _private.removed = false;
13659 if (addToPool) {
13660 cy.addToPool(_ele2);
13661 }
13662 } // for each element
13663
13664 // do compound node sanity checks
13665 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13666 // each node
13667 var node = nodes[_i4];
13668 var _data4 = node._private.data;
13669 if (number$1(_data4.parent)) {
13670 // then automake string
13671 _data4.parent = '' + _data4.parent;
13672 }
13673 var parentId = _data4.parent;
13674 var specifiedParent = parentId != null;
13675 if (specifiedParent || node._private.parent) {
13676 var parent = node._private.parent ? cy.collection().merge(node._private.parent) : cy.getElementById(parentId);
13677 if (parent.empty()) {
13678 // non-existant parent; just remove it
13679 _data4.parent = undefined;
13680 } else if (parent[0].removed()) {
13681 warn('Node added with missing parent, reference to parent removed');
13682 _data4.parent = undefined;
13683 node._private.parent = null;
13684 } else {
13685 var selfAsParent = false;
13686 var ancestor = parent;
13687 while (!ancestor.empty()) {
13688 if (node.same(ancestor)) {
13689 // mark self as parent and remove from data
13690 selfAsParent = true;
13691 _data4.parent = undefined; // remove parent reference
13692
13693 // exit or we loop forever
13694 break;
13695 }
13696 ancestor = ancestor.parent();
13697 }
13698 if (!selfAsParent) {
13699 // connect with children
13700 parent[0]._private.children.push(node);
13701 node._private.parent = parent[0];
13702
13703 // let the core know we have a compound graph
13704 cy_p.hasCompoundNodes = true;
13705 }
13706 } // else
13707 } // if specified parent
13708 } // for each node
13709
13710 if (elements.length > 0) {
13711 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13712 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13713 var _ele3 = restored[_i5];
13714 if (_ele3.isNode()) {
13715 continue;
13716 }
13717
13718 // adding an edge invalidates the traversal caches for the parallel edges
13719 _ele3.parallelEdges().clearTraversalCache();
13720
13721 // adding an edge invalidates the traversal cache for the connected nodes
13722 _ele3.source().clearTraversalCache();
13723 _ele3.target().clearTraversalCache();
13724 }
13725 var toUpdateStyle;
13726 if (cy_p.hasCompoundNodes) {
13727 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13728 } else {
13729 toUpdateStyle = restored;
13730 }
13731 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13732 if (notifyRenderer) {
13733 restored.emitAndNotify('add');
13734 } else if (addToPool) {
13735 restored.emit('add');
13736 }
13737 }
13738 return self; // chainability
13739};
13740
13741elesfn$1.removed = function () {
13742 var ele = this[0];
13743 return ele && ele._private.removed;
13744};
13745elesfn$1.inside = function () {
13746 var ele = this[0];
13747 return ele && !ele._private.removed;
13748};
13749elesfn$1.remove = function () {
13750 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13751 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13752 var self = this;
13753 var elesToRemove = [];
13754 var elesToRemoveIds = {};
13755 var cy = self._private.cy;
13756
13757 // add connected edges
13758 function addConnectedEdges(node) {
13759 var edges = node._private.edges;
13760 for (var i = 0; i < edges.length; i++) {
13761 add(edges[i]);
13762 }
13763 }
13764
13765 // add descendant nodes
13766 function addChildren(node) {
13767 var children = node._private.children;
13768 for (var i = 0; i < children.length; i++) {
13769 add(children[i]);
13770 }
13771 }
13772 function add(ele) {
13773 var alreadyAdded = elesToRemoveIds[ele.id()];
13774 if (removeFromPool && ele.removed() || alreadyAdded) {
13775 return;
13776 } else {
13777 elesToRemoveIds[ele.id()] = true;
13778 }
13779 if (ele.isNode()) {
13780 elesToRemove.push(ele); // nodes are removed last
13781
13782 addConnectedEdges(ele);
13783 addChildren(ele);
13784 } else {
13785 elesToRemove.unshift(ele); // edges are removed first
13786 }
13787 }
13788
13789 // make the list of elements to remove
13790 // (may be removing more than specified due to connected edges etc)
13791
13792 for (var i = 0, l = self.length; i < l; i++) {
13793 var ele = self[i];
13794 add(ele);
13795 }
13796 function removeEdgeRef(node, edge) {
13797 var connectedEdges = node._private.edges;
13798 removeFromArray(connectedEdges, edge);
13799
13800 // removing an edges invalidates the traversal cache for its nodes
13801 node.clearTraversalCache();
13802 }
13803 function removeParallelRef(pllEdge) {
13804 // removing an edge invalidates the traversal caches for the parallel edges
13805 pllEdge.clearTraversalCache();
13806 }
13807 var alteredParents = [];
13808 alteredParents.ids = {};
13809 function removeChildRef(parent, ele) {
13810 ele = ele[0];
13811 parent = parent[0];
13812 var children = parent._private.children;
13813 var pid = parent.id();
13814 removeFromArray(children, ele); // remove parent => child ref
13815
13816 ele._private.parent = null; // remove child => parent ref
13817
13818 if (!alteredParents.ids[pid]) {
13819 alteredParents.ids[pid] = true;
13820 alteredParents.push(parent);
13821 }
13822 }
13823 self.dirtyCompoundBoundsCache();
13824 if (removeFromPool) {
13825 cy.removeFromPool(elesToRemove); // remove from core pool
13826 }
13827
13828 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13829 var _ele4 = elesToRemove[_i6];
13830 if (_ele4.isEdge()) {
13831 // remove references to this edge in its connected nodes
13832 var src = _ele4.source()[0];
13833 var tgt = _ele4.target()[0];
13834 removeEdgeRef(src, _ele4);
13835 removeEdgeRef(tgt, _ele4);
13836 var pllEdges = _ele4.parallelEdges();
13837 for (var j = 0; j < pllEdges.length; j++) {
13838 var pllEdge = pllEdges[j];
13839 removeParallelRef(pllEdge);
13840 if (pllEdge.isBundledBezier()) {
13841 pllEdge.dirtyBoundingBoxCache();
13842 }
13843 }
13844 } else {
13845 // remove reference to parent
13846 var parent = _ele4.parent();
13847 if (parent.length !== 0) {
13848 removeChildRef(parent, _ele4);
13849 }
13850 }
13851 if (removeFromPool) {
13852 // mark as removed
13853 _ele4._private.removed = true;
13854 }
13855 }
13856
13857 // check to see if we have a compound graph or not
13858 var elesStillInside = cy._private.elements;
13859 cy._private.hasCompoundNodes = false;
13860 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13861 var _ele5 = elesStillInside[_i7];
13862 if (_ele5.isParent()) {
13863 cy._private.hasCompoundNodes = true;
13864 break;
13865 }
13866 }
13867 var removedElements = new Collection(this.cy(), elesToRemove);
13868 if (removedElements.size() > 0) {
13869 // must manually notify since trigger won't do this automatically once removed
13870
13871 if (notifyRenderer) {
13872 removedElements.emitAndNotify('remove');
13873 } else if (removeFromPool) {
13874 removedElements.emit('remove');
13875 }
13876 }
13877
13878 // the parents who were modified by the removal need their style updated
13879 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13880 var _ele6 = alteredParents[_i8];
13881 if (!removeFromPool || !_ele6.removed()) {
13882 _ele6.updateStyle();
13883 }
13884 }
13885 return removedElements;
13886};
13887elesfn$1.move = function (struct) {
13888 var cy = this._private.cy;
13889 var eles = this;
13890
13891 // just clean up refs, caches, etc. in the same way as when removing and then restoring
13892 // (our calls to remove/restore do not remove from the graph or make events)
13893 var notifyRenderer = false;
13894 var modifyPool = false;
13895 var toString = function toString(id) {
13896 return id == null ? id : '' + id;
13897 }; // id must be string
13898
13899 if (struct.source !== undefined || struct.target !== undefined) {
13900 var srcId = toString(struct.source);
13901 var tgtId = toString(struct.target);
13902 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13903 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13904 if (srcExists || tgtExists) {
13905 cy.batch(function () {
13906 // avoid duplicate style updates
13907 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13908 eles.emitAndNotify('moveout');
13909 for (var i = 0; i < eles.length; i++) {
13910 var ele = eles[i];
13911 var _data5 = ele._private.data;
13912 if (ele.isEdge()) {
13913 if (srcExists) {
13914 _data5.source = srcId;
13915 }
13916 if (tgtExists) {
13917 _data5.target = tgtId;
13918 }
13919 }
13920 }
13921 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13922 });
13923
13924 eles.emitAndNotify('move');
13925 }
13926 } else if (struct.parent !== undefined) {
13927 // move node to new parent
13928 var parentId = toString(struct.parent);
13929 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13930 if (parentExists) {
13931 var pidToAssign = parentId === null ? undefined : parentId;
13932 cy.batch(function () {
13933 // avoid duplicate style updates
13934 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13935 updated.emitAndNotify('moveout');
13936 for (var i = 0; i < eles.length; i++) {
13937 var ele = eles[i];
13938 var _data6 = ele._private.data;
13939 if (ele.isNode()) {
13940 _data6.parent = pidToAssign;
13941 }
13942 }
13943 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13944 });
13945
13946 eles.emitAndNotify('move');
13947 }
13948 }
13949 return this;
13950};
13951[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) {
13952 extend(elesfn$1, props);
13953});
13954
13955var corefn$9 = {
13956 add: function add(opts) {
13957 var elements;
13958 var cy = this;
13959
13960 // add the elements
13961 if (elementOrCollection(opts)) {
13962 var eles = opts;
13963 if (eles._private.cy === cy) {
13964 // same instance => just restore
13965 elements = eles.restore();
13966 } else {
13967 // otherwise, copy from json
13968 var jsons = [];
13969 for (var i = 0; i < eles.length; i++) {
13970 var ele = eles[i];
13971 jsons.push(ele.json());
13972 }
13973 elements = new Collection(cy, jsons);
13974 }
13975 }
13976
13977 // specify an array of options
13978 else if (array(opts)) {
13979 var _jsons = opts;
13980 elements = new Collection(cy, _jsons);
13981 }
13982
13983 // specify via opts.nodes and opts.edges
13984 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13985 var elesByGroup = opts;
13986 var _jsons2 = [];
13987 var grs = ['nodes', 'edges'];
13988 for (var _i = 0, il = grs.length; _i < il; _i++) {
13989 var group = grs[_i];
13990 var elesArray = elesByGroup[group];
13991 if (array(elesArray)) {
13992 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13993 var json = extend({
13994 group: group
13995 }, elesArray[j]);
13996 _jsons2.push(json);
13997 }
13998 }
13999 }
14000 elements = new Collection(cy, _jsons2);
14001 }
14002
14003 // specify options for one element
14004 else {
14005 var _json = opts;
14006 elements = new Element(cy, _json).collection();
14007 }
14008 return elements;
14009 },
14010 remove: function remove(collection) {
14011 if (elementOrCollection(collection)) ; else if (string(collection)) {
14012 var selector = collection;
14013 collection = this.$(selector);
14014 }
14015 return collection.remove();
14016 }
14017};
14018
14019/* global Float32Array */
14020
14021/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14022function generateCubicBezier(mX1, mY1, mX2, mY2) {
14023 var NEWTON_ITERATIONS = 4,
14024 NEWTON_MIN_SLOPE = 0.001,
14025 SUBDIVISION_PRECISION = 0.0000001,
14026 SUBDIVISION_MAX_ITERATIONS = 10,
14027 kSplineTableSize = 11,
14028 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
14029 float32ArraySupported = typeof Float32Array !== 'undefined';
14030
14031 /* Must contain four arguments. */
14032 if (arguments.length !== 4) {
14033 return false;
14034 }
14035
14036 /* Arguments must be numbers. */
14037 for (var i = 0; i < 4; ++i) {
14038 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
14039 return false;
14040 }
14041 }
14042
14043 /* X values must be in the [0, 1] range. */
14044 mX1 = Math.min(mX1, 1);
14045 mX2 = Math.min(mX2, 1);
14046 mX1 = Math.max(mX1, 0);
14047 mX2 = Math.max(mX2, 0);
14048 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
14049 function A(aA1, aA2) {
14050 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
14051 }
14052 function B(aA1, aA2) {
14053 return 3.0 * aA2 - 6.0 * aA1;
14054 }
14055 function C(aA1) {
14056 return 3.0 * aA1;
14057 }
14058 function calcBezier(aT, aA1, aA2) {
14059 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
14060 }
14061 function getSlope(aT, aA1, aA2) {
14062 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
14063 }
14064 function newtonRaphsonIterate(aX, aGuessT) {
14065 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
14066 var currentSlope = getSlope(aGuessT, mX1, mX2);
14067 if (currentSlope === 0.0) {
14068 return aGuessT;
14069 }
14070 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
14071 aGuessT -= currentX / currentSlope;
14072 }
14073 return aGuessT;
14074 }
14075 function calcSampleValues() {
14076 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
14077 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
14078 }
14079 }
14080 function binarySubdivide(aX, aA, aB) {
14081 var currentX,
14082 currentT,
14083 i = 0;
14084 do {
14085 currentT = aA + (aB - aA) / 2.0;
14086 currentX = calcBezier(currentT, mX1, mX2) - aX;
14087 if (currentX > 0.0) {
14088 aB = currentT;
14089 } else {
14090 aA = currentT;
14091 }
14092 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
14093 return currentT;
14094 }
14095 function getTForX(aX) {
14096 var intervalStart = 0.0,
14097 currentSample = 1,
14098 lastSample = kSplineTableSize - 1;
14099 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
14100 intervalStart += kSampleStepSize;
14101 }
14102 --currentSample;
14103 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
14104 guessForT = intervalStart + dist * kSampleStepSize,
14105 initialSlope = getSlope(guessForT, mX1, mX2);
14106 if (initialSlope >= NEWTON_MIN_SLOPE) {
14107 return newtonRaphsonIterate(aX, guessForT);
14108 } else if (initialSlope === 0.0) {
14109 return guessForT;
14110 } else {
14111 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
14112 }
14113 }
14114 var _precomputed = false;
14115 function precompute() {
14116 _precomputed = true;
14117 if (mX1 !== mY1 || mX2 !== mY2) {
14118 calcSampleValues();
14119 }
14120 }
14121 var f = function f(aX) {
14122 if (!_precomputed) {
14123 precompute();
14124 }
14125 if (mX1 === mY1 && mX2 === mY2) {
14126 return aX;
14127 }
14128 if (aX === 0) {
14129 return 0;
14130 }
14131 if (aX === 1) {
14132 return 1;
14133 }
14134 return calcBezier(getTForX(aX), mY1, mY2);
14135 };
14136 f.getControlPoints = function () {
14137 return [{
14138 x: mX1,
14139 y: mY1
14140 }, {
14141 x: mX2,
14142 y: mY2
14143 }];
14144 };
14145 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
14146 f.toString = function () {
14147 return str;
14148 };
14149 return f;
14150}
14151
14152/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14153/* 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
14154 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
14155var generateSpringRK4 = function () {
14156 function springAccelerationForState(state) {
14157 return -state.tension * state.x - state.friction * state.v;
14158 }
14159 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
14160 var state = {
14161 x: initialState.x + derivative.dx * dt,
14162 v: initialState.v + derivative.dv * dt,
14163 tension: initialState.tension,
14164 friction: initialState.friction
14165 };
14166 return {
14167 dx: state.v,
14168 dv: springAccelerationForState(state)
14169 };
14170 }
14171 function springIntegrateState(state, dt) {
14172 var a = {
14173 dx: state.v,
14174 dv: springAccelerationForState(state)
14175 },
14176 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
14177 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
14178 d = springEvaluateStateWithDerivative(state, dt, c),
14179 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
14180 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
14181 state.x = state.x + dxdt * dt;
14182 state.v = state.v + dvdt * dt;
14183 return state;
14184 }
14185 return function springRK4Factory(tension, friction, duration) {
14186 var initState = {
14187 x: -1,
14188 v: 0,
14189 tension: null,
14190 friction: null
14191 },
14192 path = [0],
14193 time_lapsed = 0,
14194 tolerance = 1 / 10000,
14195 DT = 16 / 1000,
14196 have_duration,
14197 dt,
14198 last_state;
14199 tension = parseFloat(tension) || 500;
14200 friction = parseFloat(friction) || 20;
14201 duration = duration || null;
14202 initState.tension = tension;
14203 initState.friction = friction;
14204 have_duration = duration !== null;
14205
14206 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
14207 if (have_duration) {
14208 /* Run the simulation without a duration. */
14209 time_lapsed = springRK4Factory(tension, friction);
14210 /* Compute the adjusted time delta. */
14211 dt = time_lapsed / duration * DT;
14212 } else {
14213 dt = DT;
14214 }
14215 for (;;) {
14216 /* Next/step function .*/
14217 last_state = springIntegrateState(last_state || initState, dt);
14218 /* Store the position. */
14219 path.push(1 + last_state.x);
14220 time_lapsed += 16;
14221 /* If the change threshold is reached, break. */
14222 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
14223 break;
14224 }
14225 }
14226
14227 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
14228 computed path and returns a snapshot of the position according to a given percentComplete. */
14229 return !have_duration ? time_lapsed : function (percentComplete) {
14230 return path[percentComplete * (path.length - 1) | 0];
14231 };
14232 };
14233}();
14234
14235var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
14236 var bezier = generateCubicBezier(t1, p1, t2, p2);
14237 return function (start, end, percent) {
14238 return start + (end - start) * bezier(percent);
14239 };
14240};
14241var easings = {
14242 'linear': function linear(start, end, percent) {
14243 return start + (end - start) * percent;
14244 },
14245 // default easings
14246 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
14247 'ease-in': cubicBezier(0.42, 0, 1, 1),
14248 'ease-out': cubicBezier(0, 0, 0.58, 1),
14249 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
14250 // sine
14251 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
14252 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
14253 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
14254 // quad
14255 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
14256 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
14257 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
14258 // cubic
14259 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
14260 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
14261 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
14262 // quart
14263 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
14264 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
14265 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
14266 // quint
14267 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
14268 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
14269 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
14270 // expo
14271 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
14272 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
14273 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
14274 // circ
14275 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
14276 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
14277 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
14278 // user param easings...
14279
14280 'spring': function spring(tension, friction, duration) {
14281 if (duration === 0) {
14282 // can't get a spring w/ duration 0
14283 return easings.linear; // duration 0 => jump to end so impl doesn't matter
14284 }
14285
14286 var spring = generateSpringRK4(tension, friction, duration);
14287 return function (start, end, percent) {
14288 return start + (end - start) * spring(percent);
14289 };
14290 },
14291 'cubic-bezier': cubicBezier
14292};
14293
14294function getEasedValue(type, start, end, percent, easingFn) {
14295 if (percent === 1) {
14296 return end;
14297 }
14298 if (start === end) {
14299 return end;
14300 }
14301 var val = easingFn(start, end, percent);
14302 if (type == null) {
14303 return val;
14304 }
14305 if (type.roundValue || type.color) {
14306 val = Math.round(val);
14307 }
14308 if (type.min !== undefined) {
14309 val = Math.max(val, type.min);
14310 }
14311 if (type.max !== undefined) {
14312 val = Math.min(val, type.max);
14313 }
14314 return val;
14315}
14316function getValue(prop, spec) {
14317 if (prop.pfValue != null || prop.value != null) {
14318 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
14319 return prop.pfValue;
14320 } else {
14321 return prop.value;
14322 }
14323 } else {
14324 return prop;
14325 }
14326}
14327function ease(startProp, endProp, percent, easingFn, propSpec) {
14328 var type = propSpec != null ? propSpec.type : null;
14329 if (percent < 0) {
14330 percent = 0;
14331 } else if (percent > 1) {
14332 percent = 1;
14333 }
14334 var start = getValue(startProp, propSpec);
14335 var end = getValue(endProp, propSpec);
14336 if (number$1(start) && number$1(end)) {
14337 return getEasedValue(type, start, end, percent, easingFn);
14338 } else if (array(start) && array(end)) {
14339 var easedArr = [];
14340 for (var i = 0; i < end.length; i++) {
14341 var si = start[i];
14342 var ei = end[i];
14343 if (si != null && ei != null) {
14344 var val = getEasedValue(type, si, ei, percent, easingFn);
14345 easedArr.push(val);
14346 } else {
14347 easedArr.push(ei);
14348 }
14349 }
14350 return easedArr;
14351 }
14352 return undefined;
14353}
14354
14355function step$1(self, ani, now, isCore) {
14356 var isEles = !isCore;
14357 var _p = self._private;
14358 var ani_p = ani._private;
14359 var pEasing = ani_p.easing;
14360 var startTime = ani_p.startTime;
14361 var cy = isCore ? self : self.cy();
14362 var style = cy.style();
14363 if (!ani_p.easingImpl) {
14364 if (pEasing == null) {
14365 // use default
14366 ani_p.easingImpl = easings['linear'];
14367 } else {
14368 // then define w/ name
14369 var easingVals;
14370 if (string(pEasing)) {
14371 var easingProp = style.parse('transition-timing-function', pEasing);
14372 easingVals = easingProp.value;
14373 } else {
14374 // then assume preparsed array
14375 easingVals = pEasing;
14376 }
14377 var name, args;
14378 if (string(easingVals)) {
14379 name = easingVals;
14380 args = [];
14381 } else {
14382 name = easingVals[1];
14383 args = easingVals.slice(2).map(function (n) {
14384 return +n;
14385 });
14386 }
14387 if (args.length > 0) {
14388 // create with args
14389 if (name === 'spring') {
14390 args.push(ani_p.duration); // need duration to generate spring
14391 }
14392
14393 ani_p.easingImpl = easings[name].apply(null, args);
14394 } else {
14395 // static impl by name
14396 ani_p.easingImpl = easings[name];
14397 }
14398 }
14399 }
14400 var easing = ani_p.easingImpl;
14401 var percent;
14402 if (ani_p.duration === 0) {
14403 percent = 1;
14404 } else {
14405 percent = (now - startTime) / ani_p.duration;
14406 }
14407 if (ani_p.applying) {
14408 percent = ani_p.progress;
14409 }
14410 if (percent < 0) {
14411 percent = 0;
14412 } else if (percent > 1) {
14413 percent = 1;
14414 }
14415 if (ani_p.delay == null) {
14416 // then update
14417
14418 var startPos = ani_p.startPosition;
14419 var endPos = ani_p.position;
14420 if (endPos && isEles && !self.locked()) {
14421 var newPos = {};
14422 if (valid(startPos.x, endPos.x)) {
14423 newPos.x = ease(startPos.x, endPos.x, percent, easing);
14424 }
14425 if (valid(startPos.y, endPos.y)) {
14426 newPos.y = ease(startPos.y, endPos.y, percent, easing);
14427 }
14428 self.position(newPos);
14429 }
14430 var startPan = ani_p.startPan;
14431 var endPan = ani_p.pan;
14432 var pan = _p.pan;
14433 var animatingPan = endPan != null && isCore;
14434 if (animatingPan) {
14435 if (valid(startPan.x, endPan.x)) {
14436 pan.x = ease(startPan.x, endPan.x, percent, easing);
14437 }
14438 if (valid(startPan.y, endPan.y)) {
14439 pan.y = ease(startPan.y, endPan.y, percent, easing);
14440 }
14441 self.emit('pan');
14442 }
14443 var startZoom = ani_p.startZoom;
14444 var endZoom = ani_p.zoom;
14445 var animatingZoom = endZoom != null && isCore;
14446 if (animatingZoom) {
14447 if (valid(startZoom, endZoom)) {
14448 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
14449 }
14450 self.emit('zoom');
14451 }
14452 if (animatingPan || animatingZoom) {
14453 self.emit('viewport');
14454 }
14455 var props = ani_p.style;
14456 if (props && props.length > 0 && isEles) {
14457 for (var i = 0; i < props.length; i++) {
14458 var prop = props[i];
14459 var _name = prop.name;
14460 var end = prop;
14461 var start = ani_p.startStyle[_name];
14462 var propSpec = style.properties[start.name];
14463 var easedVal = ease(start, end, percent, easing, propSpec);
14464 style.overrideBypass(self, _name, easedVal);
14465 } // for props
14466
14467 self.emit('style');
14468 } // if
14469 }
14470
14471 ani_p.progress = percent;
14472 return percent;
14473}
14474function valid(start, end) {
14475 if (start == null || end == null) {
14476 return false;
14477 }
14478 if (number$1(start) && number$1(end)) {
14479 return true;
14480 } else if (start && end) {
14481 return true;
14482 }
14483 return false;
14484}
14485
14486function startAnimation(self, ani, now, isCore) {
14487 var ani_p = ani._private;
14488 ani_p.started = true;
14489 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14490}
14491
14492function stepAll(now, cy) {
14493 var eles = cy._private.aniEles;
14494 var doneEles = [];
14495 function stepOne(ele, isCore) {
14496 var _p = ele._private;
14497 var current = _p.animation.current;
14498 var queue = _p.animation.queue;
14499 var ranAnis = false;
14500
14501 // if nothing currently animating, get something from the queue
14502 if (current.length === 0) {
14503 var next = queue.shift();
14504 if (next) {
14505 current.push(next);
14506 }
14507 }
14508 var callbacks = function callbacks(_callbacks) {
14509 for (var j = _callbacks.length - 1; j >= 0; j--) {
14510 var cb = _callbacks[j];
14511 cb();
14512 }
14513 _callbacks.splice(0, _callbacks.length);
14514 };
14515
14516 // step and remove if done
14517 for (var i = current.length - 1; i >= 0; i--) {
14518 var ani = current[i];
14519 var ani_p = ani._private;
14520 if (ani_p.stopped) {
14521 current.splice(i, 1);
14522 ani_p.hooked = false;
14523 ani_p.playing = false;
14524 ani_p.started = false;
14525 callbacks(ani_p.frames);
14526 continue;
14527 }
14528 if (!ani_p.playing && !ani_p.applying) {
14529 continue;
14530 }
14531
14532 // an apply() while playing shouldn't do anything
14533 if (ani_p.playing && ani_p.applying) {
14534 ani_p.applying = false;
14535 }
14536 if (!ani_p.started) {
14537 startAnimation(ele, ani, now);
14538 }
14539 step$1(ele, ani, now, isCore);
14540 if (ani_p.applying) {
14541 ani_p.applying = false;
14542 }
14543 callbacks(ani_p.frames);
14544 if (ani_p.step != null) {
14545 ani_p.step(now);
14546 }
14547 if (ani.completed()) {
14548 current.splice(i, 1);
14549 ani_p.hooked = false;
14550 ani_p.playing = false;
14551 ani_p.started = false;
14552 callbacks(ani_p.completes);
14553 }
14554 ranAnis = true;
14555 }
14556 if (!isCore && current.length === 0 && queue.length === 0) {
14557 doneEles.push(ele);
14558 }
14559 return ranAnis;
14560 } // stepElement
14561
14562 // handle all eles
14563 var ranEleAni = false;
14564 for (var e = 0; e < eles.length; e++) {
14565 var ele = eles[e];
14566 var handledThisEle = stepOne(ele);
14567 ranEleAni = ranEleAni || handledThisEle;
14568 } // each element
14569
14570 var ranCoreAni = stepOne(cy, true);
14571
14572 // notify renderer
14573 if (ranEleAni || ranCoreAni) {
14574 if (eles.length > 0) {
14575 cy.notify('draw', eles);
14576 } else {
14577 cy.notify('draw');
14578 }
14579 }
14580
14581 // remove elements from list of currently animating if its queues are empty
14582 eles.unmerge(doneEles);
14583 cy.emit('step');
14584} // stepAll
14585
14586var corefn$8 = {
14587 // pull in animation functions
14588 animate: define.animate(),
14589 animation: define.animation(),
14590 animated: define.animated(),
14591 clearQueue: define.clearQueue(),
14592 delay: define.delay(),
14593 delayAnimation: define.delayAnimation(),
14594 stop: define.stop(),
14595 addToAnimationPool: function addToAnimationPool(eles) {
14596 var cy = this;
14597 if (!cy.styleEnabled()) {
14598 return;
14599 } // save cycles when no style used
14600
14601 cy._private.aniEles.merge(eles);
14602 },
14603 stopAnimationLoop: function stopAnimationLoop() {
14604 this._private.animationsRunning = false;
14605 },
14606 startAnimationLoop: function startAnimationLoop() {
14607 var cy = this;
14608 cy._private.animationsRunning = true;
14609 if (!cy.styleEnabled()) {
14610 return;
14611 } // save cycles when no style used
14612
14613 // NB the animation loop will exec in headless environments if style enabled
14614 // and explicit cy.destroy() is necessary to stop the loop
14615
14616 function headlessStep() {
14617 if (!cy._private.animationsRunning) {
14618 return;
14619 }
14620 requestAnimationFrame(function animationStep(now) {
14621 stepAll(now, cy);
14622 headlessStep();
14623 });
14624 }
14625 var renderer = cy.renderer();
14626 if (renderer && renderer.beforeRender) {
14627 // let the renderer schedule animations
14628 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14629 stepAll(now, cy);
14630 }, renderer.beforeRenderPriorities.animations);
14631 } else {
14632 // manage the animation loop ourselves
14633 headlessStep(); // first call
14634 }
14635 }
14636};
14637
14638var emitterOptions = {
14639 qualifierCompare: function qualifierCompare(selector1, selector2) {
14640 if (selector1 == null || selector2 == null) {
14641 return selector1 == null && selector2 == null;
14642 } else {
14643 return selector1.sameText(selector2);
14644 }
14645 },
14646 eventMatches: function eventMatches(cy, listener, eventObj) {
14647 var selector = listener.qualifier;
14648 if (selector != null) {
14649 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14650 }
14651 return true;
14652 },
14653 addEventFields: function addEventFields(cy, evt) {
14654 evt.cy = cy;
14655 evt.target = cy;
14656 },
14657 callbackContext: function callbackContext(cy, listener, eventObj) {
14658 return listener.qualifier != null ? eventObj.target : cy;
14659 }
14660};
14661var argSelector = function argSelector(arg) {
14662 if (string(arg)) {
14663 return new Selector(arg);
14664 } else {
14665 return arg;
14666 }
14667};
14668var elesfn = {
14669 createEmitter: function createEmitter() {
14670 var _p = this._private;
14671 if (!_p.emitter) {
14672 _p.emitter = new Emitter(emitterOptions, this);
14673 }
14674 return this;
14675 },
14676 emitter: function emitter() {
14677 return this._private.emitter;
14678 },
14679 on: function on(events, selector, callback) {
14680 this.emitter().on(events, argSelector(selector), callback);
14681 return this;
14682 },
14683 removeListener: function removeListener(events, selector, callback) {
14684 this.emitter().removeListener(events, argSelector(selector), callback);
14685 return this;
14686 },
14687 removeAllListeners: function removeAllListeners() {
14688 this.emitter().removeAllListeners();
14689 return this;
14690 },
14691 one: function one(events, selector, callback) {
14692 this.emitter().one(events, argSelector(selector), callback);
14693 return this;
14694 },
14695 once: function once(events, selector, callback) {
14696 this.emitter().one(events, argSelector(selector), callback);
14697 return this;
14698 },
14699 emit: function emit(events, extraParams) {
14700 this.emitter().emit(events, extraParams);
14701 return this;
14702 },
14703 emitAndNotify: function emitAndNotify(event, eles) {
14704 this.emit(event);
14705 this.notify(event, eles);
14706 return this;
14707 }
14708};
14709define.eventAliasesOn(elesfn);
14710
14711var corefn$7 = {
14712 png: function png(options) {
14713 var renderer = this._private.renderer;
14714 options = options || {};
14715 return renderer.png(options);
14716 },
14717 jpg: function jpg(options) {
14718 var renderer = this._private.renderer;
14719 options = options || {};
14720 options.bg = options.bg || '#fff';
14721 return renderer.jpg(options);
14722 }
14723};
14724corefn$7.jpeg = corefn$7.jpg;
14725
14726var corefn$6 = {
14727 layout: function layout(options) {
14728 var cy = this;
14729 if (options == null) {
14730 error('Layout options must be specified to make a layout');
14731 return;
14732 }
14733 if (options.name == null) {
14734 error('A `name` must be specified to make a layout');
14735 return;
14736 }
14737 var name = options.name;
14738 var Layout = cy.extension('layout', name);
14739 if (Layout == null) {
14740 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14741 return;
14742 }
14743 var eles;
14744 if (string(options.eles)) {
14745 eles = cy.$(options.eles);
14746 } else {
14747 eles = options.eles != null ? options.eles : cy.$();
14748 }
14749 var layout = new Layout(extend({}, options, {
14750 cy: cy,
14751 eles: eles
14752 }));
14753 return layout;
14754 }
14755};
14756corefn$6.createLayout = corefn$6.makeLayout = corefn$6.layout;
14757
14758var corefn$5 = {
14759 notify: function notify(eventName, eventEles) {
14760 var _p = this._private;
14761 if (this.batching()) {
14762 _p.batchNotifications = _p.batchNotifications || {};
14763 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14764 if (eventEles != null) {
14765 eles.merge(eventEles);
14766 }
14767 return; // notifications are disabled during batching
14768 }
14769
14770 if (!_p.notificationsEnabled) {
14771 return;
14772 } // exit on disabled
14773
14774 var renderer = this.renderer();
14775
14776 // exit if destroy() called on core or renderer in between frames #1499 #1528
14777 if (this.destroyed() || !renderer) {
14778 return;
14779 }
14780 renderer.notify(eventName, eventEles);
14781 },
14782 notifications: function notifications(bool) {
14783 var p = this._private;
14784 if (bool === undefined) {
14785 return p.notificationsEnabled;
14786 } else {
14787 p.notificationsEnabled = bool ? true : false;
14788 }
14789 return this;
14790 },
14791 noNotifications: function noNotifications(callback) {
14792 this.notifications(false);
14793 callback();
14794 this.notifications(true);
14795 },
14796 batching: function batching() {
14797 return this._private.batchCount > 0;
14798 },
14799 startBatch: function startBatch() {
14800 var _p = this._private;
14801 if (_p.batchCount == null) {
14802 _p.batchCount = 0;
14803 }
14804 if (_p.batchCount === 0) {
14805 _p.batchStyleEles = this.collection();
14806 _p.batchNotifications = {};
14807 }
14808 _p.batchCount++;
14809 return this;
14810 },
14811 endBatch: function endBatch() {
14812 var _p = this._private;
14813 if (_p.batchCount === 0) {
14814 return this;
14815 }
14816 _p.batchCount--;
14817 if (_p.batchCount === 0) {
14818 // update style for dirty eles
14819 _p.batchStyleEles.updateStyle();
14820 var renderer = this.renderer();
14821
14822 // notify the renderer of queued eles and event types
14823 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14824 var eles = _p.batchNotifications[eventName];
14825 if (eles.empty()) {
14826 renderer.notify(eventName);
14827 } else {
14828 renderer.notify(eventName, eles);
14829 }
14830 });
14831 }
14832 return this;
14833 },
14834 batch: function batch(callback) {
14835 this.startBatch();
14836 callback();
14837 this.endBatch();
14838 return this;
14839 },
14840 // for backwards compatibility
14841 batchData: function batchData(map) {
14842 var cy = this;
14843 return this.batch(function () {
14844 var ids = Object.keys(map);
14845 for (var i = 0; i < ids.length; i++) {
14846 var id = ids[i];
14847 var data = map[id];
14848 var ele = cy.getElementById(id);
14849 ele.data(data);
14850 }
14851 });
14852 }
14853};
14854
14855var rendererDefaults = defaults$g({
14856 hideEdgesOnViewport: false,
14857 textureOnViewport: false,
14858 motionBlur: false,
14859 motionBlurOpacity: 0.05,
14860 pixelRatio: undefined,
14861 desktopTapThreshold: 4,
14862 touchTapThreshold: 8,
14863 wheelSensitivity: 1,
14864 debug: false,
14865 showFps: false
14866});
14867var corefn$4 = {
14868 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14869 var r = this._private.renderer;
14870 r.renderTo(context, zoom, pan, pxRatio);
14871 return this;
14872 },
14873 renderer: function renderer() {
14874 return this._private.renderer;
14875 },
14876 forceRender: function forceRender() {
14877 this.notify('draw');
14878 return this;
14879 },
14880 resize: function resize() {
14881 this.invalidateSize();
14882 this.emitAndNotify('resize');
14883 return this;
14884 },
14885 initRenderer: function initRenderer(options) {
14886 var cy = this;
14887 var RendererProto = cy.extension('renderer', options.name);
14888 if (RendererProto == null) {
14889 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14890 return;
14891 }
14892 if (options.wheelSensitivity !== undefined) {
14893 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.");
14894 }
14895 var rOpts = rendererDefaults(options);
14896 rOpts.cy = cy;
14897 cy._private.renderer = new RendererProto(rOpts);
14898 this.notify('init');
14899 },
14900 destroyRenderer: function destroyRenderer() {
14901 var cy = this;
14902 cy.notify('destroy'); // destroy the renderer
14903
14904 var domEle = cy.container();
14905 if (domEle) {
14906 domEle._cyreg = null;
14907 while (domEle.childNodes.length > 0) {
14908 domEle.removeChild(domEle.childNodes[0]);
14909 }
14910 }
14911 cy._private.renderer = null; // to be extra safe, remove the ref
14912 cy.mutableElements().forEach(function (ele) {
14913 var _p = ele._private;
14914 _p.rscratch = {};
14915 _p.rstyle = {};
14916 _p.animation.current = [];
14917 _p.animation.queue = [];
14918 });
14919 },
14920 onRender: function onRender(fn) {
14921 return this.on('render', fn);
14922 },
14923 offRender: function offRender(fn) {
14924 return this.off('render', fn);
14925 }
14926};
14927corefn$4.invalidateDimensions = corefn$4.resize;
14928
14929var corefn$3 = {
14930 // get a collection
14931 // - empty collection on no args
14932 // - collection of elements in the graph on selector arg
14933 // - guarantee a returned collection when elements or collection specified
14934 collection: function collection(eles, opts) {
14935 if (string(eles)) {
14936 return this.$(eles);
14937 } else if (elementOrCollection(eles)) {
14938 return eles.collection();
14939 } else if (array(eles)) {
14940 if (!opts) {
14941 opts = {};
14942 }
14943 return new Collection(this, eles, opts.unique, opts.removed);
14944 }
14945 return new Collection(this);
14946 },
14947 nodes: function nodes(selector) {
14948 var nodes = this.$(function (ele) {
14949 return ele.isNode();
14950 });
14951 if (selector) {
14952 return nodes.filter(selector);
14953 }
14954 return nodes;
14955 },
14956 edges: function edges(selector) {
14957 var edges = this.$(function (ele) {
14958 return ele.isEdge();
14959 });
14960 if (selector) {
14961 return edges.filter(selector);
14962 }
14963 return edges;
14964 },
14965 // search the graph like jQuery
14966 $: function $(selector) {
14967 var eles = this._private.elements;
14968 if (selector) {
14969 return eles.filter(selector);
14970 } else {
14971 return eles.spawnSelf();
14972 }
14973 },
14974 mutableElements: function mutableElements() {
14975 return this._private.elements;
14976 }
14977};
14978
14979// aliases
14980corefn$3.elements = corefn$3.filter = corefn$3.$;
14981
14982var styfn$8 = {};
14983
14984// keys for style blocks, e.g. ttfftt
14985var TRUE = 't';
14986var FALSE = 'f';
14987
14988// (potentially expensive calculation)
14989// apply the style to the element based on
14990// - its bypass
14991// - what selectors match it
14992styfn$8.apply = function (eles) {
14993 var self = this;
14994 var _p = self._private;
14995 var cy = _p.cy;
14996 var updatedEles = cy.collection();
14997 for (var ie = 0; ie < eles.length; ie++) {
14998 var ele = eles[ie];
14999 var cxtMeta = self.getContextMeta(ele);
15000 if (cxtMeta.empty) {
15001 continue;
15002 }
15003 var cxtStyle = self.getContextStyle(cxtMeta);
15004 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
15005 if (ele._private.appliedInitStyle) {
15006 self.updateTransitions(ele, app.diffProps);
15007 } else {
15008 ele._private.appliedInitStyle = true;
15009 }
15010 var hintsDiff = self.updateStyleHints(ele);
15011 if (hintsDiff) {
15012 updatedEles.push(ele);
15013 }
15014 } // for elements
15015
15016 return updatedEles;
15017};
15018styfn$8.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
15019 var self = this;
15020 var cache = self._private.propDiffs = self._private.propDiffs || {};
15021 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
15022 var cachedVal = cache[dualCxtKey];
15023 if (cachedVal) {
15024 return cachedVal;
15025 }
15026 var diffProps = [];
15027 var addedProp = {};
15028 for (var i = 0; i < self.length; i++) {
15029 var cxt = self[i];
15030 var oldHasCxt = oldCxtKey[i] === TRUE;
15031 var newHasCxt = newCxtKey[i] === TRUE;
15032 var cxtHasDiffed = oldHasCxt !== newHasCxt;
15033 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
15034 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
15035 var props = void 0;
15036 if (cxtHasDiffed && cxtHasMappedProps) {
15037 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
15038 } else if (cxtHasDiffed) {
15039 props = cxt.properties; // need to check them all
15040 } else if (cxtHasMappedProps) {
15041 props = cxt.mappedProperties; // only need to check mapped
15042 }
15043
15044 for (var j = 0; j < props.length; j++) {
15045 var prop = props[j];
15046 var name = prop.name;
15047
15048 // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
15049 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
15050 // is cached)
15051 var laterCxtOverrides = false;
15052 for (var k = i + 1; k < self.length; k++) {
15053 var laterCxt = self[k];
15054 var hasLaterCxt = newCxtKey[k] === TRUE;
15055 if (!hasLaterCxt) {
15056 continue;
15057 } // can't override unless the context is active
15058
15059 laterCxtOverrides = laterCxt.properties[prop.name] != null;
15060 if (laterCxtOverrides) {
15061 break;
15062 } // exit early as long as one later context overrides
15063 }
15064
15065 if (!addedProp[name] && !laterCxtOverrides) {
15066 addedProp[name] = true;
15067 diffProps.push(name);
15068 }
15069 } // for props
15070 } // if
15071 } // for contexts
15072
15073 cache[dualCxtKey] = diffProps;
15074 return diffProps;
15075};
15076styfn$8.getContextMeta = function (ele) {
15077 var self = this;
15078 var cxtKey = '';
15079 var diffProps;
15080 var prevKey = ele._private.styleCxtKey || '';
15081
15082 // get the cxt key
15083 for (var i = 0; i < self.length; i++) {
15084 var context = self[i];
15085 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
15086
15087 if (contextSelectorMatches) {
15088 cxtKey += TRUE;
15089 } else {
15090 cxtKey += FALSE;
15091 }
15092 } // for context
15093
15094 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
15095 ele._private.styleCxtKey = cxtKey;
15096 return {
15097 key: cxtKey,
15098 diffPropNames: diffProps,
15099 empty: diffProps.length === 0
15100 };
15101};
15102
15103// gets a computed ele style object based on matched contexts
15104styfn$8.getContextStyle = function (cxtMeta) {
15105 var cxtKey = cxtMeta.key;
15106 var self = this;
15107 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {};
15108
15109 // if already computed style, returned cached copy
15110 if (cxtStyles[cxtKey]) {
15111 return cxtStyles[cxtKey];
15112 }
15113 var style = {
15114 _private: {
15115 key: cxtKey
15116 }
15117 };
15118 for (var i = 0; i < self.length; i++) {
15119 var cxt = self[i];
15120 var hasCxt = cxtKey[i] === TRUE;
15121 if (!hasCxt) {
15122 continue;
15123 }
15124 for (var j = 0; j < cxt.properties.length; j++) {
15125 var prop = cxt.properties[j];
15126 style[prop.name] = prop;
15127 }
15128 }
15129 cxtStyles[cxtKey] = style;
15130 return style;
15131};
15132styfn$8.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
15133 var self = this;
15134 var diffProps = cxtMeta.diffPropNames;
15135 var retDiffProps = {};
15136 var types = self.types;
15137 for (var i = 0; i < diffProps.length; i++) {
15138 var diffPropName = diffProps[i];
15139 var cxtProp = cxtStyle[diffPropName];
15140 var eleProp = ele.pstyle(diffPropName);
15141 if (!cxtProp) {
15142 // no context prop means delete
15143 if (!eleProp) {
15144 continue; // no existing prop means nothing needs to be removed
15145 // nb affects initial application on mapped values like control-point-distances
15146 } else if (eleProp.bypass) {
15147 cxtProp = {
15148 name: diffPropName,
15149 deleteBypassed: true
15150 };
15151 } else {
15152 cxtProp = {
15153 name: diffPropName,
15154 "delete": true
15155 };
15156 }
15157 }
15158
15159 // save cycles when the context prop doesn't need to be applied
15160 if (eleProp === cxtProp) {
15161 continue;
15162 }
15163
15164 // save cycles when a mapped context prop doesn't need to be applied
15165 if (cxtProp.mapped === types.fn // context prop is function mapper
15166 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
15167 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
15168 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
15169 ) {
15170 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
15171 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
15172 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
15173
15174 if (fnValue === mapping.prevFnValue) {
15175 continue;
15176 }
15177 }
15178 var retDiffProp = retDiffProps[diffPropName] = {
15179 prev: eleProp
15180 };
15181 self.applyParsedProperty(ele, cxtProp);
15182 retDiffProp.next = ele.pstyle(diffPropName);
15183 if (retDiffProp.next && retDiffProp.next.bypass) {
15184 retDiffProp.next = retDiffProp.next.bypassed;
15185 }
15186 }
15187 return {
15188 diffProps: retDiffProps
15189 };
15190};
15191styfn$8.updateStyleHints = function (ele) {
15192 var _p = ele._private;
15193 var self = this;
15194 var propNames = self.propertyGroupNames;
15195 var propGrKeys = self.propertyGroupKeys;
15196 var propHash = function propHash(ele, propNames, seedKey) {
15197 return self.getPropertiesHash(ele, propNames, seedKey);
15198 };
15199 var oldStyleKey = _p.styleKey;
15200 if (ele.removed()) {
15201 return false;
15202 }
15203 var isNode = _p.group === 'nodes';
15204
15205 // get the style key hashes per prop group
15206 // but lazily -- only use non-default prop values to reduce the number of hashes
15207 //
15208
15209 var overriddenStyles = ele._private.style;
15210 propNames = Object.keys(overriddenStyles);
15211 for (var i = 0; i < propGrKeys.length; i++) {
15212 var grKey = propGrKeys[i];
15213 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15214 }
15215 var updateGrKey1 = function updateGrKey1(val, grKey) {
15216 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
15217 };
15218 var updateGrKey2 = function updateGrKey2(val, grKey) {
15219 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
15220 };
15221 var updateGrKey = function updateGrKey(val, grKey) {
15222 updateGrKey1(val, grKey);
15223 updateGrKey2(val, grKey);
15224 };
15225 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
15226 for (var j = 0; j < strVal.length; j++) {
15227 var ch = strVal.charCodeAt(j);
15228 updateGrKey1(ch, grKey);
15229 updateGrKey2(ch, grKey);
15230 }
15231 };
15232
15233 // - hashing works on 32 bit ints b/c we use bitwise ops
15234 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
15235 // - raise up small numbers so more significant digits are seen by hashing
15236 // - make small numbers larger than a normal value to avoid collisions
15237 // - works in practice and it's relatively cheap
15238 var N = 2000000000;
15239 var cleanNum = function cleanNum(val) {
15240 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
15241 };
15242 for (var _i = 0; _i < propNames.length; _i++) {
15243 var name = propNames[_i];
15244 var parsedProp = overriddenStyles[name];
15245 if (parsedProp == null) {
15246 continue;
15247 }
15248 var propInfo = this.properties[name];
15249 var type = propInfo.type;
15250 var _grKey = propInfo.groupKey;
15251 var normalizedNumberVal = void 0;
15252 if (propInfo.hashOverride != null) {
15253 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
15254 } else if (parsedProp.pfValue != null) {
15255 normalizedNumberVal = parsedProp.pfValue;
15256 }
15257
15258 // might not be a number if it allows enums
15259 var numberVal = propInfo.enums == null ? parsedProp.value : null;
15260 var haveNormNum = normalizedNumberVal != null;
15261 var haveUnitedNum = numberVal != null;
15262 var haveNum = haveNormNum || haveUnitedNum;
15263 var units = parsedProp.units;
15264
15265 // numbers are cheaper to hash than strings
15266 // 1 hash op vs n hash ops (for length n string)
15267 if (type.number && haveNum && !type.multiple) {
15268 var v = haveNormNum ? normalizedNumberVal : numberVal;
15269 updateGrKey(cleanNum(v), _grKey);
15270 if (!haveNormNum && units != null) {
15271 updateGrKeyWStr(units, _grKey);
15272 }
15273 } else {
15274 updateGrKeyWStr(parsedProp.strValue, _grKey);
15275 }
15276 }
15277
15278 // overall style key
15279 //
15280
15281 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15282 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
15283 var _grKey2 = propGrKeys[_i2];
15284 var grHash = _p.styleKeys[_grKey2];
15285 hash[0] = hashInt(grHash[0], hash[0]);
15286 hash[1] = hashIntAlt(grHash[1], hash[1]);
15287 }
15288 _p.styleKey = combineHashes(hash[0], hash[1]);
15289
15290 // label dims
15291 //
15292
15293 var sk = _p.styleKeys;
15294 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
15295 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
15296 _p.labelKey = combineHashesArray(labelKeys);
15297 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
15298 if (!isNode) {
15299 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
15300 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
15301 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
15302 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
15303 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
15304 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
15305 }
15306
15307 // node
15308 //
15309
15310 if (isNode) {
15311 var _p$styleKeys = _p.styleKeys,
15312 nodeBody = _p$styleKeys.nodeBody,
15313 nodeBorder = _p$styleKeys.nodeBorder,
15314 nodeOutline = _p$styleKeys.nodeOutline,
15315 backgroundImage = _p$styleKeys.backgroundImage,
15316 compound = _p$styleKeys.compound,
15317 pie = _p$styleKeys.pie;
15318 var nodeKeys = [nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie].filter(function (k) {
15319 return k != null;
15320 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
15321 _p.nodeKey = combineHashesArray(nodeKeys);
15322 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
15323 }
15324 return oldStyleKey !== _p.styleKey;
15325};
15326styfn$8.clearStyleHints = function (ele) {
15327 var _p = ele._private;
15328 _p.styleCxtKey = '';
15329 _p.styleKeys = {};
15330 _p.styleKey = null;
15331 _p.labelKey = null;
15332 _p.labelStyleKey = null;
15333 _p.sourceLabelKey = null;
15334 _p.sourceLabelStyleKey = null;
15335 _p.targetLabelKey = null;
15336 _p.targetLabelStyleKey = null;
15337 _p.nodeKey = null;
15338 _p.hasPie = null;
15339};
15340
15341// apply a property to the style (for internal use)
15342// returns whether application was successful
15343//
15344// now, this function flattens the property, and here's how:
15345//
15346// for parsedProp:{ bypass: true, deleteBypass: true }
15347// no property is generated, instead the bypass property in the
15348// element's style is replaced by what's pointed to by the `bypassed`
15349// field in the bypass property (i.e. restoring the property the
15350// bypass was overriding)
15351//
15352// for parsedProp:{ mapped: truthy }
15353// the generated flattenedProp:{ mapping: prop }
15354//
15355// for parsedProp:{ bypass: true }
15356// the generated flattenedProp:{ bypassed: parsedProp }
15357styfn$8.applyParsedProperty = function (ele, parsedProp) {
15358 var self = this;
15359 var prop = parsedProp;
15360 var style = ele._private.style;
15361 var flatProp;
15362 var types = self.types;
15363 var type = self.properties[prop.name].type;
15364 var propIsBypass = prop.bypass;
15365 var origProp = style[prop.name];
15366 var origPropIsBypass = origProp && origProp.bypass;
15367 var _p = ele._private;
15368 var flatPropMapping = 'mapping';
15369 var getVal = function getVal(p) {
15370 if (p == null) {
15371 return null;
15372 } else if (p.pfValue != null) {
15373 return p.pfValue;
15374 } else {
15375 return p.value;
15376 }
15377 };
15378 var checkTriggers = function checkTriggers() {
15379 var fromVal = getVal(origProp);
15380 var toVal = getVal(prop);
15381 self.checkTriggers(ele, prop.name, fromVal, toVal);
15382 };
15383
15384 // edge sanity checks to prevent the client from making serious mistakes
15385 if (parsedProp.name === 'curve-style' && ele.isEdge() && (
15386 // loops must be bundled beziers
15387 parsedProp.value !== 'bezier' && ele.isLoop() ||
15388 // edges connected to compound nodes can not be haystacks
15389 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15390 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15391 }
15392 if (prop["delete"]) {
15393 // delete the property and use the default value on falsey value
15394 style[prop.name] = undefined;
15395 checkTriggers();
15396 return true;
15397 }
15398 if (prop.deleteBypassed) {
15399 // delete the property that the
15400 if (!origProp) {
15401 checkTriggers();
15402 return true; // can't delete if no prop
15403 } else if (origProp.bypass) {
15404 // delete bypassed
15405 origProp.bypassed = undefined;
15406 checkTriggers();
15407 return true;
15408 } else {
15409 return false; // we're unsuccessful deleting the bypassed
15410 }
15411 }
15412
15413 // check if we need to delete the current bypass
15414 if (prop.deleteBypass) {
15415 // then this property is just here to indicate we need to delete
15416 if (!origProp) {
15417 checkTriggers();
15418 return true; // property is already not defined
15419 } else if (origProp.bypass) {
15420 // then replace the bypass property with the original
15421 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15422 style[prop.name] = origProp.bypassed;
15423 checkTriggers();
15424 return true;
15425 } else {
15426 return false; // we're unsuccessful deleting the bypass
15427 }
15428 }
15429
15430 var printMappingErr = function printMappingErr() {
15431 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');
15432 };
15433
15434 // put the property in the style objects
15435 switch (prop.mapped) {
15436 // flatten the property if mapped
15437 case types.mapData:
15438 {
15439 // flatten the field (e.g. data.foo.bar)
15440 var fields = prop.field.split('.');
15441 var fieldVal = _p.data;
15442 for (var i = 0; i < fields.length && fieldVal; i++) {
15443 var field = fields[i];
15444 fieldVal = fieldVal[field];
15445 }
15446 if (fieldVal == null) {
15447 printMappingErr();
15448 return false;
15449 }
15450 var percent;
15451 if (!number$1(fieldVal)) {
15452 // then don't apply and fall back on the existing style
15453 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15454 return false;
15455 } else {
15456 var fieldWidth = prop.fieldMax - prop.fieldMin;
15457 if (fieldWidth === 0) {
15458 // safety check -- not strictly necessary as no props of zero range should be passed here
15459 percent = 0;
15460 } else {
15461 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15462 }
15463 }
15464
15465 // make sure to bound percent value
15466 if (percent < 0) {
15467 percent = 0;
15468 } else if (percent > 1) {
15469 percent = 1;
15470 }
15471 if (type.color) {
15472 var r1 = prop.valueMin[0];
15473 var r2 = prop.valueMax[0];
15474 var g1 = prop.valueMin[1];
15475 var g2 = prop.valueMax[1];
15476 var b1 = prop.valueMin[2];
15477 var b2 = prop.valueMax[2];
15478 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15479 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15480 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)];
15481 flatProp = {
15482 // colours are simple, so just create the flat property instead of expensive string parsing
15483 bypass: prop.bypass,
15484 // we're a bypass if the mapping property is a bypass
15485 name: prop.name,
15486 value: clr,
15487 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15488 };
15489 } else if (type.number) {
15490 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15491 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15492 } else {
15493 return false; // can only map to colours and numbers
15494 }
15495
15496 if (!flatProp) {
15497 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15498 printMappingErr();
15499 return false;
15500 }
15501 flatProp.mapping = prop; // keep a reference to the mapping
15502 prop = flatProp; // the flattened (mapped) property is the one we want
15503
15504 break;
15505 }
15506
15507 // direct mapping
15508 case types.data:
15509 {
15510 // flatten the field (e.g. data.foo.bar)
15511 var _fields = prop.field.split('.');
15512 var _fieldVal = _p.data;
15513 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15514 var _field = _fields[_i3];
15515 _fieldVal = _fieldVal[_field];
15516 }
15517 if (_fieldVal != null) {
15518 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15519 }
15520 if (!flatProp) {
15521 // if we can't flatten the property, then don't apply and fall back on the existing style
15522 printMappingErr();
15523 return false;
15524 }
15525 flatProp.mapping = prop; // keep a reference to the mapping
15526 prop = flatProp; // the flattened (mapped) property is the one we want
15527
15528 break;
15529 }
15530 case types.fn:
15531 {
15532 var fn = prop.value;
15533 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15534
15535 prop.prevFnValue = fnRetVal;
15536 if (fnRetVal == null) {
15537 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15538 return false;
15539 }
15540 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15541 if (!flatProp) {
15542 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15543 return false;
15544 }
15545 flatProp.mapping = copy(prop); // keep a reference to the mapping
15546 prop = flatProp; // the flattened (mapped) property is the one we want
15547
15548 break;
15549 }
15550 case undefined:
15551 break;
15552 // just set the property
15553
15554 default:
15555 return false;
15556 // not a valid mapping
15557 }
15558
15559 // if the property is a bypass property, then link the resultant property to the original one
15560 if (propIsBypass) {
15561 if (origPropIsBypass) {
15562 // then this bypass overrides the existing one
15563 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15564 } else {
15565 // then link the orig prop to the new bypass
15566 prop.bypassed = origProp;
15567 }
15568 style[prop.name] = prop; // and set
15569 } else {
15570 // prop is not bypass
15571 if (origPropIsBypass) {
15572 // then keep the orig prop (since it's a bypass) and link to the new prop
15573 origProp.bypassed = prop;
15574 } else {
15575 // then just replace the old prop with the new one
15576 style[prop.name] = prop;
15577 }
15578 }
15579 checkTriggers();
15580 return true;
15581};
15582styfn$8.cleanElements = function (eles, keepBypasses) {
15583 for (var i = 0; i < eles.length; i++) {
15584 var ele = eles[i];
15585 this.clearStyleHints(ele);
15586 ele.dirtyCompoundBoundsCache();
15587 ele.dirtyBoundingBoxCache();
15588 if (!keepBypasses) {
15589 ele._private.style = {};
15590 } else {
15591 var style = ele._private.style;
15592 var propNames = Object.keys(style);
15593 for (var j = 0; j < propNames.length; j++) {
15594 var propName = propNames[j];
15595 var eleProp = style[propName];
15596 if (eleProp != null) {
15597 if (eleProp.bypass) {
15598 eleProp.bypassed = null;
15599 } else {
15600 style[propName] = null;
15601 }
15602 }
15603 }
15604 }
15605 }
15606};
15607
15608// updates the visual style for all elements (useful for manual style modification after init)
15609styfn$8.update = function () {
15610 var cy = this._private.cy;
15611 var eles = cy.mutableElements();
15612 eles.updateStyle();
15613};
15614
15615// diffProps : { name => { prev, next } }
15616styfn$8.updateTransitions = function (ele, diffProps) {
15617 var self = this;
15618 var _p = ele._private;
15619 var props = ele.pstyle('transition-property').value;
15620 var duration = ele.pstyle('transition-duration').pfValue;
15621 var delay = ele.pstyle('transition-delay').pfValue;
15622 if (props.length > 0 && duration > 0) {
15623 var style = {};
15624
15625 // build up the style to animate towards
15626 var anyPrev = false;
15627 for (var i = 0; i < props.length; i++) {
15628 var prop = props[i];
15629 var styProp = ele.pstyle(prop);
15630 var diffProp = diffProps[prop];
15631 if (!diffProp) {
15632 continue;
15633 }
15634 var prevProp = diffProp.prev;
15635 var fromProp = prevProp;
15636 var toProp = diffProp.next != null ? diffProp.next : styProp;
15637 var diff = false;
15638 var initVal = void 0;
15639 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15640
15641 if (!fromProp) {
15642 continue;
15643 }
15644
15645 // consider px values
15646 if (number$1(fromProp.pfValue) && number$1(toProp.pfValue)) {
15647 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15648 initVal = fromProp.pfValue + initDt * diff;
15649
15650 // consider numerical values
15651 } else if (number$1(fromProp.value) && number$1(toProp.value)) {
15652 diff = toProp.value - fromProp.value; // nonzero is truthy
15653 initVal = fromProp.value + initDt * diff;
15654
15655 // consider colour values
15656 } else if (array(fromProp.value) && array(toProp.value)) {
15657 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15658 initVal = fromProp.strValue;
15659 }
15660
15661 // the previous value is good for an animation only if it's different
15662 if (diff) {
15663 style[prop] = toProp.strValue; // to val
15664 this.applyBypass(ele, prop, initVal); // from val
15665 anyPrev = true;
15666 }
15667 } // end if props allow ani
15668
15669 // can't transition if there's nothing previous to transition from
15670 if (!anyPrev) {
15671 return;
15672 }
15673 _p.transitioning = true;
15674 new Promise$1(function (resolve) {
15675 if (delay > 0) {
15676 ele.delayAnimation(delay).play().promise().then(resolve);
15677 } else {
15678 resolve();
15679 }
15680 }).then(function () {
15681 return ele.animation({
15682 style: style,
15683 duration: duration,
15684 easing: ele.pstyle('transition-timing-function').value,
15685 queue: false
15686 }).play().promise();
15687 }).then(function () {
15688 // if( !isBypass ){
15689 self.removeBypasses(ele, props);
15690 ele.emitAndNotify('style');
15691 // }
15692
15693 _p.transitioning = false;
15694 });
15695 } else if (_p.transitioning) {
15696 this.removeBypasses(ele, props);
15697 ele.emitAndNotify('style');
15698 _p.transitioning = false;
15699 }
15700};
15701styfn$8.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15702 var prop = this.properties[name];
15703 var triggerCheck = getTrigger(prop);
15704 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15705 onTrigger(prop);
15706 }
15707};
15708styfn$8.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15709 var _this = this;
15710 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15711 return prop.triggersZOrder;
15712 }, function () {
15713 _this._private.cy.notify('zorder', ele);
15714 });
15715};
15716styfn$8.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15717 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15718 return prop.triggersBounds;
15719 }, function (prop) {
15720 ele.dirtyCompoundBoundsCache();
15721 ele.dirtyBoundingBoxCache();
15722
15723 // if the prop change makes the bb of pll bezier edges invalid,
15724 // then dirty the pll edge bb cache as well
15725 if (
15726 // only for beziers -- so performance of other edges isn't affected
15727 prop.triggersBoundsOfParallelBeziers && name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) {
15728 ele.parallelEdges().forEach(function (pllEdge) {
15729 if (pllEdge.isBundledBezier()) {
15730 pllEdge.dirtyBoundingBoxCache();
15731 }
15732 });
15733 }
15734 if (prop.triggersBoundsOfConnectedEdges && name === 'display' && (fromValue === 'none' || toValue === 'none')) {
15735 ele.connectedEdges().forEach(function (edge) {
15736 edge.dirtyBoundingBoxCache();
15737 });
15738 }
15739 });
15740};
15741styfn$8.checkTriggers = function (ele, name, fromValue, toValue) {
15742 ele.dirtyStyleCache();
15743 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15744 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15745};
15746
15747var styfn$7 = {};
15748
15749// bypasses are applied to an existing style on an element, and just tacked on temporarily
15750// returns true iff application was successful for at least 1 specified property
15751styfn$7.applyBypass = function (eles, name, value, updateTransitions) {
15752 var self = this;
15753 var props = [];
15754 var isBypass = true;
15755
15756 // put all the properties (can specify one or many) in an array after parsing them
15757 if (name === '*' || name === '**') {
15758 // apply to all property names
15759
15760 if (value !== undefined) {
15761 for (var i = 0; i < self.properties.length; i++) {
15762 var prop = self.properties[i];
15763 var _name = prop.name;
15764 var parsedProp = this.parse(_name, value, true);
15765 if (parsedProp) {
15766 props.push(parsedProp);
15767 }
15768 }
15769 }
15770 } else if (string(name)) {
15771 // then parse the single property
15772 var _parsedProp = this.parse(name, value, true);
15773 if (_parsedProp) {
15774 props.push(_parsedProp);
15775 }
15776 } else if (plainObject(name)) {
15777 // then parse each property
15778 var specifiedProps = name;
15779 updateTransitions = value;
15780 var names = Object.keys(specifiedProps);
15781 for (var _i = 0; _i < names.length; _i++) {
15782 var _name2 = names[_i];
15783 var _value = specifiedProps[_name2];
15784 if (_value === undefined) {
15785 // try camel case name too
15786 _value = specifiedProps[dash2camel(_name2)];
15787 }
15788 if (_value !== undefined) {
15789 var _parsedProp2 = this.parse(_name2, _value, true);
15790 if (_parsedProp2) {
15791 props.push(_parsedProp2);
15792 }
15793 }
15794 }
15795 } else {
15796 // can't do anything without well defined properties
15797 return false;
15798 }
15799
15800 // we've failed if there are no valid properties
15801 if (props.length === 0) {
15802 return false;
15803 }
15804
15805 // now, apply the bypass properties on the elements
15806 var ret = false; // return true if at least one succesful bypass applied
15807 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15808 // for each ele
15809 var ele = eles[_i2];
15810 var diffProps = {};
15811 var diffProp = void 0;
15812 for (var j = 0; j < props.length; j++) {
15813 // for each prop
15814 var _prop = props[j];
15815 if (updateTransitions) {
15816 var prevProp = ele.pstyle(_prop.name);
15817 diffProp = diffProps[_prop.name] = {
15818 prev: prevProp
15819 };
15820 }
15821 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15822 if (updateTransitions) {
15823 diffProp.next = ele.pstyle(_prop.name);
15824 }
15825 } // for props
15826
15827 if (ret) {
15828 this.updateStyleHints(ele);
15829 }
15830 if (updateTransitions) {
15831 this.updateTransitions(ele, diffProps, isBypass);
15832 }
15833 } // for eles
15834
15835 return ret;
15836};
15837
15838// only useful in specific cases like animation
15839styfn$7.overrideBypass = function (eles, name, value) {
15840 name = camel2dash(name);
15841 for (var i = 0; i < eles.length; i++) {
15842 var ele = eles[i];
15843 var prop = ele._private.style[name];
15844 var type = this.properties[name].type;
15845 var isColor = type.color;
15846 var isMulti = type.mutiple;
15847 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15848 if (!prop || !prop.bypass) {
15849 // need a bypass if one doesn't exist
15850 this.applyBypass(ele, name, value);
15851 } else {
15852 prop.value = value;
15853 if (prop.pfValue != null) {
15854 prop.pfValue = value;
15855 }
15856 if (isColor) {
15857 prop.strValue = 'rgb(' + value.join(',') + ')';
15858 } else if (isMulti) {
15859 prop.strValue = value.join(' ');
15860 } else {
15861 prop.strValue = '' + value;
15862 }
15863 this.updateStyleHints(ele);
15864 }
15865 this.checkTriggers(ele, name, oldValue, value);
15866 }
15867};
15868styfn$7.removeAllBypasses = function (eles, updateTransitions) {
15869 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15870};
15871styfn$7.removeBypasses = function (eles, props, updateTransitions) {
15872 var isBypass = true;
15873 for (var j = 0; j < eles.length; j++) {
15874 var ele = eles[j];
15875 var diffProps = {};
15876 for (var i = 0; i < props.length; i++) {
15877 var name = props[i];
15878 var prop = this.properties[name];
15879 var prevProp = ele.pstyle(prop.name);
15880 if (!prevProp || !prevProp.bypass) {
15881 // if a bypass doesn't exist for the prop, nothing needs to be removed
15882 continue;
15883 }
15884 var value = ''; // empty => remove bypass
15885 var parsedProp = this.parse(name, value, true);
15886 var diffProp = diffProps[prop.name] = {
15887 prev: prevProp
15888 };
15889 this.applyParsedProperty(ele, parsedProp);
15890 diffProp.next = ele.pstyle(prop.name);
15891 } // for props
15892
15893 this.updateStyleHints(ele);
15894 if (updateTransitions) {
15895 this.updateTransitions(ele, diffProps, isBypass);
15896 }
15897 } // for eles
15898};
15899
15900var styfn$6 = {};
15901
15902// gets what an em size corresponds to in pixels relative to a dom element
15903styfn$6.getEmSizeInPixels = function () {
15904 var px = this.containerCss('font-size');
15905 if (px != null) {
15906 return parseFloat(px);
15907 } else {
15908 return 1; // for headless
15909 }
15910};
15911
15912// gets css property from the core container
15913styfn$6.containerCss = function (propName) {
15914 var cy = this._private.cy;
15915 var domElement = cy.container();
15916 var containerWindow = cy.window();
15917 if (containerWindow && domElement && containerWindow.getComputedStyle) {
15918 return containerWindow.getComputedStyle(domElement).getPropertyValue(propName);
15919 }
15920};
15921
15922var styfn$5 = {};
15923
15924// gets the rendered style for an element
15925styfn$5.getRenderedStyle = function (ele, prop) {
15926 if (prop) {
15927 return this.getStylePropertyValue(ele, prop, true);
15928 } else {
15929 return this.getRawStyle(ele, true);
15930 }
15931};
15932
15933// gets the raw style for an element
15934styfn$5.getRawStyle = function (ele, isRenderedVal) {
15935 var self = this;
15936 ele = ele[0]; // insure it's an element
15937
15938 if (ele) {
15939 var rstyle = {};
15940 for (var i = 0; i < self.properties.length; i++) {
15941 var prop = self.properties[i];
15942 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15943 if (val != null) {
15944 rstyle[prop.name] = val;
15945 rstyle[dash2camel(prop.name)] = val;
15946 }
15947 }
15948 return rstyle;
15949 }
15950};
15951styfn$5.getIndexedStyle = function (ele, property, subproperty, index) {
15952 var pstyle = ele.pstyle(property)[subproperty][index];
15953 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15954};
15955styfn$5.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15956 var self = this;
15957 ele = ele[0]; // insure it's an element
15958
15959 if (ele) {
15960 var prop = self.properties[propName];
15961 if (prop.alias) {
15962 prop = prop.pointsTo;
15963 }
15964 var type = prop.type;
15965 var styleProp = ele.pstyle(prop.name);
15966 if (styleProp) {
15967 var value = styleProp.value,
15968 units = styleProp.units,
15969 strValue = styleProp.strValue;
15970 if (isRenderedVal && type.number && value != null && number$1(value)) {
15971 var zoom = ele.cy().zoom();
15972 var getRenderedValue = function getRenderedValue(val) {
15973 return val * zoom;
15974 };
15975 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15976 return getRenderedValue(val) + units;
15977 };
15978 var isArrayValue = array(value);
15979 var haveUnits = isArrayValue ? units.every(function (u) {
15980 return u != null;
15981 }) : units != null;
15982 if (haveUnits) {
15983 if (isArrayValue) {
15984 return value.map(function (v, i) {
15985 return getValueStringWithUnits(v, units[i]);
15986 }).join(' ');
15987 } else {
15988 return getValueStringWithUnits(value, units);
15989 }
15990 } else {
15991 if (isArrayValue) {
15992 return value.map(function (v) {
15993 return string(v) ? v : '' + getRenderedValue(v);
15994 }).join(' ');
15995 } else {
15996 return '' + getRenderedValue(value);
15997 }
15998 }
15999 } else if (strValue != null) {
16000 return strValue;
16001 }
16002 }
16003 return null;
16004 }
16005};
16006styfn$5.getAnimationStartStyle = function (ele, aniProps) {
16007 var rstyle = {};
16008 for (var i = 0; i < aniProps.length; i++) {
16009 var aniProp = aniProps[i];
16010 var name = aniProp.name;
16011 var styleProp = ele.pstyle(name);
16012 if (styleProp !== undefined) {
16013 // then make a prop of it
16014 if (plainObject(styleProp)) {
16015 styleProp = this.parse(name, styleProp.strValue);
16016 } else {
16017 styleProp = this.parse(name, styleProp);
16018 }
16019 }
16020 if (styleProp) {
16021 rstyle[name] = styleProp;
16022 }
16023 }
16024 return rstyle;
16025};
16026styfn$5.getPropsList = function (propsObj) {
16027 var self = this;
16028 var rstyle = [];
16029 var style = propsObj;
16030 var props = self.properties;
16031 if (style) {
16032 var names = Object.keys(style);
16033 for (var i = 0; i < names.length; i++) {
16034 var name = names[i];
16035 var val = style[name];
16036 var prop = props[name] || props[camel2dash(name)];
16037 var styleProp = this.parse(prop.name, val);
16038 if (styleProp) {
16039 rstyle.push(styleProp);
16040 }
16041 }
16042 }
16043 return rstyle;
16044};
16045styfn$5.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
16046 var hash = seed.slice();
16047 var name, val, strVal, chVal;
16048 var i, j;
16049 for (i = 0; i < propNames.length; i++) {
16050 name = propNames[i];
16051 val = ele.pstyle(name, false);
16052 if (val == null) {
16053 continue;
16054 } else if (val.pfValue != null) {
16055 hash[0] = hashInt(chVal, hash[0]);
16056 hash[1] = hashIntAlt(chVal, hash[1]);
16057 } else {
16058 strVal = val.strValue;
16059 for (j = 0; j < strVal.length; j++) {
16060 chVal = strVal.charCodeAt(j);
16061 hash[0] = hashInt(chVal, hash[0]);
16062 hash[1] = hashIntAlt(chVal, hash[1]);
16063 }
16064 }
16065 }
16066 return hash;
16067};
16068styfn$5.getPropertiesHash = styfn$5.getNonDefaultPropertiesHash;
16069
16070var styfn$4 = {};
16071styfn$4.appendFromJson = function (json) {
16072 var style = this;
16073 for (var i = 0; i < json.length; i++) {
16074 var context = json[i];
16075 var selector = context.selector;
16076 var props = context.style || context.css;
16077 var names = Object.keys(props);
16078 style.selector(selector); // apply selector
16079
16080 for (var j = 0; j < names.length; j++) {
16081 var name = names[j];
16082 var value = props[name];
16083 style.css(name, value); // apply property
16084 }
16085 }
16086
16087 return style;
16088};
16089
16090// accessible cy.style() function
16091styfn$4.fromJson = function (json) {
16092 var style = this;
16093 style.resetToDefault();
16094 style.appendFromJson(json);
16095 return style;
16096};
16097
16098// get json from cy.style() api
16099styfn$4.json = function () {
16100 var json = [];
16101 for (var i = this.defaultLength; i < this.length; i++) {
16102 var cxt = this[i];
16103 var selector = cxt.selector;
16104 var props = cxt.properties;
16105 var css = {};
16106 for (var j = 0; j < props.length; j++) {
16107 var prop = props[j];
16108 css[prop.name] = prop.strValue;
16109 }
16110 json.push({
16111 selector: !selector ? 'core' : selector.toString(),
16112 style: css
16113 });
16114 }
16115 return json;
16116};
16117
16118var styfn$3 = {};
16119styfn$3.appendFromString = function (string) {
16120 var self = this;
16121 var style = this;
16122 var remaining = '' + string;
16123 var selAndBlockStr;
16124 var blockRem;
16125 var propAndValStr;
16126
16127 // remove comments from the style string
16128 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
16129 function removeSelAndBlockFromRemaining() {
16130 // remove the parsed selector and block from the remaining text to parse
16131 if (remaining.length > selAndBlockStr.length) {
16132 remaining = remaining.substr(selAndBlockStr.length);
16133 } else {
16134 remaining = '';
16135 }
16136 }
16137 function removePropAndValFromRem() {
16138 // remove the parsed property and value from the remaining block text to parse
16139 if (blockRem.length > propAndValStr.length) {
16140 blockRem = blockRem.substr(propAndValStr.length);
16141 } else {
16142 blockRem = '';
16143 }
16144 }
16145 for (;;) {
16146 var nothingLeftToParse = remaining.match(/^\s*$/);
16147 if (nothingLeftToParse) {
16148 break;
16149 }
16150 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
16151 if (!selAndBlock) {
16152 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
16153 break;
16154 }
16155 selAndBlockStr = selAndBlock[0];
16156
16157 // parse the selector
16158 var selectorStr = selAndBlock[1];
16159 if (selectorStr !== 'core') {
16160 var selector = new Selector(selectorStr);
16161 if (selector.invalid) {
16162 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr);
16163
16164 // skip this selector and block
16165 removeSelAndBlockFromRemaining();
16166 continue;
16167 }
16168 }
16169
16170 // parse the block of properties and values
16171 var blockStr = selAndBlock[2];
16172 var invalidBlock = false;
16173 blockRem = blockStr;
16174 var props = [];
16175 for (;;) {
16176 var _nothingLeftToParse = blockRem.match(/^\s*$/);
16177 if (_nothingLeftToParse) {
16178 break;
16179 }
16180 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);
16181 if (!propAndVal) {
16182 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
16183 invalidBlock = true;
16184 break;
16185 }
16186 propAndValStr = propAndVal[0];
16187 var propStr = propAndVal[1];
16188 var valStr = propAndVal[2];
16189 var prop = self.properties[propStr];
16190 if (!prop) {
16191 warn('Skipping property: Invalid property name in: ' + propAndValStr);
16192
16193 // skip this property in the block
16194 removePropAndValFromRem();
16195 continue;
16196 }
16197 var parsedProp = style.parse(propStr, valStr);
16198 if (!parsedProp) {
16199 warn('Skipping property: Invalid property definition in: ' + propAndValStr);
16200
16201 // skip this property in the block
16202 removePropAndValFromRem();
16203 continue;
16204 }
16205 props.push({
16206 name: propStr,
16207 val: valStr
16208 });
16209 removePropAndValFromRem();
16210 }
16211 if (invalidBlock) {
16212 removeSelAndBlockFromRemaining();
16213 break;
16214 }
16215
16216 // put the parsed block in the style
16217 style.selector(selectorStr);
16218 for (var i = 0; i < props.length; i++) {
16219 var _prop = props[i];
16220 style.css(_prop.name, _prop.val);
16221 }
16222 removeSelAndBlockFromRemaining();
16223 }
16224 return style;
16225};
16226styfn$3.fromString = function (string) {
16227 var style = this;
16228 style.resetToDefault();
16229 style.appendFromString(string);
16230 return style;
16231};
16232
16233var styfn$2 = {};
16234(function () {
16235 var number$1 = number;
16236 var rgba = rgbaNoBackRefs;
16237 var hsla = hslaNoBackRefs;
16238 var hex3$1 = hex3;
16239 var hex6$1 = hex6;
16240 var data = function data(prefix) {
16241 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16242 };
16243 var mapData = function mapData(prefix) {
16244 var mapArg = number$1 + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16245 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number$1 + ')\\s*\\,\\s*(' + number$1 + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16246 };
16247 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$'];
16248
16249 // each visual style property has a type and needs to be validated according to it
16250 styfn$2.types = {
16251 time: {
16252 number: true,
16253 min: 0,
16254 units: 's|ms',
16255 implicitUnits: 'ms'
16256 },
16257 percent: {
16258 number: true,
16259 min: 0,
16260 max: 100,
16261 units: '%',
16262 implicitUnits: '%'
16263 },
16264 percentages: {
16265 number: true,
16266 min: 0,
16267 max: 100,
16268 units: '%',
16269 implicitUnits: '%',
16270 multiple: true
16271 },
16272 zeroOneNumber: {
16273 number: true,
16274 min: 0,
16275 max: 1,
16276 unitless: true
16277 },
16278 zeroOneNumbers: {
16279 number: true,
16280 min: 0,
16281 max: 1,
16282 unitless: true,
16283 multiple: true
16284 },
16285 nOneOneNumber: {
16286 number: true,
16287 min: -1,
16288 max: 1,
16289 unitless: true
16290 },
16291 nonNegativeInt: {
16292 number: true,
16293 min: 0,
16294 integer: true,
16295 unitless: true
16296 },
16297 nonNegativeNumber: {
16298 number: true,
16299 min: 0,
16300 unitless: true
16301 },
16302 position: {
16303 enums: ['parent', 'origin']
16304 },
16305 nodeSize: {
16306 number: true,
16307 min: 0,
16308 enums: ['label']
16309 },
16310 number: {
16311 number: true,
16312 unitless: true
16313 },
16314 numbers: {
16315 number: true,
16316 unitless: true,
16317 multiple: true
16318 },
16319 positiveNumber: {
16320 number: true,
16321 unitless: true,
16322 min: 0,
16323 strictMin: true
16324 },
16325 size: {
16326 number: true,
16327 min: 0
16328 },
16329 bidirectionalSize: {
16330 number: true
16331 },
16332 // allows negative
16333 bidirectionalSizeMaybePercent: {
16334 number: true,
16335 allowPercent: true
16336 },
16337 // allows negative
16338 bidirectionalSizes: {
16339 number: true,
16340 multiple: true
16341 },
16342 // allows negative
16343 sizeMaybePercent: {
16344 number: true,
16345 min: 0,
16346 allowPercent: true
16347 },
16348 axisDirection: {
16349 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16350 },
16351 paddingRelativeTo: {
16352 enums: ['width', 'height', 'average', 'min', 'max']
16353 },
16354 bgWH: {
16355 number: true,
16356 min: 0,
16357 allowPercent: true,
16358 enums: ['auto'],
16359 multiple: true
16360 },
16361 bgPos: {
16362 number: true,
16363 allowPercent: true,
16364 multiple: true
16365 },
16366 bgRelativeTo: {
16367 enums: ['inner', 'include-padding'],
16368 multiple: true
16369 },
16370 bgRepeat: {
16371 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16372 multiple: true
16373 },
16374 bgFit: {
16375 enums: ['none', 'contain', 'cover'],
16376 multiple: true
16377 },
16378 bgCrossOrigin: {
16379 enums: ['anonymous', 'use-credentials', 'null'],
16380 multiple: true
16381 },
16382 bgClip: {
16383 enums: ['none', 'node'],
16384 multiple: true
16385 },
16386 bgContainment: {
16387 enums: ['inside', 'over'],
16388 multiple: true
16389 },
16390 color: {
16391 color: true
16392 },
16393 colors: {
16394 color: true,
16395 multiple: true
16396 },
16397 fill: {
16398 enums: ['solid', 'linear-gradient', 'radial-gradient']
16399 },
16400 bool: {
16401 enums: ['yes', 'no']
16402 },
16403 bools: {
16404 enums: ['yes', 'no'],
16405 multiple: true
16406 },
16407 lineStyle: {
16408 enums: ['solid', 'dotted', 'dashed']
16409 },
16410 lineCap: {
16411 enums: ['butt', 'round', 'square']
16412 },
16413 linePosition: {
16414 enums: ['center', 'inside', 'outside']
16415 },
16416 lineJoin: {
16417 enums: ['round', 'bevel', 'miter']
16418 },
16419 borderStyle: {
16420 enums: ['solid', 'dotted', 'dashed', 'double']
16421 },
16422 curveStyle: {
16423 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi', 'round-segments', 'round-taxi']
16424 },
16425 radiusType: {
16426 enums: ['arc-radius', 'influence-radius'],
16427 multiple: true
16428 },
16429 fontFamily: {
16430 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16431 },
16432 fontStyle: {
16433 enums: ['italic', 'normal', 'oblique']
16434 },
16435 fontWeight: {
16436 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16437 },
16438 textDecoration: {
16439 enums: ['none', 'underline', 'overline', 'line-through']
16440 },
16441 textTransform: {
16442 enums: ['none', 'uppercase', 'lowercase']
16443 },
16444 textWrap: {
16445 enums: ['none', 'wrap', 'ellipsis']
16446 },
16447 textOverflowWrap: {
16448 enums: ['whitespace', 'anywhere']
16449 },
16450 textBackgroundShape: {
16451 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16452 },
16453 nodeShape: {
16454 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']
16455 },
16456 overlayShape: {
16457 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16458 },
16459 cornerRadius: {
16460 number: true,
16461 min: 0,
16462 units: 'px|em',
16463 implicitUnits: 'px',
16464 enums: ['auto']
16465 },
16466 compoundIncludeLabels: {
16467 enums: ['include', 'exclude']
16468 },
16469 arrowShape: {
16470 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16471 },
16472 arrowFill: {
16473 enums: ['filled', 'hollow']
16474 },
16475 arrowWidth: {
16476 number: true,
16477 units: '%|px|em',
16478 implicitUnits: 'px',
16479 enums: ['match-line']
16480 },
16481 display: {
16482 enums: ['element', 'none']
16483 },
16484 visibility: {
16485 enums: ['hidden', 'visible']
16486 },
16487 zCompoundDepth: {
16488 enums: ['bottom', 'orphan', 'auto', 'top']
16489 },
16490 zIndexCompare: {
16491 enums: ['auto', 'manual']
16492 },
16493 valign: {
16494 enums: ['top', 'center', 'bottom']
16495 },
16496 halign: {
16497 enums: ['left', 'center', 'right']
16498 },
16499 justification: {
16500 enums: ['left', 'center', 'right', 'auto']
16501 },
16502 text: {
16503 string: true
16504 },
16505 data: {
16506 mapping: true,
16507 regex: data('data')
16508 },
16509 layoutData: {
16510 mapping: true,
16511 regex: data('layoutData')
16512 },
16513 scratch: {
16514 mapping: true,
16515 regex: data('scratch')
16516 },
16517 mapData: {
16518 mapping: true,
16519 regex: mapData('mapData')
16520 },
16521 mapLayoutData: {
16522 mapping: true,
16523 regex: mapData('mapLayoutData')
16524 },
16525 mapScratch: {
16526 mapping: true,
16527 regex: mapData('mapScratch')
16528 },
16529 fn: {
16530 mapping: true,
16531 fn: true
16532 },
16533 url: {
16534 regexes: urlRegexes,
16535 singleRegexMatchValue: true
16536 },
16537 urls: {
16538 regexes: urlRegexes,
16539 singleRegexMatchValue: true,
16540 multiple: true
16541 },
16542 propList: {
16543 propList: true
16544 },
16545 angle: {
16546 number: true,
16547 units: 'deg|rad',
16548 implicitUnits: 'rad'
16549 },
16550 textRotation: {
16551 number: true,
16552 units: 'deg|rad',
16553 implicitUnits: 'rad',
16554 enums: ['none', 'autorotate']
16555 },
16556 polygonPointList: {
16557 number: true,
16558 multiple: true,
16559 evenMultiple: true,
16560 min: -1,
16561 max: 1,
16562 unitless: true
16563 },
16564 edgeDistances: {
16565 enums: ['intersection', 'node-position', 'endpoints']
16566 },
16567 edgeEndpoint: {
16568 number: true,
16569 multiple: true,
16570 units: '%|px|em|deg|rad',
16571 implicitUnits: 'px',
16572 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16573 singleEnum: true,
16574 validate: function validate(valArr, unitsArr) {
16575 switch (valArr.length) {
16576 case 2:
16577 // can be % or px only
16578 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16579 case 1:
16580 // can be enum, deg, or rad only
16581 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16582 default:
16583 return false;
16584 }
16585 }
16586 },
16587 easing: {
16588 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*\\)$'],
16589 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']
16590 },
16591 gradientDirection: {
16592 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
16593 ]
16594 },
16595
16596 boundsExpansion: {
16597 number: true,
16598 multiple: true,
16599 min: 0,
16600 validate: function validate(valArr) {
16601 var length = valArr.length;
16602 return length === 1 || length === 2 || length === 4;
16603 }
16604 }
16605 };
16606 var diff = {
16607 zeroNonZero: function zeroNonZero(val1, val2) {
16608 if ((val1 == null || val2 == null) && val1 !== val2) {
16609 return true; // null cases could represent any value
16610 }
16611 if (val1 == 0 && val2 != 0) {
16612 return true;
16613 } else if (val1 != 0 && val2 == 0) {
16614 return true;
16615 } else {
16616 return false;
16617 }
16618 },
16619 any: function any(val1, val2) {
16620 return val1 != val2;
16621 },
16622 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16623 var empty1 = emptyString(str1);
16624 var empty2 = emptyString(str2);
16625 return empty1 && !empty2 || !empty1 && empty2;
16626 }
16627 };
16628
16629 // define visual style properties
16630 //
16631 // - n.b. adding a new group of props may require updates to updateStyleHints()
16632 // - adding new props to an existing group gets handled automatically
16633
16634 var t = styfn$2.types;
16635 var mainLabel = [{
16636 name: 'label',
16637 type: t.text,
16638 triggersBounds: diff.any,
16639 triggersZOrder: diff.emptyNonEmpty
16640 }, {
16641 name: 'text-rotation',
16642 type: t.textRotation,
16643 triggersBounds: diff.any
16644 }, {
16645 name: 'text-margin-x',
16646 type: t.bidirectionalSize,
16647 triggersBounds: diff.any
16648 }, {
16649 name: 'text-margin-y',
16650 type: t.bidirectionalSize,
16651 triggersBounds: diff.any
16652 }];
16653 var sourceLabel = [{
16654 name: 'source-label',
16655 type: t.text,
16656 triggersBounds: diff.any
16657 }, {
16658 name: 'source-text-rotation',
16659 type: t.textRotation,
16660 triggersBounds: diff.any
16661 }, {
16662 name: 'source-text-margin-x',
16663 type: t.bidirectionalSize,
16664 triggersBounds: diff.any
16665 }, {
16666 name: 'source-text-margin-y',
16667 type: t.bidirectionalSize,
16668 triggersBounds: diff.any
16669 }, {
16670 name: 'source-text-offset',
16671 type: t.size,
16672 triggersBounds: diff.any
16673 }];
16674 var targetLabel = [{
16675 name: 'target-label',
16676 type: t.text,
16677 triggersBounds: diff.any
16678 }, {
16679 name: 'target-text-rotation',
16680 type: t.textRotation,
16681 triggersBounds: diff.any
16682 }, {
16683 name: 'target-text-margin-x',
16684 type: t.bidirectionalSize,
16685 triggersBounds: diff.any
16686 }, {
16687 name: 'target-text-margin-y',
16688 type: t.bidirectionalSize,
16689 triggersBounds: diff.any
16690 }, {
16691 name: 'target-text-offset',
16692 type: t.size,
16693 triggersBounds: diff.any
16694 }];
16695 var labelDimensions = [{
16696 name: 'font-family',
16697 type: t.fontFamily,
16698 triggersBounds: diff.any
16699 }, {
16700 name: 'font-style',
16701 type: t.fontStyle,
16702 triggersBounds: diff.any
16703 }, {
16704 name: 'font-weight',
16705 type: t.fontWeight,
16706 triggersBounds: diff.any
16707 }, {
16708 name: 'font-size',
16709 type: t.size,
16710 triggersBounds: diff.any
16711 }, {
16712 name: 'text-transform',
16713 type: t.textTransform,
16714 triggersBounds: diff.any
16715 }, {
16716 name: 'text-wrap',
16717 type: t.textWrap,
16718 triggersBounds: diff.any
16719 }, {
16720 name: 'text-overflow-wrap',
16721 type: t.textOverflowWrap,
16722 triggersBounds: diff.any
16723 }, {
16724 name: 'text-max-width',
16725 type: t.size,
16726 triggersBounds: diff.any
16727 }, {
16728 name: 'text-outline-width',
16729 type: t.size,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'line-height',
16733 type: t.positiveNumber,
16734 triggersBounds: diff.any
16735 }];
16736 var commonLabel = [{
16737 name: 'text-valign',
16738 type: t.valign,
16739 triggersBounds: diff.any
16740 }, {
16741 name: 'text-halign',
16742 type: t.halign,
16743 triggersBounds: diff.any
16744 }, {
16745 name: 'color',
16746 type: t.color
16747 }, {
16748 name: 'text-outline-color',
16749 type: t.color
16750 }, {
16751 name: 'text-outline-opacity',
16752 type: t.zeroOneNumber
16753 }, {
16754 name: 'text-background-color',
16755 type: t.color
16756 }, {
16757 name: 'text-background-opacity',
16758 type: t.zeroOneNumber
16759 }, {
16760 name: 'text-background-padding',
16761 type: t.size,
16762 triggersBounds: diff.any
16763 }, {
16764 name: 'text-border-opacity',
16765 type: t.zeroOneNumber
16766 }, {
16767 name: 'text-border-color',
16768 type: t.color
16769 }, {
16770 name: 'text-border-width',
16771 type: t.size,
16772 triggersBounds: diff.any
16773 }, {
16774 name: 'text-border-style',
16775 type: t.borderStyle,
16776 triggersBounds: diff.any
16777 }, {
16778 name: 'text-background-shape',
16779 type: t.textBackgroundShape,
16780 triggersBounds: diff.any
16781 }, {
16782 name: 'text-justification',
16783 type: t.justification
16784 }];
16785 var behavior = [{
16786 name: 'events',
16787 type: t.bool,
16788 triggersZOrder: diff.any
16789 }, {
16790 name: 'text-events',
16791 type: t.bool,
16792 triggersZOrder: diff.any
16793 }];
16794 var visibility = [{
16795 name: 'display',
16796 type: t.display,
16797 triggersZOrder: diff.any,
16798 triggersBounds: diff.any,
16799 triggersBoundsOfConnectedEdges: true
16800 }, {
16801 name: 'visibility',
16802 type: t.visibility,
16803 triggersZOrder: diff.any
16804 }, {
16805 name: 'opacity',
16806 type: t.zeroOneNumber,
16807 triggersZOrder: diff.zeroNonZero
16808 }, {
16809 name: 'text-opacity',
16810 type: t.zeroOneNumber
16811 }, {
16812 name: 'min-zoomed-font-size',
16813 type: t.size
16814 }, {
16815 name: 'z-compound-depth',
16816 type: t.zCompoundDepth,
16817 triggersZOrder: diff.any
16818 }, {
16819 name: 'z-index-compare',
16820 type: t.zIndexCompare,
16821 triggersZOrder: diff.any
16822 }, {
16823 name: 'z-index',
16824 type: t.number,
16825 triggersZOrder: diff.any
16826 }];
16827 var overlay = [{
16828 name: 'overlay-padding',
16829 type: t.size,
16830 triggersBounds: diff.any
16831 }, {
16832 name: 'overlay-color',
16833 type: t.color
16834 }, {
16835 name: 'overlay-opacity',
16836 type: t.zeroOneNumber,
16837 triggersBounds: diff.zeroNonZero
16838 }, {
16839 name: 'overlay-shape',
16840 type: t.overlayShape,
16841 triggersBounds: diff.any
16842 }, {
16843 name: 'overlay-corner-radius',
16844 type: t.cornerRadius
16845 }];
16846 var underlay = [{
16847 name: 'underlay-padding',
16848 type: t.size,
16849 triggersBounds: diff.any
16850 }, {
16851 name: 'underlay-color',
16852 type: t.color
16853 }, {
16854 name: 'underlay-opacity',
16855 type: t.zeroOneNumber,
16856 triggersBounds: diff.zeroNonZero
16857 }, {
16858 name: 'underlay-shape',
16859 type: t.overlayShape,
16860 triggersBounds: diff.any
16861 }, {
16862 name: 'underlay-corner-radius',
16863 type: t.cornerRadius
16864 }];
16865 var transition = [{
16866 name: 'transition-property',
16867 type: t.propList
16868 }, {
16869 name: 'transition-duration',
16870 type: t.time
16871 }, {
16872 name: 'transition-delay',
16873 type: t.time
16874 }, {
16875 name: 'transition-timing-function',
16876 type: t.easing
16877 }];
16878 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16879 if (parsedProp.value === 'label') {
16880 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16881 } else {
16882 return parsedProp.pfValue;
16883 }
16884 };
16885 var nodeBody = [{
16886 name: 'height',
16887 type: t.nodeSize,
16888 triggersBounds: diff.any,
16889 hashOverride: nodeSizeHashOverride
16890 }, {
16891 name: 'width',
16892 type: t.nodeSize,
16893 triggersBounds: diff.any,
16894 hashOverride: nodeSizeHashOverride
16895 }, {
16896 name: 'shape',
16897 type: t.nodeShape,
16898 triggersBounds: diff.any
16899 }, {
16900 name: 'shape-polygon-points',
16901 type: t.polygonPointList,
16902 triggersBounds: diff.any
16903 }, {
16904 name: 'corner-radius',
16905 type: t.cornerRadius
16906 }, {
16907 name: 'background-color',
16908 type: t.color
16909 }, {
16910 name: 'background-fill',
16911 type: t.fill
16912 }, {
16913 name: 'background-opacity',
16914 type: t.zeroOneNumber
16915 }, {
16916 name: 'background-blacken',
16917 type: t.nOneOneNumber
16918 }, {
16919 name: 'background-gradient-stop-colors',
16920 type: t.colors
16921 }, {
16922 name: 'background-gradient-stop-positions',
16923 type: t.percentages
16924 }, {
16925 name: 'background-gradient-direction',
16926 type: t.gradientDirection
16927 }, {
16928 name: 'padding',
16929 type: t.sizeMaybePercent,
16930 triggersBounds: diff.any
16931 }, {
16932 name: 'padding-relative-to',
16933 type: t.paddingRelativeTo,
16934 triggersBounds: diff.any
16935 }, {
16936 name: 'bounds-expansion',
16937 type: t.boundsExpansion,
16938 triggersBounds: diff.any
16939 }];
16940 var nodeBorder = [{
16941 name: 'border-color',
16942 type: t.color
16943 }, {
16944 name: 'border-opacity',
16945 type: t.zeroOneNumber
16946 }, {
16947 name: 'border-width',
16948 type: t.size,
16949 triggersBounds: diff.any
16950 }, {
16951 name: 'border-style',
16952 type: t.borderStyle
16953 }, {
16954 name: 'border-cap',
16955 type: t.lineCap
16956 }, {
16957 name: 'border-join',
16958 type: t.lineJoin
16959 }, {
16960 name: 'border-dash-pattern',
16961 type: t.numbers
16962 }, {
16963 name: 'border-dash-offset',
16964 type: t.number
16965 }, {
16966 name: 'border-position',
16967 type: t.linePosition
16968 }];
16969 var nodeOutline = [{
16970 name: 'outline-color',
16971 type: t.color
16972 }, {
16973 name: 'outline-opacity',
16974 type: t.zeroOneNumber
16975 }, {
16976 name: 'outline-width',
16977 type: t.size,
16978 triggersBounds: diff.any
16979 }, {
16980 name: 'outline-style',
16981 type: t.borderStyle
16982 }, {
16983 name: 'outline-offset',
16984 type: t.size,
16985 triggersBounds: diff.any
16986 }];
16987 var backgroundImage = [{
16988 name: 'background-image',
16989 type: t.urls
16990 }, {
16991 name: 'background-image-crossorigin',
16992 type: t.bgCrossOrigin
16993 }, {
16994 name: 'background-image-opacity',
16995 type: t.zeroOneNumbers
16996 }, {
16997 name: 'background-image-containment',
16998 type: t.bgContainment
16999 }, {
17000 name: 'background-image-smoothing',
17001 type: t.bools
17002 }, {
17003 name: 'background-position-x',
17004 type: t.bgPos
17005 }, {
17006 name: 'background-position-y',
17007 type: t.bgPos
17008 }, {
17009 name: 'background-width-relative-to',
17010 type: t.bgRelativeTo
17011 }, {
17012 name: 'background-height-relative-to',
17013 type: t.bgRelativeTo
17014 }, {
17015 name: 'background-repeat',
17016 type: t.bgRepeat
17017 }, {
17018 name: 'background-fit',
17019 type: t.bgFit
17020 }, {
17021 name: 'background-clip',
17022 type: t.bgClip
17023 }, {
17024 name: 'background-width',
17025 type: t.bgWH
17026 }, {
17027 name: 'background-height',
17028 type: t.bgWH
17029 }, {
17030 name: 'background-offset-x',
17031 type: t.bgPos
17032 }, {
17033 name: 'background-offset-y',
17034 type: t.bgPos
17035 }];
17036 var compound = [{
17037 name: 'position',
17038 type: t.position,
17039 triggersBounds: diff.any
17040 }, {
17041 name: 'compound-sizing-wrt-labels',
17042 type: t.compoundIncludeLabels,
17043 triggersBounds: diff.any
17044 }, {
17045 name: 'min-width',
17046 type: t.size,
17047 triggersBounds: diff.any
17048 }, {
17049 name: 'min-width-bias-left',
17050 type: t.sizeMaybePercent,
17051 triggersBounds: diff.any
17052 }, {
17053 name: 'min-width-bias-right',
17054 type: t.sizeMaybePercent,
17055 triggersBounds: diff.any
17056 }, {
17057 name: 'min-height',
17058 type: t.size,
17059 triggersBounds: diff.any
17060 }, {
17061 name: 'min-height-bias-top',
17062 type: t.sizeMaybePercent,
17063 triggersBounds: diff.any
17064 }, {
17065 name: 'min-height-bias-bottom',
17066 type: t.sizeMaybePercent,
17067 triggersBounds: diff.any
17068 }];
17069 var edgeLine = [{
17070 name: 'line-style',
17071 type: t.lineStyle
17072 }, {
17073 name: 'line-color',
17074 type: t.color
17075 }, {
17076 name: 'line-fill',
17077 type: t.fill
17078 }, {
17079 name: 'line-cap',
17080 type: t.lineCap
17081 }, {
17082 name: 'line-opacity',
17083 type: t.zeroOneNumber
17084 }, {
17085 name: 'line-dash-pattern',
17086 type: t.numbers
17087 }, {
17088 name: 'line-dash-offset',
17089 type: t.number
17090 }, {
17091 name: 'line-outline-width',
17092 type: t.size
17093 }, {
17094 name: 'line-outline-color',
17095 type: t.color
17096 }, {
17097 name: 'line-gradient-stop-colors',
17098 type: t.colors
17099 }, {
17100 name: 'line-gradient-stop-positions',
17101 type: t.percentages
17102 }, {
17103 name: 'curve-style',
17104 type: t.curveStyle,
17105 triggersBounds: diff.any,
17106 triggersBoundsOfParallelBeziers: true
17107 }, {
17108 name: 'haystack-radius',
17109 type: t.zeroOneNumber,
17110 triggersBounds: diff.any
17111 }, {
17112 name: 'source-endpoint',
17113 type: t.edgeEndpoint,
17114 triggersBounds: diff.any
17115 }, {
17116 name: 'target-endpoint',
17117 type: t.edgeEndpoint,
17118 triggersBounds: diff.any
17119 }, {
17120 name: 'control-point-step-size',
17121 type: t.size,
17122 triggersBounds: diff.any
17123 }, {
17124 name: 'control-point-distances',
17125 type: t.bidirectionalSizes,
17126 triggersBounds: diff.any
17127 }, {
17128 name: 'control-point-weights',
17129 type: t.numbers,
17130 triggersBounds: diff.any
17131 }, {
17132 name: 'segment-distances',
17133 type: t.bidirectionalSizes,
17134 triggersBounds: diff.any
17135 }, {
17136 name: 'segment-weights',
17137 type: t.numbers,
17138 triggersBounds: diff.any
17139 }, {
17140 name: 'segment-radii',
17141 type: t.numbers,
17142 triggersBounds: diff.any
17143 }, {
17144 name: 'radius-type',
17145 type: t.radiusType,
17146 triggersBounds: diff.any
17147 }, {
17148 name: 'taxi-turn',
17149 type: t.bidirectionalSizeMaybePercent,
17150 triggersBounds: diff.any
17151 }, {
17152 name: 'taxi-turn-min-distance',
17153 type: t.size,
17154 triggersBounds: diff.any
17155 }, {
17156 name: 'taxi-direction',
17157 type: t.axisDirection,
17158 triggersBounds: diff.any
17159 }, {
17160 name: 'taxi-radius',
17161 type: t.number,
17162 triggersBounds: diff.any
17163 }, {
17164 name: 'edge-distances',
17165 type: t.edgeDistances,
17166 triggersBounds: diff.any
17167 }, {
17168 name: 'arrow-scale',
17169 type: t.positiveNumber,
17170 triggersBounds: diff.any
17171 }, {
17172 name: 'loop-direction',
17173 type: t.angle,
17174 triggersBounds: diff.any
17175 }, {
17176 name: 'loop-sweep',
17177 type: t.angle,
17178 triggersBounds: diff.any
17179 }, {
17180 name: 'source-distance-from-node',
17181 type: t.size,
17182 triggersBounds: diff.any
17183 }, {
17184 name: 'target-distance-from-node',
17185 type: t.size,
17186 triggersBounds: diff.any
17187 }];
17188 var ghost = [{
17189 name: 'ghost',
17190 type: t.bool,
17191 triggersBounds: diff.any
17192 }, {
17193 name: 'ghost-offset-x',
17194 type: t.bidirectionalSize,
17195 triggersBounds: diff.any
17196 }, {
17197 name: 'ghost-offset-y',
17198 type: t.bidirectionalSize,
17199 triggersBounds: diff.any
17200 }, {
17201 name: 'ghost-opacity',
17202 type: t.zeroOneNumber
17203 }];
17204 var core = [{
17205 name: 'selection-box-color',
17206 type: t.color
17207 }, {
17208 name: 'selection-box-opacity',
17209 type: t.zeroOneNumber
17210 }, {
17211 name: 'selection-box-border-color',
17212 type: t.color
17213 }, {
17214 name: 'selection-box-border-width',
17215 type: t.size
17216 }, {
17217 name: 'active-bg-color',
17218 type: t.color
17219 }, {
17220 name: 'active-bg-opacity',
17221 type: t.zeroOneNumber
17222 }, {
17223 name: 'active-bg-size',
17224 type: t.size
17225 }, {
17226 name: 'outside-texture-bg-color',
17227 type: t.color
17228 }, {
17229 name: 'outside-texture-bg-opacity',
17230 type: t.zeroOneNumber
17231 }];
17232
17233 // pie backgrounds for nodes
17234 var pie = [];
17235 styfn$2.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
17236 pie.push({
17237 name: 'pie-size',
17238 type: t.sizeMaybePercent
17239 });
17240 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17241 pie.push({
17242 name: 'pie-' + i + '-background-color',
17243 type: t.color
17244 });
17245 pie.push({
17246 name: 'pie-' + i + '-background-size',
17247 type: t.percent
17248 });
17249 pie.push({
17250 name: 'pie-' + i + '-background-opacity',
17251 type: t.zeroOneNumber
17252 });
17253 }
17254
17255 // edge arrows
17256 var edgeArrow = [];
17257 var arrowPrefixes = styfn$2.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
17258 [{
17259 name: 'arrow-shape',
17260 type: t.arrowShape,
17261 triggersBounds: diff.any
17262 }, {
17263 name: 'arrow-color',
17264 type: t.color
17265 }, {
17266 name: 'arrow-fill',
17267 type: t.arrowFill
17268 }, {
17269 name: 'arrow-width',
17270 type: t.arrowWidth
17271 }].forEach(function (prop) {
17272 arrowPrefixes.forEach(function (prefix) {
17273 var name = prefix + '-' + prop.name;
17274 var type = prop.type,
17275 triggersBounds = prop.triggersBounds;
17276 edgeArrow.push({
17277 name: name,
17278 type: type,
17279 triggersBounds: triggersBounds
17280 });
17281 });
17282 }, {});
17283 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);
17284 var propGroups = styfn$2.propertyGroups = {
17285 // common to all eles
17286 behavior: behavior,
17287 transition: transition,
17288 visibility: visibility,
17289 overlay: overlay,
17290 underlay: underlay,
17291 ghost: ghost,
17292 // labels
17293 commonLabel: commonLabel,
17294 labelDimensions: labelDimensions,
17295 mainLabel: mainLabel,
17296 sourceLabel: sourceLabel,
17297 targetLabel: targetLabel,
17298 // node props
17299 nodeBody: nodeBody,
17300 nodeBorder: nodeBorder,
17301 nodeOutline: nodeOutline,
17302 backgroundImage: backgroundImage,
17303 pie: pie,
17304 compound: compound,
17305 // edge props
17306 edgeLine: edgeLine,
17307 edgeArrow: edgeArrow,
17308 core: core
17309 };
17310 var propGroupNames = styfn$2.propertyGroupNames = {};
17311 var propGroupKeys = styfn$2.propertyGroupKeys = Object.keys(propGroups);
17312 propGroupKeys.forEach(function (key) {
17313 propGroupNames[key] = propGroups[key].map(function (prop) {
17314 return prop.name;
17315 });
17316 propGroups[key].forEach(function (prop) {
17317 return prop.groupKey = key;
17318 });
17319 });
17320
17321 // define aliases
17322 var aliases = styfn$2.aliases = [{
17323 name: 'content',
17324 pointsTo: 'label'
17325 }, {
17326 name: 'control-point-distance',
17327 pointsTo: 'control-point-distances'
17328 }, {
17329 name: 'control-point-weight',
17330 pointsTo: 'control-point-weights'
17331 }, {
17332 name: 'segment-distance',
17333 pointsTo: 'segment-distances'
17334 }, {
17335 name: 'segment-weight',
17336 pointsTo: 'segment-weights'
17337 }, {
17338 name: 'segment-radius',
17339 pointsTo: 'segment-radii'
17340 }, {
17341 name: 'edge-text-rotation',
17342 pointsTo: 'text-rotation'
17343 }, {
17344 name: 'padding-left',
17345 pointsTo: 'padding'
17346 }, {
17347 name: 'padding-right',
17348 pointsTo: 'padding'
17349 }, {
17350 name: 'padding-top',
17351 pointsTo: 'padding'
17352 }, {
17353 name: 'padding-bottom',
17354 pointsTo: 'padding'
17355 }];
17356
17357 // list of property names
17358 styfn$2.propertyNames = props.map(function (p) {
17359 return p.name;
17360 });
17361
17362 // allow access of properties by name ( e.g. style.properties.height )
17363 for (var _i = 0; _i < props.length; _i++) {
17364 var prop = props[_i];
17365 props[prop.name] = prop; // allow lookup by name
17366 }
17367
17368 // map aliases
17369 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17370 var alias = aliases[_i2];
17371 var pointsToProp = props[alias.pointsTo];
17372 var aliasProp = {
17373 name: alias.name,
17374 alias: true,
17375 pointsTo: pointsToProp
17376 };
17377
17378 // add alias prop for parsing
17379 props.push(aliasProp);
17380 props[alias.name] = aliasProp; // allow lookup by name
17381 }
17382})();
17383
17384styfn$2.getDefaultProperty = function (name) {
17385 return this.getDefaultProperties()[name];
17386};
17387styfn$2.getDefaultProperties = function () {
17388 var _p = this._private;
17389 if (_p.defaultProperties != null) {
17390 return _p.defaultProperties;
17391 }
17392 var rawProps = extend({
17393 // core props
17394 'selection-box-color': '#ddd',
17395 'selection-box-opacity': 0.65,
17396 'selection-box-border-color': '#aaa',
17397 'selection-box-border-width': 1,
17398 'active-bg-color': 'black',
17399 'active-bg-opacity': 0.15,
17400 'active-bg-size': 30,
17401 'outside-texture-bg-color': '#000',
17402 'outside-texture-bg-opacity': 0.125,
17403 // common node/edge props
17404 'events': 'yes',
17405 'text-events': 'no',
17406 'text-valign': 'top',
17407 'text-halign': 'center',
17408 'text-justification': 'auto',
17409 'line-height': 1,
17410 'color': '#000',
17411 'text-outline-color': '#000',
17412 'text-outline-width': 0,
17413 'text-outline-opacity': 1,
17414 'text-opacity': 1,
17415 'text-decoration': 'none',
17416 'text-transform': 'none',
17417 'text-wrap': 'none',
17418 'text-overflow-wrap': 'whitespace',
17419 'text-max-width': 9999,
17420 'text-background-color': '#000',
17421 'text-background-opacity': 0,
17422 'text-background-shape': 'rectangle',
17423 'text-background-padding': 0,
17424 'text-border-opacity': 0,
17425 'text-border-width': 0,
17426 'text-border-style': 'solid',
17427 'text-border-color': '#000',
17428 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17429 'font-style': 'normal',
17430 'font-weight': 'normal',
17431 'font-size': 16,
17432 'min-zoomed-font-size': 0,
17433 'text-rotation': 'none',
17434 'source-text-rotation': 'none',
17435 'target-text-rotation': 'none',
17436 'visibility': 'visible',
17437 'display': 'element',
17438 'opacity': 1,
17439 'z-compound-depth': 'auto',
17440 'z-index-compare': 'auto',
17441 'z-index': 0,
17442 'label': '',
17443 'text-margin-x': 0,
17444 'text-margin-y': 0,
17445 'source-label': '',
17446 'source-text-offset': 0,
17447 'source-text-margin-x': 0,
17448 'source-text-margin-y': 0,
17449 'target-label': '',
17450 'target-text-offset': 0,
17451 'target-text-margin-x': 0,
17452 'target-text-margin-y': 0,
17453 'overlay-opacity': 0,
17454 'overlay-color': '#000',
17455 'overlay-padding': 10,
17456 'overlay-shape': 'round-rectangle',
17457 'overlay-corner-radius': 'auto',
17458 'underlay-opacity': 0,
17459 'underlay-color': '#000',
17460 'underlay-padding': 10,
17461 'underlay-shape': 'round-rectangle',
17462 'underlay-corner-radius': 'auto',
17463 'transition-property': 'none',
17464 'transition-duration': 0,
17465 'transition-delay': 0,
17466 'transition-timing-function': 'linear',
17467 // node props
17468 'background-blacken': 0,
17469 'background-color': '#999',
17470 'background-fill': 'solid',
17471 'background-opacity': 1,
17472 'background-image': 'none',
17473 'background-image-crossorigin': 'anonymous',
17474 'background-image-opacity': 1,
17475 'background-image-containment': 'inside',
17476 'background-image-smoothing': 'yes',
17477 'background-position-x': '50%',
17478 'background-position-y': '50%',
17479 'background-offset-x': 0,
17480 'background-offset-y': 0,
17481 'background-width-relative-to': 'include-padding',
17482 'background-height-relative-to': 'include-padding',
17483 'background-repeat': 'no-repeat',
17484 'background-fit': 'none',
17485 'background-clip': 'node',
17486 'background-width': 'auto',
17487 'background-height': 'auto',
17488 'border-color': '#000',
17489 'border-opacity': 1,
17490 'border-width': 0,
17491 'border-style': 'solid',
17492 'border-dash-pattern': [4, 2],
17493 'border-dash-offset': 0,
17494 'border-cap': 'butt',
17495 'border-join': 'miter',
17496 'border-position': 'center',
17497 'outline-color': '#999',
17498 'outline-opacity': 1,
17499 'outline-width': 0,
17500 'outline-offset': 0,
17501 'outline-style': 'solid',
17502 'height': 30,
17503 'width': 30,
17504 'shape': 'ellipse',
17505 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17506 'corner-radius': 'auto',
17507 'bounds-expansion': 0,
17508 // node gradient
17509 'background-gradient-direction': 'to-bottom',
17510 'background-gradient-stop-colors': '#999',
17511 'background-gradient-stop-positions': '0%',
17512 // ghost props
17513 'ghost': 'no',
17514 'ghost-offset-y': 0,
17515 'ghost-offset-x': 0,
17516 'ghost-opacity': 0,
17517 // compound props
17518 'padding': 0,
17519 'padding-relative-to': 'width',
17520 'position': 'origin',
17521 'compound-sizing-wrt-labels': 'include',
17522 'min-width': 0,
17523 'min-width-bias-left': 0,
17524 'min-width-bias-right': 0,
17525 'min-height': 0,
17526 'min-height-bias-top': 0,
17527 'min-height-bias-bottom': 0
17528 }, {
17529 // node pie bg
17530 'pie-size': '100%'
17531 }, [{
17532 name: 'pie-{{i}}-background-color',
17533 value: 'black'
17534 }, {
17535 name: 'pie-{{i}}-background-size',
17536 value: '0%'
17537 }, {
17538 name: 'pie-{{i}}-background-opacity',
17539 value: 1
17540 }].reduce(function (css, prop) {
17541 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17542 var name = prop.name.replace('{{i}}', i);
17543 var val = prop.value;
17544 css[name] = val;
17545 }
17546 return css;
17547 }, {}), {
17548 // edge props
17549 'line-style': 'solid',
17550 'line-color': '#999',
17551 'line-fill': 'solid',
17552 'line-cap': 'butt',
17553 'line-opacity': 1,
17554 'line-outline-width': 0,
17555 'line-outline-color': '#000',
17556 'line-gradient-stop-colors': '#999',
17557 'line-gradient-stop-positions': '0%',
17558 'control-point-step-size': 40,
17559 'control-point-weights': 0.5,
17560 'segment-weights': 0.5,
17561 'segment-distances': 20,
17562 'segment-radii': 15,
17563 'radius-type': 'arc-radius',
17564 'taxi-turn': '50%',
17565 'taxi-radius': 15,
17566 'taxi-turn-min-distance': 10,
17567 'taxi-direction': 'auto',
17568 'edge-distances': 'intersection',
17569 'curve-style': 'haystack',
17570 'haystack-radius': 0,
17571 'arrow-scale': 1,
17572 'loop-direction': '-45deg',
17573 'loop-sweep': '-90deg',
17574 'source-distance-from-node': 0,
17575 'target-distance-from-node': 0,
17576 'source-endpoint': 'outside-to-node',
17577 'target-endpoint': 'outside-to-node',
17578 'line-dash-pattern': [6, 3],
17579 'line-dash-offset': 0
17580 }, [{
17581 name: 'arrow-shape',
17582 value: 'none'
17583 }, {
17584 name: 'arrow-color',
17585 value: '#999'
17586 }, {
17587 name: 'arrow-fill',
17588 value: 'filled'
17589 }, {
17590 name: 'arrow-width',
17591 value: 1
17592 }].reduce(function (css, prop) {
17593 styfn$2.arrowPrefixes.forEach(function (prefix) {
17594 var name = prefix + '-' + prop.name;
17595 var val = prop.value;
17596 css[name] = val;
17597 });
17598 return css;
17599 }, {}));
17600 var parsedProps = {};
17601 for (var i = 0; i < this.properties.length; i++) {
17602 var prop = this.properties[i];
17603 if (prop.pointsTo) {
17604 continue;
17605 }
17606 var name = prop.name;
17607 var val = rawProps[name];
17608 var parsedProp = this.parse(name, val);
17609 parsedProps[name] = parsedProp;
17610 }
17611 _p.defaultProperties = parsedProps;
17612 return _p.defaultProperties;
17613};
17614styfn$2.addDefaultStylesheet = function () {
17615 this.selector(':parent').css({
17616 'shape': 'rectangle',
17617 'padding': 10,
17618 'background-color': '#eee',
17619 'border-color': '#ccc',
17620 'border-width': 1
17621 }).selector('edge').css({
17622 'width': 3
17623 }).selector(':loop').css({
17624 'curve-style': 'bezier'
17625 }).selector('edge:compound').css({
17626 'curve-style': 'bezier',
17627 'source-endpoint': 'outside-to-line',
17628 'target-endpoint': 'outside-to-line'
17629 }).selector(':selected').css({
17630 'background-color': '#0169D9',
17631 'line-color': '#0169D9',
17632 'source-arrow-color': '#0169D9',
17633 'target-arrow-color': '#0169D9',
17634 'mid-source-arrow-color': '#0169D9',
17635 'mid-target-arrow-color': '#0169D9'
17636 }).selector(':parent:selected').css({
17637 'background-color': '#CCE1F9',
17638 'border-color': '#aec8e5'
17639 }).selector(':active').css({
17640 'overlay-color': 'black',
17641 'overlay-padding': 10,
17642 'overlay-opacity': 0.25
17643 });
17644 this.defaultLength = this.length;
17645};
17646
17647var styfn$1 = {};
17648
17649// a caching layer for property parsing
17650styfn$1.parse = function (name, value, propIsBypass, propIsFlat) {
17651 var self = this;
17652
17653 // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17654 if (fn$6(value)) {
17655 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17656 }
17657 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17658 var bypassKey = propIsBypass ? 't' : 'f';
17659 var valueKey = '' + value;
17660 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17661 var propCache = self.propCache = self.propCache || [];
17662 var ret;
17663 if (!(ret = propCache[argHash])) {
17664 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17665 }
17666
17667 // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17668 // - mappings can't be shared b/c mappings are per-element
17669 if (propIsBypass || propIsFlat === 'mapping') {
17670 // need a copy since props are mutated later in their lifecycles
17671 ret = copy(ret);
17672 if (ret) {
17673 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17674 }
17675 }
17676
17677 return ret;
17678};
17679styfn$1.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17680 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17681 if (!prop && value != null) {
17682 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17683 }
17684 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17685 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17686 }
17687 return prop;
17688};
17689
17690// parse a property; return null on invalid; return parsed property otherwise
17691// fields :
17692// - name : the name of the property
17693// - value : the parsed, native-typed value of the property
17694// - strValue : a string value that represents the property value in valid css
17695// - bypass : true iff the property is a bypass property
17696styfn$1.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17697 var self = this;
17698 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17699
17700 var property = self.properties[name];
17701 var passedValue = value;
17702 var types = self.types;
17703 if (!property) {
17704 return null;
17705 } // return null on property of unknown name
17706 if (value === undefined) {
17707 return null;
17708 } // can't assign undefined
17709
17710 // the property may be an alias
17711 if (property.alias) {
17712 property = property.pointsTo;
17713 name = property.name;
17714 }
17715 var valueIsString = string(value);
17716 if (valueIsString) {
17717 // trim the value to make parsing easier
17718 value = value.trim();
17719 }
17720 var type = property.type;
17721 if (!type) {
17722 return null;
17723 } // no type, no luck
17724
17725 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17726 if (propIsBypass && (value === '' || value === null)) {
17727 return {
17728 name: name,
17729 value: value,
17730 bypass: true,
17731 deleteBypass: true
17732 };
17733 }
17734
17735 // check if value is a function used as a mapper
17736 if (fn$6(value)) {
17737 return {
17738 name: name,
17739 value: value,
17740 strValue: 'fn',
17741 mapped: types.fn,
17742 bypass: propIsBypass
17743 };
17744 }
17745
17746 // check if value is mapped
17747 var data, mapData;
17748 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))) {
17749 if (propIsBypass) {
17750 return false;
17751 } // mappers not allowed in bypass
17752
17753 var mapped = types.data;
17754 return {
17755 name: name,
17756 value: data,
17757 strValue: '' + value,
17758 mapped: mapped,
17759 field: data[1],
17760 bypass: propIsBypass
17761 };
17762 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17763 if (propIsBypass) {
17764 return false;
17765 } // mappers not allowed in bypass
17766 if (type.multiple) {
17767 return false;
17768 } // impossible to map to num
17769
17770 var _mapped = types.mapData;
17771
17772 // we can map only if the type is a colour or a number
17773 if (!(type.color || type.number)) {
17774 return false;
17775 }
17776 var valueMin = this.parse(name, mapData[4]); // parse to validate
17777 if (!valueMin || valueMin.mapped) {
17778 return false;
17779 } // can't be invalid or mapped
17780
17781 var valueMax = this.parse(name, mapData[5]); // parse to validate
17782 if (!valueMax || valueMax.mapped) {
17783 return false;
17784 } // can't be invalid or mapped
17785
17786 // check if valueMin and valueMax are the same
17787 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17788 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17789 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17790 } else if (type.color) {
17791 var c1 = valueMin.value;
17792 var c2 = valueMax.value;
17793 var same = c1[0] === c2[0] // red
17794 && c1[1] === c2[1] // green
17795 && c1[2] === c2[2] // blue
17796 && (
17797 // optional alpha
17798 c1[3] === c2[3] // same alpha outright
17799 || (c1[3] == null || c1[3] === 1 // full opacity for colour 1?
17800 ) && (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17801 );
17802
17803 if (same) {
17804 return false;
17805 } // can't make a mapper without a range
17806 }
17807
17808 return {
17809 name: name,
17810 value: mapData,
17811 strValue: '' + value,
17812 mapped: _mapped,
17813 field: mapData[1],
17814 fieldMin: parseFloat(mapData[2]),
17815 // min & max are numeric
17816 fieldMax: parseFloat(mapData[3]),
17817 valueMin: valueMin.value,
17818 valueMax: valueMax.value,
17819 bypass: propIsBypass
17820 };
17821 }
17822 if (type.multiple && propIsFlat !== 'multiple') {
17823 var vals;
17824 if (valueIsString) {
17825 vals = value.split(/\s+/);
17826 } else if (array(value)) {
17827 vals = value;
17828 } else {
17829 vals = [value];
17830 }
17831 if (type.evenMultiple && vals.length % 2 !== 0) {
17832 return null;
17833 }
17834 var valArr = [];
17835 var unitsArr = [];
17836 var pfValArr = [];
17837 var strVal = '';
17838 var hasEnum = false;
17839 for (var i = 0; i < vals.length; i++) {
17840 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17841 hasEnum = hasEnum || string(p.value);
17842 valArr.push(p.value);
17843 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17844 unitsArr.push(p.units);
17845 strVal += (i > 0 ? ' ' : '') + p.strValue;
17846 }
17847 if (type.validate && !type.validate(valArr, unitsArr)) {
17848 return null;
17849 }
17850 if (type.singleEnum && hasEnum) {
17851 if (valArr.length === 1 && string(valArr[0])) {
17852 return {
17853 name: name,
17854 value: valArr[0],
17855 strValue: valArr[0],
17856 bypass: propIsBypass
17857 };
17858 } else {
17859 return null;
17860 }
17861 }
17862 return {
17863 name: name,
17864 value: valArr,
17865 pfValue: pfValArr,
17866 strValue: strVal,
17867 bypass: propIsBypass,
17868 units: unitsArr
17869 };
17870 }
17871
17872 // several types also allow enums
17873 var checkEnums = function checkEnums() {
17874 for (var _i = 0; _i < type.enums.length; _i++) {
17875 var en = type.enums[_i];
17876 if (en === value) {
17877 return {
17878 name: name,
17879 value: value,
17880 strValue: '' + value,
17881 bypass: propIsBypass
17882 };
17883 }
17884 }
17885 return null;
17886 };
17887
17888 // check the type and return the appropriate object
17889 if (type.number) {
17890 var units;
17891 var implicitUnits = 'px'; // not set => px
17892
17893 if (type.units) {
17894 // use specified units if set
17895 units = type.units;
17896 }
17897 if (type.implicitUnits) {
17898 implicitUnits = type.implicitUnits;
17899 }
17900 if (!type.unitless) {
17901 if (valueIsString) {
17902 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17903 if (units) {
17904 unitsRegex = units;
17905 } // only allow explicit units if so set
17906 var match = value.match('^(' + number + ')(' + unitsRegex + ')?' + '$');
17907 if (match) {
17908 value = match[1];
17909 units = match[2] || implicitUnits;
17910 }
17911 } else if (!units || type.implicitUnits) {
17912 units = implicitUnits; // implicitly px if unspecified
17913 }
17914 }
17915
17916 value = parseFloat(value);
17917
17918 // if not a number and enums not allowed, then the value is invalid
17919 if (isNaN(value) && type.enums === undefined) {
17920 return null;
17921 }
17922
17923 // check if this number type also accepts special keywords in place of numbers
17924 // (i.e. `left`, `auto`, etc)
17925 if (isNaN(value) && type.enums !== undefined) {
17926 value = passedValue;
17927 return checkEnums();
17928 }
17929
17930 // check if value must be an integer
17931 if (type.integer && !integer(value)) {
17932 return null;
17933 }
17934
17935 // check value is within range
17936 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17937 return null;
17938 }
17939 var ret = {
17940 name: name,
17941 value: value,
17942 strValue: '' + value + (units ? units : ''),
17943 units: units,
17944 bypass: propIsBypass
17945 };
17946
17947 // normalise value in pixels
17948 if (type.unitless || units !== 'px' && units !== 'em') {
17949 ret.pfValue = value;
17950 } else {
17951 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17952 }
17953
17954 // normalise value in ms
17955 if (units === 'ms' || units === 's') {
17956 ret.pfValue = units === 'ms' ? value : 1000 * value;
17957 }
17958
17959 // normalise value in rad
17960 if (units === 'deg' || units === 'rad') {
17961 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17962 }
17963
17964 // normalize value in %
17965 if (units === '%') {
17966 ret.pfValue = value / 100;
17967 }
17968 return ret;
17969 } else if (type.propList) {
17970 var props = [];
17971 var propsStr = '' + value;
17972 if (propsStr === 'none') ; else {
17973 // go over each prop
17974
17975 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17976 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17977 var propName = propsSplit[_i2].trim();
17978 if (self.properties[propName]) {
17979 props.push(propName);
17980 } else {
17981 warn('`' + propName + '` is not a valid property name');
17982 }
17983 }
17984 if (props.length === 0) {
17985 return null;
17986 }
17987 }
17988 return {
17989 name: name,
17990 value: props,
17991 strValue: props.length === 0 ? 'none' : props.join(' '),
17992 bypass: propIsBypass
17993 };
17994 } else if (type.color) {
17995 var tuple = color2tuple(value);
17996 if (!tuple) {
17997 return null;
17998 }
17999 return {
18000 name: name,
18001 value: tuple,
18002 pfValue: tuple,
18003 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
18004 // n.b. no spaces b/c of multiple support
18005 bypass: propIsBypass
18006 };
18007 } else if (type.regex || type.regexes) {
18008 // first check enums
18009 if (type.enums) {
18010 var enumProp = checkEnums();
18011 if (enumProp) {
18012 return enumProp;
18013 }
18014 }
18015 var regexes = type.regexes ? type.regexes : [type.regex];
18016 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
18017 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
18018 var m = regex.exec(value);
18019 if (m) {
18020 // regex matches
18021 return {
18022 name: name,
18023 value: type.singleRegexMatchValue ? m[1] : m,
18024 strValue: '' + value,
18025 bypass: propIsBypass
18026 };
18027 }
18028 }
18029 return null; // didn't match any
18030 } else if (type.string) {
18031 // just return
18032 return {
18033 name: name,
18034 value: '' + value,
18035 strValue: '' + value,
18036 bypass: propIsBypass
18037 };
18038 } else if (type.enums) {
18039 // check enums last because it's a combo type in others
18040 return checkEnums();
18041 } else {
18042 return null; // not a type we can handle
18043 }
18044};
18045
18046var Style = function Style(cy) {
18047 if (!(this instanceof Style)) {
18048 return new Style(cy);
18049 }
18050 if (!core(cy)) {
18051 error('A style must have a core reference');
18052 return;
18053 }
18054 this._private = {
18055 cy: cy,
18056 coreStyle: {}
18057 };
18058 this.length = 0;
18059 this.resetToDefault();
18060};
18061var styfn = Style.prototype;
18062styfn.instanceString = function () {
18063 return 'style';
18064};
18065
18066// remove all contexts
18067styfn.clear = function () {
18068 var _p = this._private;
18069 var cy = _p.cy;
18070 var eles = cy.elements();
18071 for (var i = 0; i < this.length; i++) {
18072 this[i] = undefined;
18073 }
18074 this.length = 0;
18075 _p.contextStyles = {};
18076 _p.propDiffs = {};
18077 this.cleanElements(eles, true);
18078 eles.forEach(function (ele) {
18079 var ele_p = ele[0]._private;
18080 ele_p.styleDirty = true;
18081 ele_p.appliedInitStyle = false;
18082 });
18083 return this; // chaining
18084};
18085
18086styfn.resetToDefault = function () {
18087 this.clear();
18088 this.addDefaultStylesheet();
18089 return this;
18090};
18091
18092// builds a style object for the 'core' selector
18093styfn.core = function (propName) {
18094 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
18095};
18096
18097// create a new context from the specified selector string and switch to that context
18098styfn.selector = function (selectorStr) {
18099 // 'core' is a special case and does not need a selector
18100 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
18101 var i = this.length++; // new context means new index
18102 this[i] = {
18103 selector: selector,
18104 properties: [],
18105 mappedProperties: [],
18106 index: i
18107 };
18108 return this; // chaining
18109};
18110
18111// add one or many css rules to the current context
18112styfn.css = function () {
18113 var self = this;
18114 var args = arguments;
18115 if (args.length === 1) {
18116 var map = args[0];
18117 for (var i = 0; i < self.properties.length; i++) {
18118 var prop = self.properties[i];
18119 var mapVal = map[prop.name];
18120 if (mapVal === undefined) {
18121 mapVal = map[dash2camel(prop.name)];
18122 }
18123 if (mapVal !== undefined) {
18124 this.cssRule(prop.name, mapVal);
18125 }
18126 }
18127 } else if (args.length === 2) {
18128 this.cssRule(args[0], args[1]);
18129 }
18130
18131 // do nothing if args are invalid
18132
18133 return this; // chaining
18134};
18135
18136styfn.style = styfn.css;
18137
18138// add a single css rule to the current context
18139styfn.cssRule = function (name, value) {
18140 // name-value pair
18141 var property = this.parse(name, value);
18142
18143 // add property to current context if valid
18144 if (property) {
18145 var i = this.length - 1;
18146 this[i].properties.push(property);
18147 this[i].properties[property.name] = property; // allow access by name as well
18148
18149 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
18150 this._private.hasPie = true;
18151 }
18152 if (property.mapped) {
18153 this[i].mappedProperties.push(property);
18154 }
18155
18156 // add to core style if necessary
18157 var currentSelectorIsCore = !this[i].selector;
18158 if (currentSelectorIsCore) {
18159 this._private.coreStyle[property.name] = property;
18160 }
18161 }
18162 return this; // chaining
18163};
18164
18165styfn.append = function (style) {
18166 if (stylesheet(style)) {
18167 style.appendToStyle(this);
18168 } else if (array(style)) {
18169 this.appendFromJson(style);
18170 } else if (string(style)) {
18171 this.appendFromString(style);
18172 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
18173
18174 return this;
18175};
18176
18177// static function
18178Style.fromJson = function (cy, json) {
18179 var style = new Style(cy);
18180 style.fromJson(json);
18181 return style;
18182};
18183Style.fromString = function (cy, string) {
18184 return new Style(cy).fromString(string);
18185};
18186[styfn$8, styfn$7, styfn$6, styfn$5, styfn$4, styfn$3, styfn$2, styfn$1].forEach(function (props) {
18187 extend(styfn, props);
18188});
18189Style.types = styfn.types;
18190Style.properties = styfn.properties;
18191Style.propertyGroups = styfn.propertyGroups;
18192Style.propertyGroupNames = styfn.propertyGroupNames;
18193Style.propertyGroupKeys = styfn.propertyGroupKeys;
18194
18195var corefn$2 = {
18196 style: function style(newStyle) {
18197 if (newStyle) {
18198 var s = this.setStyle(newStyle);
18199 s.update();
18200 }
18201 return this._private.style;
18202 },
18203 setStyle: function setStyle(style) {
18204 var _p = this._private;
18205 if (stylesheet(style)) {
18206 _p.style = style.generateStyle(this);
18207 } else if (array(style)) {
18208 _p.style = Style.fromJson(this, style);
18209 } else if (string(style)) {
18210 _p.style = Style.fromString(this, style);
18211 } else {
18212 _p.style = Style(this);
18213 }
18214 return _p.style;
18215 },
18216 // e.g. cy.data() changed => recalc ele mappers
18217 updateStyle: function updateStyle() {
18218 this.mutableElements().updateStyle(); // just send to all eles
18219 }
18220};
18221
18222var defaultSelectionType = 'single';
18223var corefn$1 = {
18224 autolock: function autolock(bool) {
18225 if (bool !== undefined) {
18226 this._private.autolock = bool ? true : false;
18227 } else {
18228 return this._private.autolock;
18229 }
18230 return this; // chaining
18231 },
18232
18233 autoungrabify: function autoungrabify(bool) {
18234 if (bool !== undefined) {
18235 this._private.autoungrabify = bool ? true : false;
18236 } else {
18237 return this._private.autoungrabify;
18238 }
18239 return this; // chaining
18240 },
18241
18242 autounselectify: function autounselectify(bool) {
18243 if (bool !== undefined) {
18244 this._private.autounselectify = bool ? true : false;
18245 } else {
18246 return this._private.autounselectify;
18247 }
18248 return this; // chaining
18249 },
18250
18251 selectionType: function selectionType(selType) {
18252 var _p = this._private;
18253 if (_p.selectionType == null) {
18254 _p.selectionType = defaultSelectionType;
18255 }
18256 if (selType !== undefined) {
18257 if (selType === 'additive' || selType === 'single') {
18258 _p.selectionType = selType;
18259 }
18260 } else {
18261 return _p.selectionType;
18262 }
18263 return this;
18264 },
18265 panningEnabled: function panningEnabled(bool) {
18266 if (bool !== undefined) {
18267 this._private.panningEnabled = bool ? true : false;
18268 } else {
18269 return this._private.panningEnabled;
18270 }
18271 return this; // chaining
18272 },
18273
18274 userPanningEnabled: function userPanningEnabled(bool) {
18275 if (bool !== undefined) {
18276 this._private.userPanningEnabled = bool ? true : false;
18277 } else {
18278 return this._private.userPanningEnabled;
18279 }
18280 return this; // chaining
18281 },
18282
18283 zoomingEnabled: function zoomingEnabled(bool) {
18284 if (bool !== undefined) {
18285 this._private.zoomingEnabled = bool ? true : false;
18286 } else {
18287 return this._private.zoomingEnabled;
18288 }
18289 return this; // chaining
18290 },
18291
18292 userZoomingEnabled: function userZoomingEnabled(bool) {
18293 if (bool !== undefined) {
18294 this._private.userZoomingEnabled = bool ? true : false;
18295 } else {
18296 return this._private.userZoomingEnabled;
18297 }
18298 return this; // chaining
18299 },
18300
18301 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18302 if (bool !== undefined) {
18303 this._private.boxSelectionEnabled = bool ? true : false;
18304 } else {
18305 return this._private.boxSelectionEnabled;
18306 }
18307 return this; // chaining
18308 },
18309
18310 pan: function pan() {
18311 var args = arguments;
18312 var pan = this._private.pan;
18313 var dim, val, dims, x, y;
18314 switch (args.length) {
18315 case 0:
18316 // .pan()
18317 return pan;
18318 case 1:
18319 if (string(args[0])) {
18320 // .pan('x')
18321 dim = args[0];
18322 return pan[dim];
18323 } else if (plainObject(args[0])) {
18324 // .pan({ x: 0, y: 100 })
18325 if (!this._private.panningEnabled) {
18326 return this;
18327 }
18328 dims = args[0];
18329 x = dims.x;
18330 y = dims.y;
18331 if (number$1(x)) {
18332 pan.x = x;
18333 }
18334 if (number$1(y)) {
18335 pan.y = y;
18336 }
18337 this.emit('pan viewport');
18338 }
18339 break;
18340 case 2:
18341 // .pan('x', 100)
18342 if (!this._private.panningEnabled) {
18343 return this;
18344 }
18345 dim = args[0];
18346 val = args[1];
18347 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18348 pan[dim] = val;
18349 }
18350 this.emit('pan viewport');
18351 break;
18352 // invalid
18353 }
18354
18355 this.notify('viewport');
18356 return this; // chaining
18357 },
18358
18359 panBy: function panBy(arg0, arg1) {
18360 var args = arguments;
18361 var pan = this._private.pan;
18362 var dim, val, dims, x, y;
18363 if (!this._private.panningEnabled) {
18364 return this;
18365 }
18366 switch (args.length) {
18367 case 1:
18368 if (plainObject(arg0)) {
18369 // .panBy({ x: 0, y: 100 })
18370 dims = args[0];
18371 x = dims.x;
18372 y = dims.y;
18373 if (number$1(x)) {
18374 pan.x += x;
18375 }
18376 if (number$1(y)) {
18377 pan.y += y;
18378 }
18379 this.emit('pan viewport');
18380 }
18381 break;
18382 case 2:
18383 // .panBy('x', 100)
18384 dim = arg0;
18385 val = arg1;
18386 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18387 pan[dim] += val;
18388 }
18389 this.emit('pan viewport');
18390 break;
18391 // invalid
18392 }
18393
18394 this.notify('viewport');
18395 return this; // chaining
18396 },
18397
18398 fit: function fit(elements, padding) {
18399 var viewportState = this.getFitViewport(elements, padding);
18400 if (viewportState) {
18401 var _p = this._private;
18402 _p.zoom = viewportState.zoom;
18403 _p.pan = viewportState.pan;
18404 this.emit('pan zoom viewport');
18405 this.notify('viewport');
18406 }
18407 return this; // chaining
18408 },
18409
18410 getFitViewport: function getFitViewport(elements, padding) {
18411 if (number$1(elements) && padding === undefined) {
18412 // elements is optional
18413 padding = elements;
18414 elements = undefined;
18415 }
18416 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18417 return;
18418 }
18419 var bb;
18420 if (string(elements)) {
18421 var sel = elements;
18422 elements = this.$(sel);
18423 } else if (boundingBox(elements)) {
18424 // assume bb
18425 var bbe = elements;
18426 bb = {
18427 x1: bbe.x1,
18428 y1: bbe.y1,
18429 x2: bbe.x2,
18430 y2: bbe.y2
18431 };
18432 bb.w = bb.x2 - bb.x1;
18433 bb.h = bb.y2 - bb.y1;
18434 } else if (!elementOrCollection(elements)) {
18435 elements = this.mutableElements();
18436 }
18437 if (elementOrCollection(elements) && elements.empty()) {
18438 return;
18439 } // can't fit to nothing
18440
18441 bb = bb || elements.boundingBox();
18442 var w = this.width();
18443 var h = this.height();
18444 var zoom;
18445 padding = number$1(padding) ? padding : 0;
18446 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18447 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h);
18448
18449 // crop zoom
18450 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18451 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18452 var pan = {
18453 // now pan to middle
18454 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18455 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18456 };
18457 return {
18458 zoom: zoom,
18459 pan: pan
18460 };
18461 }
18462 return;
18463 },
18464 zoomRange: function zoomRange(min, max) {
18465 var _p = this._private;
18466 if (max == null) {
18467 var opts = min;
18468 min = opts.min;
18469 max = opts.max;
18470 }
18471 if (number$1(min) && number$1(max) && min <= max) {
18472 _p.minZoom = min;
18473 _p.maxZoom = max;
18474 } else if (number$1(min) && max === undefined && min <= _p.maxZoom) {
18475 _p.minZoom = min;
18476 } else if (number$1(max) && min === undefined && max >= _p.minZoom) {
18477 _p.maxZoom = max;
18478 }
18479 return this;
18480 },
18481 minZoom: function minZoom(zoom) {
18482 if (zoom === undefined) {
18483 return this._private.minZoom;
18484 } else {
18485 return this.zoomRange({
18486 min: zoom
18487 });
18488 }
18489 },
18490 maxZoom: function maxZoom(zoom) {
18491 if (zoom === undefined) {
18492 return this._private.maxZoom;
18493 } else {
18494 return this.zoomRange({
18495 max: zoom
18496 });
18497 }
18498 },
18499 getZoomedViewport: function getZoomedViewport(params) {
18500 var _p = this._private;
18501 var currentPan = _p.pan;
18502 var currentZoom = _p.zoom;
18503 var pos; // in rendered px
18504 var zoom;
18505 var bail = false;
18506 if (!_p.zoomingEnabled) {
18507 // zooming disabled
18508 bail = true;
18509 }
18510 if (number$1(params)) {
18511 // then set the zoom
18512 zoom = params;
18513 } else if (plainObject(params)) {
18514 // then zoom about a point
18515 zoom = params.level;
18516 if (params.position != null) {
18517 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18518 } else if (params.renderedPosition != null) {
18519 pos = params.renderedPosition;
18520 }
18521 if (pos != null && !_p.panningEnabled) {
18522 // panning disabled
18523 bail = true;
18524 }
18525 }
18526
18527 // crop zoom
18528 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18529 zoom = zoom < _p.minZoom ? _p.minZoom : zoom;
18530
18531 // can't zoom with invalid params
18532 if (bail || !number$1(zoom) || zoom === currentZoom || pos != null && (!number$1(pos.x) || !number$1(pos.y))) {
18533 return null;
18534 }
18535 if (pos != null) {
18536 // set zoom about position
18537 var pan1 = currentPan;
18538 var zoom1 = currentZoom;
18539 var zoom2 = zoom;
18540 var pan2 = {
18541 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18542 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18543 };
18544 return {
18545 zoomed: true,
18546 panned: true,
18547 zoom: zoom2,
18548 pan: pan2
18549 };
18550 } else {
18551 // just set the zoom
18552 return {
18553 zoomed: true,
18554 panned: false,
18555 zoom: zoom,
18556 pan: currentPan
18557 };
18558 }
18559 },
18560 zoom: function zoom(params) {
18561 if (params === undefined) {
18562 // get
18563 return this._private.zoom;
18564 } else {
18565 // set
18566 var vp = this.getZoomedViewport(params);
18567 var _p = this._private;
18568 if (vp == null || !vp.zoomed) {
18569 return this;
18570 }
18571 _p.zoom = vp.zoom;
18572 if (vp.panned) {
18573 _p.pan.x = vp.pan.x;
18574 _p.pan.y = vp.pan.y;
18575 }
18576 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18577 this.notify('viewport');
18578 return this; // chaining
18579 }
18580 },
18581
18582 viewport: function viewport(opts) {
18583 var _p = this._private;
18584 var zoomDefd = true;
18585 var panDefd = true;
18586 var events = []; // to trigger
18587 var zoomFailed = false;
18588 var panFailed = false;
18589 if (!opts) {
18590 return this;
18591 }
18592 if (!number$1(opts.zoom)) {
18593 zoomDefd = false;
18594 }
18595 if (!plainObject(opts.pan)) {
18596 panDefd = false;
18597 }
18598 if (!zoomDefd && !panDefd) {
18599 return this;
18600 }
18601 if (zoomDefd) {
18602 var z = opts.zoom;
18603 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18604 zoomFailed = true;
18605 } else {
18606 _p.zoom = z;
18607 events.push('zoom');
18608 }
18609 }
18610 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18611 var p = opts.pan;
18612 if (number$1(p.x)) {
18613 _p.pan.x = p.x;
18614 panFailed = false;
18615 }
18616 if (number$1(p.y)) {
18617 _p.pan.y = p.y;
18618 panFailed = false;
18619 }
18620 if (!panFailed) {
18621 events.push('pan');
18622 }
18623 }
18624 if (events.length > 0) {
18625 events.push('viewport');
18626 this.emit(events.join(' '));
18627 this.notify('viewport');
18628 }
18629 return this; // chaining
18630 },
18631
18632 center: function center(elements) {
18633 var pan = this.getCenterPan(elements);
18634 if (pan) {
18635 this._private.pan = pan;
18636 this.emit('pan viewport');
18637 this.notify('viewport');
18638 }
18639 return this; // chaining
18640 },
18641
18642 getCenterPan: function getCenterPan(elements, zoom) {
18643 if (!this._private.panningEnabled) {
18644 return;
18645 }
18646 if (string(elements)) {
18647 var selector = elements;
18648 elements = this.mutableElements().filter(selector);
18649 } else if (!elementOrCollection(elements)) {
18650 elements = this.mutableElements();
18651 }
18652 if (elements.length === 0) {
18653 return;
18654 } // can't centre pan to nothing
18655
18656 var bb = elements.boundingBox();
18657 var w = this.width();
18658 var h = this.height();
18659 zoom = zoom === undefined ? this._private.zoom : zoom;
18660 var pan = {
18661 // middle
18662 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18663 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18664 };
18665 return pan;
18666 },
18667 reset: function reset() {
18668 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18669 return this;
18670 }
18671 this.viewport({
18672 pan: {
18673 x: 0,
18674 y: 0
18675 },
18676 zoom: 1
18677 });
18678 return this; // chaining
18679 },
18680
18681 invalidateSize: function invalidateSize() {
18682 this._private.sizeCache = null;
18683 },
18684 size: function size() {
18685 var _p = this._private;
18686 var container = _p.container;
18687 var cy = this;
18688 return _p.sizeCache = _p.sizeCache || (container ? function () {
18689 var style = cy.window().getComputedStyle(container);
18690 var val = function val(name) {
18691 return parseFloat(style.getPropertyValue(name));
18692 };
18693 return {
18694 width: container.clientWidth - val('padding-left') - val('padding-right'),
18695 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18696 };
18697 }() : {
18698 // fallback if no container (not 0 b/c can be used for dividing etc)
18699 width: 1,
18700 height: 1
18701 });
18702 },
18703 width: function width() {
18704 return this.size().width;
18705 },
18706 height: function height() {
18707 return this.size().height;
18708 },
18709 extent: function extent() {
18710 var pan = this._private.pan;
18711 var zoom = this._private.zoom;
18712 var rb = this.renderedExtent();
18713 var b = {
18714 x1: (rb.x1 - pan.x) / zoom,
18715 x2: (rb.x2 - pan.x) / zoom,
18716 y1: (rb.y1 - pan.y) / zoom,
18717 y2: (rb.y2 - pan.y) / zoom
18718 };
18719 b.w = b.x2 - b.x1;
18720 b.h = b.y2 - b.y1;
18721 return b;
18722 },
18723 renderedExtent: function renderedExtent() {
18724 var width = this.width();
18725 var height = this.height();
18726 return {
18727 x1: 0,
18728 y1: 0,
18729 x2: width,
18730 y2: height,
18731 w: width,
18732 h: height
18733 };
18734 },
18735 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18736 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18737 return this; // chaining
18738 }
18739};
18740
18741// aliases
18742corefn$1.centre = corefn$1.center;
18743
18744// backwards compatibility
18745corefn$1.autolockNodes = corefn$1.autolock;
18746corefn$1.autoungrabifyNodes = corefn$1.autoungrabify;
18747
18748var fn = {
18749 data: define.data({
18750 field: 'data',
18751 bindingEvent: 'data',
18752 allowBinding: true,
18753 allowSetting: true,
18754 settingEvent: 'data',
18755 settingTriggersEvent: true,
18756 triggerFnName: 'trigger',
18757 allowGetting: true,
18758 updateStyle: true
18759 }),
18760 removeData: define.removeData({
18761 field: 'data',
18762 event: 'data',
18763 triggerFnName: 'trigger',
18764 triggerEvent: true,
18765 updateStyle: true
18766 }),
18767 scratch: define.data({
18768 field: 'scratch',
18769 bindingEvent: 'scratch',
18770 allowBinding: true,
18771 allowSetting: true,
18772 settingEvent: 'scratch',
18773 settingTriggersEvent: true,
18774 triggerFnName: 'trigger',
18775 allowGetting: true,
18776 updateStyle: true
18777 }),
18778 removeScratch: define.removeData({
18779 field: 'scratch',
18780 event: 'scratch',
18781 triggerFnName: 'trigger',
18782 triggerEvent: true,
18783 updateStyle: true
18784 })
18785};
18786
18787// aliases
18788fn.attr = fn.data;
18789fn.removeAttr = fn.removeData;
18790
18791var Core = function Core(opts) {
18792 var cy = this;
18793 opts = extend({}, opts);
18794 var container = opts.container;
18795
18796 // allow for passing a wrapped jquery object
18797 // e.g. cytoscape({ container: $('#cy') })
18798 if (container && !htmlElement(container) && htmlElement(container[0])) {
18799 container = container[0];
18800 }
18801 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18802 reg = reg || {};
18803 if (reg && reg.cy) {
18804 reg.cy.destroy();
18805 reg = {}; // old instance => replace reg completely
18806 }
18807
18808 var readies = reg.readies = reg.readies || [];
18809 if (container) {
18810 container._cyreg = reg;
18811 } // make sure container assoc'd reg points to this cy
18812 reg.cy = cy;
18813 var head = _window !== undefined && container !== undefined && !opts.headless;
18814 var options = opts;
18815 options.layout = extend({
18816 name: head ? 'grid' : 'null'
18817 }, options.layout);
18818 options.renderer = extend({
18819 name: head ? 'canvas' : 'null'
18820 }, options.renderer);
18821 var defVal = function defVal(def, val, altVal) {
18822 if (val !== undefined) {
18823 return val;
18824 } else if (altVal !== undefined) {
18825 return altVal;
18826 } else {
18827 return def;
18828 }
18829 };
18830 var _p = this._private = {
18831 container: container,
18832 // html dom ele container
18833 ready: false,
18834 // whether ready has been triggered
18835 options: options,
18836 // cached options
18837 elements: new Collection(this),
18838 // elements in the graph
18839 listeners: [],
18840 // list of listeners
18841 aniEles: new Collection(this),
18842 // elements being animated
18843 data: options.data || {},
18844 // data for the core
18845 scratch: {},
18846 // scratch object for core
18847 layout: null,
18848 renderer: null,
18849 destroyed: false,
18850 // whether destroy was called
18851 notificationsEnabled: true,
18852 // whether notifications are sent to the renderer
18853 minZoom: 1e-50,
18854 maxZoom: 1e50,
18855 zoomingEnabled: defVal(true, options.zoomingEnabled),
18856 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18857 panningEnabled: defVal(true, options.panningEnabled),
18858 userPanningEnabled: defVal(true, options.userPanningEnabled),
18859 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18860 autolock: defVal(false, options.autolock, options.autolockNodes),
18861 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18862 autounselectify: defVal(false, options.autounselectify),
18863 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18864 zoom: number$1(options.zoom) ? options.zoom : 1,
18865 pan: {
18866 x: plainObject(options.pan) && number$1(options.pan.x) ? options.pan.x : 0,
18867 y: plainObject(options.pan) && number$1(options.pan.y) ? options.pan.y : 0
18868 },
18869 animation: {
18870 // object for currently-running animations
18871 current: [],
18872 queue: []
18873 },
18874 hasCompoundNodes: false,
18875 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18876 };
18877 this.createEmitter();
18878
18879 // set selection type
18880 this.selectionType(options.selectionType);
18881
18882 // init zoom bounds
18883 this.zoomRange({
18884 min: options.minZoom,
18885 max: options.maxZoom
18886 });
18887 var loadExtData = function loadExtData(extData, next) {
18888 var anyIsPromise = extData.some(promise);
18889 if (anyIsPromise) {
18890 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18891 } else {
18892 next(extData); // exec synchronously for convenience
18893 }
18894 };
18895
18896 // start with the default stylesheet so we have something before loading an external stylesheet
18897 if (_p.styleEnabled) {
18898 cy.setStyle([]);
18899 }
18900
18901 // create the renderer
18902 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18903 cy.initRenderer(rendererOptions);
18904 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18905 cy.notifications(false);
18906
18907 // remove old elements
18908 var oldEles = cy.mutableElements();
18909 if (oldEles.length > 0) {
18910 oldEles.remove();
18911 }
18912 if (elements != null) {
18913 if (plainObject(elements) || array(elements)) {
18914 cy.add(elements);
18915 }
18916 }
18917 cy.one('layoutready', function (e) {
18918 cy.notifications(true);
18919 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18920
18921 cy.one('load', onload);
18922 cy.emitAndNotify('load');
18923 }).one('layoutstop', function () {
18924 cy.one('done', ondone);
18925 cy.emit('done');
18926 });
18927 var layoutOpts = extend({}, cy._private.options.layout);
18928 layoutOpts.eles = cy.elements();
18929 cy.layout(layoutOpts).run();
18930 };
18931 loadExtData([options.style, options.elements], function (thens) {
18932 var initStyle = thens[0];
18933 var initEles = thens[1];
18934
18935 // init style
18936 if (_p.styleEnabled) {
18937 cy.style().append(initStyle);
18938 }
18939
18940 // initial load
18941 setElesAndLayout(initEles, function () {
18942 // onready
18943 cy.startAnimationLoop();
18944 _p.ready = true;
18945
18946 // if a ready callback is specified as an option, the bind it
18947 if (fn$6(options.ready)) {
18948 cy.on('ready', options.ready);
18949 }
18950
18951 // bind all the ready handlers registered before creating this instance
18952 for (var i = 0; i < readies.length; i++) {
18953 var fn = readies[i];
18954 cy.on('ready', fn);
18955 }
18956 if (reg) {
18957 reg.readies = [];
18958 } // 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
18959
18960 cy.emit('ready');
18961 }, options.done);
18962 });
18963};
18964var corefn = Core.prototype; // short alias
18965
18966extend(corefn, {
18967 instanceString: function instanceString() {
18968 return 'core';
18969 },
18970 isReady: function isReady() {
18971 return this._private.ready;
18972 },
18973 destroyed: function destroyed() {
18974 return this._private.destroyed;
18975 },
18976 ready: function ready(fn) {
18977 if (this.isReady()) {
18978 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18979 } else {
18980 this.on('ready', fn);
18981 }
18982 return this;
18983 },
18984 destroy: function destroy() {
18985 var cy = this;
18986 if (cy.destroyed()) return;
18987 cy.stopAnimationLoop();
18988 cy.destroyRenderer();
18989 this.emit('destroy');
18990 cy._private.destroyed = true;
18991 return cy;
18992 },
18993 hasElementWithId: function hasElementWithId(id) {
18994 return this._private.elements.hasElementWithId(id);
18995 },
18996 getElementById: function getElementById(id) {
18997 return this._private.elements.getElementById(id);
18998 },
18999 hasCompoundNodes: function hasCompoundNodes() {
19000 return this._private.hasCompoundNodes;
19001 },
19002 headless: function headless() {
19003 return this._private.renderer.isHeadless();
19004 },
19005 styleEnabled: function styleEnabled() {
19006 return this._private.styleEnabled;
19007 },
19008 addToPool: function addToPool(eles) {
19009 this._private.elements.merge(eles);
19010 return this; // chaining
19011 },
19012
19013 removeFromPool: function removeFromPool(eles) {
19014 this._private.elements.unmerge(eles);
19015 return this;
19016 },
19017 container: function container() {
19018 return this._private.container || null;
19019 },
19020 window: function window() {
19021 var container = this._private.container;
19022 if (container == null) return _window;
19023 var ownerDocument = this._private.container.ownerDocument;
19024 if (ownerDocument === undefined || ownerDocument == null) {
19025 return _window;
19026 }
19027 return ownerDocument.defaultView || _window;
19028 },
19029 mount: function mount(container) {
19030 if (container == null) {
19031 return;
19032 }
19033 var cy = this;
19034 var _p = cy._private;
19035 var options = _p.options;
19036 if (!htmlElement(container) && htmlElement(container[0])) {
19037 container = container[0];
19038 }
19039 cy.stopAnimationLoop();
19040 cy.destroyRenderer();
19041 _p.container = container;
19042 _p.styleEnabled = true;
19043 cy.invalidateSize();
19044 cy.initRenderer(extend({}, options, options.renderer, {
19045 // allow custom renderer name to be re-used, otherwise use canvas
19046 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
19047 }));
19048 cy.startAnimationLoop();
19049 cy.style(options.style);
19050 cy.emit('mount');
19051 return cy;
19052 },
19053 unmount: function unmount() {
19054 var cy = this;
19055 cy.stopAnimationLoop();
19056 cy.destroyRenderer();
19057 cy.initRenderer({
19058 name: 'null'
19059 });
19060 cy.emit('unmount');
19061 return cy;
19062 },
19063 options: function options() {
19064 return copy(this._private.options);
19065 },
19066 json: function json(obj) {
19067 var cy = this;
19068 var _p = cy._private;
19069 var eles = cy.mutableElements();
19070 var getFreshRef = function getFreshRef(ele) {
19071 return cy.getElementById(ele.id());
19072 };
19073 if (plainObject(obj)) {
19074 // set
19075
19076 cy.startBatch();
19077 if (obj.elements) {
19078 var idInJson = {};
19079 var updateEles = function updateEles(jsons, gr) {
19080 var toAdd = [];
19081 var toMod = [];
19082 for (var i = 0; i < jsons.length; i++) {
19083 var json = jsons[i];
19084 if (!json.data.id) {
19085 warn('cy.json() cannot handle elements without an ID attribute');
19086 continue;
19087 }
19088 var id = '' + json.data.id; // id must be string
19089 var ele = cy.getElementById(id);
19090 idInJson[id] = true;
19091 if (ele.length !== 0) {
19092 // existing element should be updated
19093 toMod.push({
19094 ele: ele,
19095 json: json
19096 });
19097 } else {
19098 // otherwise should be added
19099 if (gr) {
19100 json.group = gr;
19101 toAdd.push(json);
19102 } else {
19103 toAdd.push(json);
19104 }
19105 }
19106 }
19107 cy.add(toAdd);
19108 for (var _i = 0; _i < toMod.length; _i++) {
19109 var _toMod$_i = toMod[_i],
19110 _ele = _toMod$_i.ele,
19111 _json = _toMod$_i.json;
19112 _ele.json(_json);
19113 }
19114 };
19115 if (array(obj.elements)) {
19116 // elements: []
19117 updateEles(obj.elements);
19118 } else {
19119 // elements: { nodes: [], edges: [] }
19120 var grs = ['nodes', 'edges'];
19121 for (var i = 0; i < grs.length; i++) {
19122 var gr = grs[i];
19123 var elements = obj.elements[gr];
19124 if (array(elements)) {
19125 updateEles(elements, gr);
19126 }
19127 }
19128 }
19129 var parentsToRemove = cy.collection();
19130 eles.filter(function (ele) {
19131 return !idInJson[ele.id()];
19132 }).forEach(function (ele) {
19133 if (ele.isParent()) {
19134 parentsToRemove.merge(ele);
19135 } else {
19136 ele.remove();
19137 }
19138 });
19139
19140 // so that children are not removed w/parent
19141 parentsToRemove.forEach(function (ele) {
19142 return ele.children().move({
19143 parent: null
19144 });
19145 });
19146
19147 // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
19148 parentsToRemove.forEach(function (ele) {
19149 return getFreshRef(ele).remove();
19150 });
19151 }
19152 if (obj.style) {
19153 cy.style(obj.style);
19154 }
19155 if (obj.zoom != null && obj.zoom !== _p.zoom) {
19156 cy.zoom(obj.zoom);
19157 }
19158 if (obj.pan) {
19159 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
19160 cy.pan(obj.pan);
19161 }
19162 }
19163 if (obj.data) {
19164 cy.data(obj.data);
19165 }
19166 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
19167 for (var _i2 = 0; _i2 < fields.length; _i2++) {
19168 var f = fields[_i2];
19169 if (obj[f] != null) {
19170 cy[f](obj[f]);
19171 }
19172 }
19173 cy.endBatch();
19174 return this; // chaining
19175 } else {
19176 // get
19177 var flat = !!obj;
19178 var json = {};
19179 if (flat) {
19180 json.elements = this.elements().map(function (ele) {
19181 return ele.json();
19182 });
19183 } else {
19184 json.elements = {};
19185 eles.forEach(function (ele) {
19186 var group = ele.group();
19187 if (!json.elements[group]) {
19188 json.elements[group] = [];
19189 }
19190 json.elements[group].push(ele.json());
19191 });
19192 }
19193 if (this._private.styleEnabled) {
19194 json.style = cy.style().json();
19195 }
19196 json.data = copy(cy.data());
19197 var options = _p.options;
19198 json.zoomingEnabled = _p.zoomingEnabled;
19199 json.userZoomingEnabled = _p.userZoomingEnabled;
19200 json.zoom = _p.zoom;
19201 json.minZoom = _p.minZoom;
19202 json.maxZoom = _p.maxZoom;
19203 json.panningEnabled = _p.panningEnabled;
19204 json.userPanningEnabled = _p.userPanningEnabled;
19205 json.pan = copy(_p.pan);
19206 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19207 json.renderer = copy(options.renderer);
19208 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19209 json.textureOnViewport = options.textureOnViewport;
19210 json.wheelSensitivity = options.wheelSensitivity;
19211 json.motionBlur = options.motionBlur;
19212 json.multiClickDebounceTime = options.multiClickDebounceTime;
19213 return json;
19214 }
19215 }
19216});
19217corefn.$id = corefn.getElementById;
19218[corefn$9, corefn$8, elesfn, corefn$7, corefn$6, corefn$5, corefn$4, corefn$3, corefn$2, corefn$1, fn].forEach(function (props) {
19219 extend(corefn, props);
19220});
19221
19222/* eslint-disable no-unused-vars */
19223var defaults$7 = {
19224 fit: true,
19225 // whether to fit the viewport to the graph
19226 directed: false,
19227 // whether the tree is directed downwards (or edges can point in any direction if false)
19228 padding: 30,
19229 // padding on fit
19230 circle: false,
19231 // put depths in concentric circles if true, put depths top down if false
19232 grid: false,
19233 // whether to create an even grid into which the DAG is placed (circle:false only)
19234 spacingFactor: 1.75,
19235 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19236 boundingBox: undefined,
19237 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19238 avoidOverlap: true,
19239 // prevents node overlap, may overflow boundingBox if not enough space
19240 nodeDimensionsIncludeLabels: false,
19241 // Excludes the label when calculating node bounding boxes for the layout algorithm
19242 roots: undefined,
19243 // the roots of the trees
19244 depthSort: undefined,
19245 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19246 animate: false,
19247 // whether to transition the node positions
19248 animationDuration: 500,
19249 // duration of animation in ms if enabled
19250 animationEasing: undefined,
19251 // easing of animation if enabled,
19252 animateFilter: function animateFilter(node, i) {
19253 return true;
19254 },
19255 // 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
19256 ready: undefined,
19257 // callback on layoutready
19258 stop: undefined,
19259 // callback on layoutstop
19260 transform: function transform(node, position) {
19261 return position;
19262 } // transform a given node position. Useful for changing flow direction in discrete layouts
19263};
19264
19265var deprecatedOptionDefaults = {
19266 maximal: false,
19267 // 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
19268 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
19269};
19270
19271/* eslint-enable */
19272
19273var getInfo = function getInfo(ele) {
19274 return ele.scratch('breadthfirst');
19275};
19276var setInfo = function setInfo(ele, obj) {
19277 return ele.scratch('breadthfirst', obj);
19278};
19279function BreadthFirstLayout(options) {
19280 this.options = extend({}, defaults$7, deprecatedOptionDefaults, options);
19281}
19282BreadthFirstLayout.prototype.run = function () {
19283 var params = this.options;
19284 var options = params;
19285 var cy = params.cy;
19286 var eles = options.eles;
19287 var nodes = eles.nodes().filter(function (n) {
19288 return !n.isParent();
19289 });
19290 var graph = eles;
19291 var directed = options.directed;
19292 var maximal = options.acyclic || options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code; also, setting acyclic to true sets maximal to true
19293
19294 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19295 x1: 0,
19296 y1: 0,
19297 w: cy.width(),
19298 h: cy.height()
19299 });
19300 var roots;
19301 if (elementOrCollection(options.roots)) {
19302 roots = options.roots;
19303 } else if (array(options.roots)) {
19304 var rootsArray = [];
19305 for (var i = 0; i < options.roots.length; i++) {
19306 var id = options.roots[i];
19307 var ele = cy.getElementById(id);
19308 rootsArray.push(ele);
19309 }
19310 roots = cy.collection(rootsArray);
19311 } else if (string(options.roots)) {
19312 roots = cy.$(options.roots);
19313 } else {
19314 if (directed) {
19315 roots = nodes.roots();
19316 } else {
19317 var components = eles.components();
19318 roots = cy.collection();
19319 var _loop = function _loop(_i) {
19320 var comp = components[_i];
19321 var maxDegree = comp.maxDegree(false);
19322 var compRoots = comp.filter(function (ele) {
19323 return ele.degree(false) === maxDegree;
19324 });
19325 roots = roots.add(compRoots);
19326 };
19327 for (var _i = 0; _i < components.length; _i++) {
19328 _loop(_i);
19329 }
19330 }
19331 }
19332 var depths = [];
19333 var foundByBfs = {};
19334 var addToDepth = function addToDepth(ele, d) {
19335 if (depths[d] == null) {
19336 depths[d] = [];
19337 }
19338 var i = depths[d].length;
19339 depths[d].push(ele);
19340 setInfo(ele, {
19341 index: i,
19342 depth: d
19343 });
19344 };
19345 var changeDepth = function changeDepth(ele, newDepth) {
19346 var _getInfo = getInfo(ele),
19347 depth = _getInfo.depth,
19348 index = _getInfo.index;
19349 depths[depth][index] = null;
19350 addToDepth(ele, newDepth);
19351 };
19352
19353 // find the depths of the nodes
19354 graph.bfs({
19355 roots: roots,
19356 directed: options.directed,
19357 visit: function visit(node, edge, pNode, i, depth) {
19358 var ele = node[0];
19359 var id = ele.id();
19360 addToDepth(ele, depth);
19361 foundByBfs[id] = true;
19362 }
19363 });
19364
19365 // check for nodes not found by bfs
19366 var orphanNodes = [];
19367 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19368 var _ele = nodes[_i2];
19369 if (foundByBfs[_ele.id()]) {
19370 continue;
19371 } else {
19372 orphanNodes.push(_ele);
19373 }
19374 }
19375
19376 // assign the nodes a depth and index
19377
19378 var assignDepthsAt = function assignDepthsAt(i) {
19379 var eles = depths[i];
19380 for (var j = 0; j < eles.length; j++) {
19381 var _ele2 = eles[j];
19382 if (_ele2 == null) {
19383 eles.splice(j, 1);
19384 j--;
19385 continue;
19386 }
19387 setInfo(_ele2, {
19388 depth: i,
19389 index: j
19390 });
19391 }
19392 };
19393 var assignDepths = function assignDepths() {
19394 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19395 assignDepthsAt(_i3);
19396 }
19397 };
19398 var adjustMaximally = function adjustMaximally(ele, shifted) {
19399 var eInfo = getInfo(ele);
19400 var incomers = ele.incomers().filter(function (el) {
19401 return el.isNode() && eles.has(el);
19402 });
19403 var maxDepth = -1;
19404 var id = ele.id();
19405 for (var k = 0; k < incomers.length; k++) {
19406 var incmr = incomers[k];
19407 var iInfo = getInfo(incmr);
19408 maxDepth = Math.max(maxDepth, iInfo.depth);
19409 }
19410 if (eInfo.depth <= maxDepth) {
19411 if (!options.acyclic && shifted[id]) {
19412 return null;
19413 }
19414 var newDepth = maxDepth + 1;
19415 changeDepth(ele, newDepth);
19416 shifted[id] = newDepth;
19417 return true;
19418 }
19419 return false;
19420 };
19421
19422 // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19423 if (directed && maximal) {
19424 var Q = [];
19425 var shifted = {};
19426 var enqueue = function enqueue(n) {
19427 return Q.push(n);
19428 };
19429 var dequeue = function dequeue() {
19430 return Q.shift();
19431 };
19432 nodes.forEach(function (n) {
19433 return Q.push(n);
19434 });
19435 while (Q.length > 0) {
19436 var _ele3 = dequeue();
19437 var didShift = adjustMaximally(_ele3, shifted);
19438 if (didShift) {
19439 _ele3.outgoers().filter(function (el) {
19440 return el.isNode() && eles.has(el);
19441 }).forEach(enqueue);
19442 } else if (didShift === null) {
19443 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19444 break; // exit on failure
19445 }
19446 }
19447 }
19448
19449 assignDepths(); // clear holes
19450
19451 // find min distance we need to leave between nodes
19452 var minDistance = 0;
19453 if (options.avoidOverlap) {
19454 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19455 var n = nodes[_i4];
19456 var nbb = n.layoutDimensions(options);
19457 var w = nbb.w;
19458 var h = nbb.h;
19459 minDistance = Math.max(minDistance, w, h);
19460 }
19461 }
19462
19463 // get the weighted percent for an element based on its connectivity to other levels
19464 var cachedWeightedPercent = {};
19465 var getWeightedPercent = function getWeightedPercent(ele) {
19466 if (cachedWeightedPercent[ele.id()]) {
19467 return cachedWeightedPercent[ele.id()];
19468 }
19469 var eleDepth = getInfo(ele).depth;
19470 var neighbors = ele.neighborhood();
19471 var percent = 0;
19472 var samples = 0;
19473 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19474 var neighbor = neighbors[_i5];
19475 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19476 continue;
19477 }
19478 var bf = getInfo(neighbor);
19479 if (bf == null) {
19480 continue;
19481 }
19482 var index = bf.index;
19483 var depth = bf.depth;
19484
19485 // unassigned neighbours shouldn't affect the ordering
19486 if (index == null || depth == null) {
19487 continue;
19488 }
19489 var nDepth = depths[depth].length;
19490 if (depth < eleDepth) {
19491 // only get influenced by elements above
19492 percent += index / nDepth;
19493 samples++;
19494 }
19495 }
19496 samples = Math.max(1, samples);
19497 percent = percent / samples;
19498 if (samples === 0) {
19499 // put lone nodes at the start
19500 percent = 0;
19501 }
19502 cachedWeightedPercent[ele.id()] = percent;
19503 return percent;
19504 };
19505
19506 // rearrange the indices in each depth level based on connectivity
19507
19508 var sortFn = function sortFn(a, b) {
19509 var apct = getWeightedPercent(a);
19510 var bpct = getWeightedPercent(b);
19511 var diff = apct - bpct;
19512 if (diff === 0) {
19513 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19514 } else {
19515 return diff;
19516 }
19517 };
19518 if (options.depthSort !== undefined) {
19519 sortFn = options.depthSort;
19520 }
19521
19522 // sort each level to make connected nodes closer
19523 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19524 depths[_i6].sort(sortFn);
19525 assignDepthsAt(_i6);
19526 }
19527
19528 // assign orphan nodes to a new top-level depth
19529 var orphanDepth = [];
19530 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19531 orphanDepth.push(orphanNodes[_i7]);
19532 }
19533 depths.unshift(orphanDepth);
19534 assignDepths();
19535 var biggestDepthSize = 0;
19536 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19537 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19538 }
19539 var center = {
19540 x: bb.x1 + bb.w / 2,
19541 y: bb.x1 + bb.h / 2
19542 };
19543 var maxDepthSize = depths.reduce(function (max, eles) {
19544 return Math.max(max, eles.length);
19545 }, 0);
19546 var getPosition = function getPosition(ele) {
19547 var _getInfo2 = getInfo(ele),
19548 depth = _getInfo2.depth,
19549 index = _getInfo2.index;
19550 var depthSize = depths[depth].length;
19551 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19552 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19553 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19554 radiusStepSize = Math.max(radiusStepSize, minDistance);
19555 if (!options.circle) {
19556 var epos = {
19557 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19558 y: (depth + 1) * distanceY
19559 };
19560 return epos;
19561 } else {
19562 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19563 var theta = 2 * Math.PI / depths[depth].length * index;
19564 if (depth === 0 && depths[0].length === 1) {
19565 radius = 1;
19566 }
19567 return {
19568 x: center.x + radius * Math.cos(theta),
19569 y: center.y + radius * Math.sin(theta)
19570 };
19571 }
19572 };
19573 eles.nodes().layoutPositions(this, options, getPosition);
19574 return this; // chaining
19575};
19576
19577var defaults$6 = {
19578 fit: true,
19579 // whether to fit the viewport to the graph
19580 padding: 30,
19581 // the padding on fit
19582 boundingBox: undefined,
19583 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19584 avoidOverlap: true,
19585 // prevents node overlap, may overflow boundingBox and radius if not enough space
19586 nodeDimensionsIncludeLabels: false,
19587 // Excludes the label when calculating node bounding boxes for the layout algorithm
19588 spacingFactor: undefined,
19589 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19590 radius: undefined,
19591 // the radius of the circle
19592 startAngle: 3 / 2 * Math.PI,
19593 // where nodes start in radians
19594 sweep: undefined,
19595 // how many radians should be between the first and last node (defaults to full circle)
19596 clockwise: true,
19597 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19598 sort: undefined,
19599 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19600 animate: false,
19601 // whether to transition the node positions
19602 animationDuration: 500,
19603 // duration of animation in ms if enabled
19604 animationEasing: undefined,
19605 // easing of animation if enabled
19606 animateFilter: function animateFilter(node, i) {
19607 return true;
19608 },
19609 // 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
19610 ready: undefined,
19611 // callback on layoutready
19612 stop: undefined,
19613 // callback on layoutstop
19614 transform: function transform(node, position) {
19615 return position;
19616 } // transform a given node position. Useful for changing flow direction in discrete layouts
19617};
19618
19619function CircleLayout(options) {
19620 this.options = extend({}, defaults$6, options);
19621}
19622CircleLayout.prototype.run = function () {
19623 var params = this.options;
19624 var options = params;
19625 var cy = params.cy;
19626 var eles = options.eles;
19627 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19628 var nodes = eles.nodes().not(':parent');
19629 if (options.sort) {
19630 nodes = nodes.sort(options.sort);
19631 }
19632 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19633 x1: 0,
19634 y1: 0,
19635 w: cy.width(),
19636 h: cy.height()
19637 });
19638 var center = {
19639 x: bb.x1 + bb.w / 2,
19640 y: bb.y1 + bb.h / 2
19641 };
19642 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19643 var dTheta = sweep / Math.max(1, nodes.length - 1);
19644 var r;
19645 var minDistance = 0;
19646 for (var i = 0; i < nodes.length; i++) {
19647 var n = nodes[i];
19648 var nbb = n.layoutDimensions(options);
19649 var w = nbb.w;
19650 var h = nbb.h;
19651 minDistance = Math.max(minDistance, w, h);
19652 }
19653 if (number$1(options.radius)) {
19654 r = options.radius;
19655 } else if (nodes.length <= 1) {
19656 r = 0;
19657 } else {
19658 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19659 }
19660
19661 // calculate the radius
19662 if (nodes.length > 1 && options.avoidOverlap) {
19663 // but only if more than one node (can't overlap)
19664 minDistance *= 1.75; // just to have some nice spacing
19665
19666 var dcos = Math.cos(dTheta) - Math.cos(0);
19667 var dsin = Math.sin(dTheta) - Math.sin(0);
19668 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19669 r = Math.max(rMin, r);
19670 }
19671 var getPos = function getPos(ele, i) {
19672 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19673 var rx = r * Math.cos(theta);
19674 var ry = r * Math.sin(theta);
19675 var pos = {
19676 x: center.x + rx,
19677 y: center.y + ry
19678 };
19679 return pos;
19680 };
19681 eles.nodes().layoutPositions(this, options, getPos);
19682 return this; // chaining
19683};
19684
19685var defaults$5 = {
19686 fit: true,
19687 // whether to fit the viewport to the graph
19688 padding: 30,
19689 // the padding on fit
19690 startAngle: 3 / 2 * Math.PI,
19691 // where nodes start in radians
19692 sweep: undefined,
19693 // how many radians should be between the first and last node (defaults to full circle)
19694 clockwise: true,
19695 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19696 equidistant: false,
19697 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19698 minNodeSpacing: 10,
19699 // min spacing between outside of nodes (used for radius adjustment)
19700 boundingBox: undefined,
19701 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19702 avoidOverlap: true,
19703 // prevents node overlap, may overflow boundingBox if not enough space
19704 nodeDimensionsIncludeLabels: false,
19705 // Excludes the label when calculating node bounding boxes for the layout algorithm
19706 height: undefined,
19707 // height of layout area (overrides container height)
19708 width: undefined,
19709 // width of layout area (overrides container width)
19710 spacingFactor: undefined,
19711 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19712 concentric: function concentric(node) {
19713 // returns numeric value for each node, placing higher nodes in levels towards the centre
19714 return node.degree();
19715 },
19716 levelWidth: function levelWidth(nodes) {
19717 // the variation of concentric values in each level
19718 return nodes.maxDegree() / 4;
19719 },
19720 animate: false,
19721 // whether to transition the node positions
19722 animationDuration: 500,
19723 // duration of animation in ms if enabled
19724 animationEasing: undefined,
19725 // easing of animation if enabled
19726 animateFilter: function animateFilter(node, i) {
19727 return true;
19728 },
19729 // 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
19730 ready: undefined,
19731 // callback on layoutready
19732 stop: undefined,
19733 // callback on layoutstop
19734 transform: function transform(node, position) {
19735 return position;
19736 } // transform a given node position. Useful for changing flow direction in discrete layouts
19737};
19738
19739function ConcentricLayout(options) {
19740 this.options = extend({}, defaults$5, options);
19741}
19742ConcentricLayout.prototype.run = function () {
19743 var params = this.options;
19744 var options = params;
19745 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19746 var cy = params.cy;
19747 var eles = options.eles;
19748 var nodes = eles.nodes().not(':parent');
19749 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19750 x1: 0,
19751 y1: 0,
19752 w: cy.width(),
19753 h: cy.height()
19754 });
19755 var center = {
19756 x: bb.x1 + bb.w / 2,
19757 y: bb.y1 + bb.h / 2
19758 };
19759 var nodeValues = []; // { node, value }
19760 var maxNodeSize = 0;
19761 for (var i = 0; i < nodes.length; i++) {
19762 var node = nodes[i];
19763 var value = void 0;
19764
19765 // calculate the node value
19766 value = options.concentric(node);
19767 nodeValues.push({
19768 value: value,
19769 node: node
19770 });
19771
19772 // for style mapping
19773 node._private.scratch.concentric = value;
19774 }
19775
19776 // in case we used the `concentric` in style
19777 nodes.updateStyle();
19778
19779 // calculate max size now based on potentially updated mappers
19780 for (var _i = 0; _i < nodes.length; _i++) {
19781 var _node = nodes[_i];
19782 var nbb = _node.layoutDimensions(options);
19783 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19784 }
19785
19786 // sort node values in descreasing order
19787 nodeValues.sort(function (a, b) {
19788 return b.value - a.value;
19789 });
19790 var levelWidth = options.levelWidth(nodes);
19791
19792 // put the values into levels
19793 var levels = [[]];
19794 var currentLevel = levels[0];
19795 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19796 var val = nodeValues[_i2];
19797 if (currentLevel.length > 0) {
19798 var diff = Math.abs(currentLevel[0].value - val.value);
19799 if (diff >= levelWidth) {
19800 currentLevel = [];
19801 levels.push(currentLevel);
19802 }
19803 }
19804 currentLevel.push(val);
19805 }
19806
19807 // create positions from levels
19808
19809 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19810
19811 if (!options.avoidOverlap) {
19812 // then strictly constrain to bb
19813 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19814 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19815 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19816 minDist = Math.min(minDist, rStep);
19817 }
19818
19819 // find the metrics for each level
19820 var r = 0;
19821 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19822 var level = levels[_i3];
19823 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19824 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1);
19825
19826 // calculate the radius
19827 if (level.length > 1 && options.avoidOverlap) {
19828 // but only if more than one node (can't overlap)
19829 var dcos = Math.cos(dTheta) - Math.cos(0);
19830 var dsin = Math.sin(dTheta) - Math.sin(0);
19831 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19832
19833 r = Math.max(rMin, r);
19834 }
19835 level.r = r;
19836 r += minDist;
19837 }
19838 if (options.equidistant) {
19839 var rDeltaMax = 0;
19840 var _r = 0;
19841 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19842 var _level = levels[_i4];
19843 var rDelta = _level.r - _r;
19844 rDeltaMax = Math.max(rDeltaMax, rDelta);
19845 }
19846 _r = 0;
19847 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19848 var _level2 = levels[_i5];
19849 if (_i5 === 0) {
19850 _r = _level2.r;
19851 }
19852 _level2.r = _r;
19853 _r += rDeltaMax;
19854 }
19855 }
19856
19857 // calculate the node positions
19858 var pos = {}; // id => position
19859 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19860 var _level3 = levels[_i6];
19861 var _dTheta = _level3.dTheta;
19862 var _r2 = _level3.r;
19863 for (var j = 0; j < _level3.length; j++) {
19864 var _val = _level3[j];
19865 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19866 var p = {
19867 x: center.x + _r2 * Math.cos(theta),
19868 y: center.y + _r2 * Math.sin(theta)
19869 };
19870 pos[_val.node.id()] = p;
19871 }
19872 }
19873
19874 // position the nodes
19875 eles.nodes().layoutPositions(this, options, function (ele) {
19876 var id = ele.id();
19877 return pos[id];
19878 });
19879 return this; // chaining
19880};
19881
19882/*
19883The CoSE layout was written by Gerardo Huck.
19884https://www.linkedin.com/in/gerardohuck/
19885
19886Based on the following article:
19887http://dl.acm.org/citation.cfm?id=1498047
19888
19889Modifications tracked on Github.
19890*/
19891var DEBUG;
19892
19893/**
19894 * @brief : default layout options
19895 */
19896var defaults$4 = {
19897 // Called on `layoutready`
19898 ready: function ready() {},
19899 // Called on `layoutstop`
19900 stop: function stop() {},
19901 // Whether to animate while running the layout
19902 // true : Animate continuously as the layout is running
19903 // false : Just show the end result
19904 // 'end' : Animate with the end result, from the initial positions to the end positions
19905 animate: true,
19906 // Easing of the animation for animate:'end'
19907 animationEasing: undefined,
19908 // The duration of the animation for animate:'end'
19909 animationDuration: undefined,
19910 // A function that determines whether the node should be animated
19911 // All nodes animated by default on animate enabled
19912 // Non-animated nodes are positioned immediately when the layout starts
19913 animateFilter: function animateFilter(node, i) {
19914 return true;
19915 },
19916 // The layout animates only after this many milliseconds for animate:true
19917 // (prevents flashing on fast runs)
19918 animationThreshold: 250,
19919 // Number of iterations between consecutive screen positions update
19920 refresh: 20,
19921 // Whether to fit the network view after when done
19922 fit: true,
19923 // Padding on fit
19924 padding: 30,
19925 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19926 boundingBox: undefined,
19927 // Excludes the label when calculating node bounding boxes for the layout algorithm
19928 nodeDimensionsIncludeLabels: false,
19929 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19930 randomize: false,
19931 // Extra spacing between components in non-compound graphs
19932 componentSpacing: 40,
19933 // Node repulsion (non overlapping) multiplier
19934 nodeRepulsion: function nodeRepulsion(node) {
19935 return 2048;
19936 },
19937 // Node repulsion (overlapping) multiplier
19938 nodeOverlap: 4,
19939 // Ideal edge (non nested) length
19940 idealEdgeLength: function idealEdgeLength(edge) {
19941 return 32;
19942 },
19943 // Divisor to compute edge forces
19944 edgeElasticity: function edgeElasticity(edge) {
19945 return 32;
19946 },
19947 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19948 nestingFactor: 1.2,
19949 // Gravity force (constant)
19950 gravity: 1,
19951 // Maximum number of iterations to perform
19952 numIter: 1000,
19953 // Initial temperature (maximum node displacement)
19954 initialTemp: 1000,
19955 // Cooling factor (how the temperature is reduced between consecutive iterations
19956 coolingFactor: 0.99,
19957 // Lower temperature threshold (below this point the layout will end)
19958 minTemp: 1.0
19959};
19960
19961/**
19962 * @brief : constructor
19963 * @arg options : object containing layout options
19964 */
19965function CoseLayout(options) {
19966 this.options = extend({}, defaults$4, options);
19967 this.options.layout = this;
19968
19969 // Exclude any edge that has a source or target node that is not in the set of passed-in nodes
19970 var nodes = this.options.eles.nodes();
19971 var edges = this.options.eles.edges();
19972 var notEdges = edges.filter(function (e) {
19973 var sourceId = e.source().data('id');
19974 var targetId = e.target().data('id');
19975 var hasSource = nodes.some(function (n) {
19976 return n.data('id') === sourceId;
19977 });
19978 var hasTarget = nodes.some(function (n) {
19979 return n.data('id') === targetId;
19980 });
19981 return !hasSource || !hasTarget;
19982 });
19983 this.options.eles = this.options.eles.not(notEdges);
19984}
19985
19986/**
19987 * @brief : runs the layout
19988 */
19989CoseLayout.prototype.run = function () {
19990 var options = this.options;
19991 var cy = options.cy;
19992 var layout = this;
19993 layout.stopped = false;
19994 if (options.animate === true || options.animate === false) {
19995 layout.emit({
19996 type: 'layoutstart',
19997 layout: layout
19998 });
19999 }
20000
20001 // Set DEBUG - Global variable
20002 if (true === options.debug) {
20003 DEBUG = true;
20004 } else {
20005 DEBUG = false;
20006 }
20007
20008 // Initialize layout info
20009 var layoutInfo = createLayoutInfo(cy, layout, options);
20010
20011 // Show LayoutInfo contents if debugging
20012 if (DEBUG) {
20013 printLayoutInfo(layoutInfo);
20014 }
20015
20016 // If required, randomize node positions
20017 if (options.randomize) {
20018 randomizePositions(layoutInfo);
20019 }
20020 var startTime = performanceNow();
20021 var refresh = function refresh() {
20022 refreshPositions(layoutInfo, cy, options);
20023
20024 // Fit the graph if necessary
20025 if (true === options.fit) {
20026 cy.fit(options.padding);
20027 }
20028 };
20029 var mainLoop = function mainLoop(i) {
20030 if (layout.stopped || i >= options.numIter) {
20031 // logDebug("Layout manually stopped. Stopping computation in step " + i);
20032 return false;
20033 }
20034
20035 // Do one step in the phisical simulation
20036 step(layoutInfo, options);
20037
20038 // Update temperature
20039 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor;
20040 // logDebug("New temperature: " + layoutInfo.temperature);
20041
20042 if (layoutInfo.temperature < options.minTemp) {
20043 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
20044 return false;
20045 }
20046 return true;
20047 };
20048 var done = function done() {
20049 if (options.animate === true || options.animate === false) {
20050 refresh();
20051
20052 // Layout has finished
20053 layout.one('layoutstop', options.stop);
20054 layout.emit({
20055 type: 'layoutstop',
20056 layout: layout
20057 });
20058 } else {
20059 var nodes = options.eles.nodes();
20060 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20061 nodes.layoutPositions(layout, options, getScaledPos);
20062 }
20063 };
20064 var i = 0;
20065 var loopRet = true;
20066 if (options.animate === true) {
20067 var frame = function frame() {
20068 var f = 0;
20069 while (loopRet && f < options.refresh) {
20070 loopRet = mainLoop(i);
20071 i++;
20072 f++;
20073 }
20074 if (!loopRet) {
20075 // it's done
20076 separateComponents(layoutInfo, options);
20077 done();
20078 } else {
20079 var now = performanceNow();
20080 if (now - startTime >= options.animationThreshold) {
20081 refresh();
20082 }
20083 requestAnimationFrame(frame);
20084 }
20085 };
20086 frame();
20087 } else {
20088 while (loopRet) {
20089 loopRet = mainLoop(i);
20090 i++;
20091 }
20092 separateComponents(layoutInfo, options);
20093 done();
20094 }
20095 return this; // chaining
20096};
20097
20098/**
20099 * @brief : called on continuous layouts to stop them before they finish
20100 */
20101CoseLayout.prototype.stop = function () {
20102 this.stopped = true;
20103 if (this.thread) {
20104 this.thread.stop();
20105 }
20106 this.emit('layoutstop');
20107 return this; // chaining
20108};
20109
20110CoseLayout.prototype.destroy = function () {
20111 if (this.thread) {
20112 this.thread.stop();
20113 }
20114 return this; // chaining
20115};
20116
20117/**
20118 * @brief : Creates an object which is contains all the data
20119 * used in the layout process
20120 * @arg cy : cytoscape.js object
20121 * @return : layoutInfo object initialized
20122 */
20123var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20124 // Shortcut
20125 var edges = options.eles.edges();
20126 var nodes = options.eles.nodes();
20127 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20128 x1: 0,
20129 y1: 0,
20130 w: cy.width(),
20131 h: cy.height()
20132 });
20133 var layoutInfo = {
20134 isCompound: cy.hasCompoundNodes(),
20135 layoutNodes: [],
20136 idToIndex: {},
20137 nodeSize: nodes.size(),
20138 graphSet: [],
20139 indexToGraph: [],
20140 layoutEdges: [],
20141 edgeSize: edges.size(),
20142 temperature: options.initialTemp,
20143 clientWidth: bb.w,
20144 clientHeight: bb.h,
20145 boundingBox: bb
20146 };
20147 var components = options.eles.components();
20148 var id2cmptId = {};
20149 for (var i = 0; i < components.length; i++) {
20150 var component = components[i];
20151 for (var j = 0; j < component.length; j++) {
20152 var node = component[j];
20153 id2cmptId[node.id()] = i;
20154 }
20155 }
20156
20157 // Iterate over all nodes, creating layout nodes
20158 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20159 var n = nodes[i];
20160 var nbb = n.layoutDimensions(options);
20161 var tempNode = {};
20162 tempNode.isLocked = n.locked();
20163 tempNode.id = n.data('id');
20164 tempNode.parentId = n.data('parent');
20165 tempNode.cmptId = id2cmptId[n.id()];
20166 tempNode.children = [];
20167 tempNode.positionX = n.position('x');
20168 tempNode.positionY = n.position('y');
20169 tempNode.offsetX = 0;
20170 tempNode.offsetY = 0;
20171 tempNode.height = nbb.w;
20172 tempNode.width = nbb.h;
20173 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20174 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20175 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20176 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20177 tempNode.padLeft = parseFloat(n.style('padding'));
20178 tempNode.padRight = parseFloat(n.style('padding'));
20179 tempNode.padTop = parseFloat(n.style('padding'));
20180 tempNode.padBottom = parseFloat(n.style('padding'));
20181
20182 // forces
20183 tempNode.nodeRepulsion = fn$6(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion;
20184
20185 // Add new node
20186 layoutInfo.layoutNodes.push(tempNode);
20187 // Add entry to id-index map
20188 layoutInfo.idToIndex[tempNode.id] = i;
20189 }
20190
20191 // Inline implementation of a queue, used for traversing the graph in BFS order
20192 var queue = [];
20193 var start = 0; // Points to the start the queue
20194 var end = -1; // Points to the end of the queue
20195
20196 var tempGraph = [];
20197
20198 // Second pass to add child information and
20199 // initialize queue for hierarchical traversal
20200 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20201 var n = layoutInfo.layoutNodes[i];
20202 var p_id = n.parentId;
20203 // Check if node n has a parent node
20204 if (null != p_id) {
20205 // Add node Id to parent's list of children
20206 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20207 } else {
20208 // If a node doesn't have a parent, then it's in the root graph
20209 queue[++end] = n.id;
20210 tempGraph.push(n.id);
20211 }
20212 }
20213
20214 // Add root graph to graphSet
20215 layoutInfo.graphSet.push(tempGraph);
20216
20217 // Traverse the graph, level by level,
20218 while (start <= end) {
20219 // Get the node to visit and remove it from queue
20220 var node_id = queue[start++];
20221 var node_ix = layoutInfo.idToIndex[node_id];
20222 var node = layoutInfo.layoutNodes[node_ix];
20223 var children = node.children;
20224 if (children.length > 0) {
20225 // Add children nodes as a new graph to graph set
20226 layoutInfo.graphSet.push(children);
20227 // Add children to que queue to be visited
20228 for (var i = 0; i < children.length; i++) {
20229 queue[++end] = children[i];
20230 }
20231 }
20232 }
20233
20234 // Create indexToGraph map
20235 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20236 var graph = layoutInfo.graphSet[i];
20237 for (var j = 0; j < graph.length; j++) {
20238 var index = layoutInfo.idToIndex[graph[j]];
20239 layoutInfo.indexToGraph[index] = i;
20240 }
20241 }
20242
20243 // Iterate over all edges, creating Layout Edges
20244 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20245 var e = edges[i];
20246 var tempEdge = {};
20247 tempEdge.id = e.data('id');
20248 tempEdge.sourceId = e.data('source');
20249 tempEdge.targetId = e.data('target');
20250
20251 // Compute ideal length
20252 var idealLength = fn$6(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20253 var elasticity = fn$6(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity;
20254
20255 // Check if it's an inter graph edge
20256 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20257 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20258 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20259 var targetGraph = layoutInfo.indexToGraph[targetIx];
20260 if (sourceGraph != targetGraph) {
20261 // Find lowest common graph ancestor
20262 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo);
20263
20264 // Compute sum of node depths, relative to lca graph
20265 var lcaGraph = layoutInfo.graphSet[lca];
20266 var depth = 0;
20267
20268 // Source depth
20269 var tempNode = layoutInfo.layoutNodes[sourceIx];
20270 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20271 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20272 depth++;
20273 }
20274
20275 // Target depth
20276 tempNode = layoutInfo.layoutNodes[targetIx];
20277 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20278 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20279 depth++;
20280 }
20281
20282 // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20283 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20284 // ". Depth: " + depth);
20285
20286 // Update idealLength
20287 idealLength *= depth * options.nestingFactor;
20288 }
20289 tempEdge.idealLength = idealLength;
20290 tempEdge.elasticity = elasticity;
20291 layoutInfo.layoutEdges.push(tempEdge);
20292 }
20293
20294 // Finally, return layoutInfo object
20295 return layoutInfo;
20296};
20297
20298/**
20299 * @brief : This function finds the index of the lowest common
20300 * graph ancestor between 2 nodes in the subtree
20301 * (from the graph hierarchy induced tree) whose
20302 * root is graphIx
20303 *
20304 * @arg node1: node1's ID
20305 * @arg node2: node2's ID
20306 * @arg layoutInfo: layoutInfo object
20307 *
20308 */
20309var findLCA = function findLCA(node1, node2, layoutInfo) {
20310 // Find their common ancester, starting from the root graph
20311 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20312 if (2 > res.count) {
20313 // If aux function couldn't find the common ancester,
20314 // then it is the root graph
20315 return 0;
20316 } else {
20317 return res.graph;
20318 }
20319};
20320
20321/**
20322 * @brief : Auxiliary function used for LCA computation
20323 *
20324 * @arg node1 : node1's ID
20325 * @arg node2 : node2's ID
20326 * @arg graphIx : subgraph index
20327 * @arg layoutInfo : layoutInfo object
20328 *
20329 * @return : object of the form {count: X, graph: Y}, where:
20330 * X is the number of ancestors (max: 2) found in
20331 * graphIx (and it's subgraphs),
20332 * Y is the graph index of the lowest graph containing
20333 * all X nodes
20334 */
20335var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20336 var graph = layoutInfo.graphSet[graphIx];
20337 // If both nodes belongs to graphIx
20338 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20339 return {
20340 count: 2,
20341 graph: graphIx
20342 };
20343 }
20344
20345 // Make recursive calls for all subgraphs
20346 var c = 0;
20347 for (var i = 0; i < graph.length; i++) {
20348 var nodeId = graph[i];
20349 var nodeIx = layoutInfo.idToIndex[nodeId];
20350 var children = layoutInfo.layoutNodes[nodeIx].children;
20351
20352 // If the node has no child, skip it
20353 if (0 === children.length) {
20354 continue;
20355 }
20356 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20357 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20358 if (0 === result.count) {
20359 // Neither node1 nor node2 are present in this subgraph
20360 continue;
20361 } else if (1 === result.count) {
20362 // One of (node1, node2) is present in this subgraph
20363 c++;
20364 if (2 === c) {
20365 // We've already found both nodes, no need to keep searching
20366 break;
20367 }
20368 } else {
20369 // Both nodes are present in this subgraph
20370 return result;
20371 }
20372 }
20373 return {
20374 count: c,
20375 graph: graphIx
20376 };
20377};
20378
20379/**
20380 * @brief: printsLayoutInfo into js console
20381 * Only used for debbuging
20382 */
20383var printLayoutInfo;
20384
20385/**
20386 * @brief : Randomizes the position of all nodes
20387 */
20388var randomizePositions = function randomizePositions(layoutInfo, cy) {
20389 var width = layoutInfo.clientWidth;
20390 var height = layoutInfo.clientHeight;
20391 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20392 var n = layoutInfo.layoutNodes[i];
20393
20394 // No need to randomize compound nodes or locked nodes
20395 if (0 === n.children.length && !n.isLocked) {
20396 n.positionX = Math.random() * width;
20397 n.positionY = Math.random() * height;
20398 }
20399 }
20400};
20401var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20402 var bb = layoutInfo.boundingBox;
20403 var coseBB = {
20404 x1: Infinity,
20405 x2: -Infinity,
20406 y1: Infinity,
20407 y2: -Infinity
20408 };
20409 if (options.boundingBox) {
20410 nodes.forEach(function (node) {
20411 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20412 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20413 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20414 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20415 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20416 });
20417 coseBB.w = coseBB.x2 - coseBB.x1;
20418 coseBB.h = coseBB.y2 - coseBB.y1;
20419 }
20420 return function (ele, i) {
20421 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20422 if (options.boundingBox) {
20423 // then add extra bounding box constraint
20424 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20425 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20426 return {
20427 x: bb.x1 + pctX * bb.w,
20428 y: bb.y1 + pctY * bb.h
20429 };
20430 } else {
20431 return {
20432 x: lnode.positionX,
20433 y: lnode.positionY
20434 };
20435 }
20436 };
20437};
20438
20439/**
20440 * @brief : Updates the positions of nodes in the network
20441 * @arg layoutInfo : LayoutInfo object
20442 * @arg cy : Cytoscape object
20443 * @arg options : Layout options
20444 */
20445var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20446 // var s = 'Refreshing positions';
20447 // logDebug(s);
20448
20449 var layout = options.layout;
20450 var nodes = options.eles.nodes();
20451 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20452 nodes.positions(getScaledPos);
20453
20454 // Trigger layoutReady only on first call
20455 if (true !== layoutInfo.ready) {
20456 // s = 'Triggering layoutready';
20457 // logDebug(s);
20458 layoutInfo.ready = true;
20459 layout.one('layoutready', options.ready);
20460 layout.emit({
20461 type: 'layoutready',
20462 layout: this
20463 });
20464 }
20465};
20466
20467/**
20468 * @brief : Logs a debug message in JS console, if DEBUG is ON
20469 */
20470// var logDebug = function(text) {
20471// if (DEBUG) {
20472// console.debug(text);
20473// }
20474// };
20475
20476/**
20477 * @brief : Performs one iteration of the physical simulation
20478 * @arg layoutInfo : LayoutInfo object already initialized
20479 * @arg cy : Cytoscape object
20480 * @arg options : Layout options
20481 */
20482var step = function step(layoutInfo, options, _step) {
20483 // var s = "\n\n###############################";
20484 // s += "\nSTEP: " + step;
20485 // s += "\n###############################\n";
20486 // logDebug(s);
20487
20488 // Calculate node repulsions
20489 calculateNodeForces(layoutInfo, options);
20490 // Calculate edge forces
20491 calculateEdgeForces(layoutInfo);
20492 // Calculate gravity forces
20493 calculateGravityForces(layoutInfo, options);
20494 // Propagate forces from parent to child
20495 propagateForces(layoutInfo);
20496 // Update positions based on calculated forces
20497 updatePositions(layoutInfo);
20498};
20499
20500/**
20501 * @brief : Computes the node repulsion forces
20502 */
20503var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20504 // Go through each of the graphs in graphSet
20505 // Nodes only repel each other if they belong to the same graph
20506 // var s = 'calculateNodeForces';
20507 // logDebug(s);
20508 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20509 var graph = layoutInfo.graphSet[i];
20510 var numNodes = graph.length;
20511
20512 // s = "Set: " + graph.toString();
20513 // logDebug(s);
20514
20515 // Now get all the pairs of nodes
20516 // Only get each pair once, (A, B) = (B, A)
20517 for (var j = 0; j < numNodes; j++) {
20518 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20519 for (var k = j + 1; k < numNodes; k++) {
20520 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20521 nodeRepulsion(node1, node2, layoutInfo, options);
20522 }
20523 }
20524 }
20525};
20526var randomDistance = function randomDistance(max) {
20527 return -max + 2 * max * Math.random();
20528};
20529
20530/**
20531 * @brief : Compute the node repulsion forces between a pair of nodes
20532 */
20533var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20534 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20535
20536 var cmptId1 = node1.cmptId;
20537 var cmptId2 = node2.cmptId;
20538 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20539 return;
20540 }
20541
20542 // Get direction of line connecting both node centers
20543 var directionX = node2.positionX - node1.positionX;
20544 var directionY = node2.positionY - node1.positionY;
20545 var maxRandDist = 1;
20546 // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20547
20548 // If both centers are the same, apply a random force
20549 if (0 === directionX && 0 === directionY) {
20550 directionX = randomDistance(maxRandDist);
20551 directionY = randomDistance(maxRandDist);
20552 }
20553 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20554 if (overlap > 0) {
20555 // s += "\nNodes DO overlap.";
20556 // s += "\nOverlap: " + overlap;
20557 // If nodes overlap, repulsion force is proportional
20558 // to the overlap
20559 var force = options.nodeOverlap * overlap;
20560
20561 // Compute the module and components of the force vector
20562 var distance = Math.sqrt(directionX * directionX + directionY * directionY);
20563 // s += "\nDistance: " + distance;
20564 var forceX = force * directionX / distance;
20565 var forceY = force * directionY / distance;
20566 } else {
20567 // s += "\nNodes do NOT overlap.";
20568 // If there's no overlap, force is inversely proportional
20569 // to squared distance
20570
20571 // Get clipping points for both nodes
20572 var point1 = findClippingPoint(node1, directionX, directionY);
20573 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY);
20574
20575 // Use clipping points to compute distance
20576 var distanceX = point2.x - point1.x;
20577 var distanceY = point2.y - point1.y;
20578 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20579 var distance = Math.sqrt(distanceSqr);
20580 // s += "\nDistance: " + distance;
20581
20582 // Compute the module and components of the force vector
20583 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20584 var forceX = force * distanceX / distance;
20585 var forceY = force * distanceY / distance;
20586 }
20587
20588 // Apply force
20589 if (!node1.isLocked) {
20590 node1.offsetX -= forceX;
20591 node1.offsetY -= forceY;
20592 }
20593 if (!node2.isLocked) {
20594 node2.offsetX += forceX;
20595 node2.offsetY += forceY;
20596 }
20597
20598 // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20599 // logDebug(s);
20600
20601 return;
20602};
20603
20604/**
20605 * @brief : Determines whether two nodes overlap or not
20606 * @return : Amount of overlapping (0 => no overlap)
20607 */
20608var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20609 if (dX > 0) {
20610 var overlapX = node1.maxX - node2.minX;
20611 } else {
20612 var overlapX = node2.maxX - node1.minX;
20613 }
20614 if (dY > 0) {
20615 var overlapY = node1.maxY - node2.minY;
20616 } else {
20617 var overlapY = node2.maxY - node1.minY;
20618 }
20619 if (overlapX >= 0 && overlapY >= 0) {
20620 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20621 } else {
20622 return 0;
20623 }
20624};
20625
20626/**
20627 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20628 * the rectangular bounding box of it's source/target node
20629 */
20630var findClippingPoint = function findClippingPoint(node, dX, dY) {
20631 // Shorcuts
20632 var X = node.positionX;
20633 var Y = node.positionY;
20634 var H = node.height || 1;
20635 var W = node.width || 1;
20636 var dirSlope = dY / dX;
20637 var nodeSlope = H / W;
20638
20639 // var s = 'Computing clipping point of node ' + node.id +
20640 // " . Height: " + H + ", Width: " + W +
20641 // "\nDirection " + dX + ", " + dY;
20642 //
20643 // Compute intersection
20644 var res = {};
20645
20646 // Case: Vertical direction (up)
20647 if (0 === dX && 0 < dY) {
20648 res.x = X;
20649 // s += "\nUp direction";
20650 res.y = Y + H / 2;
20651 return res;
20652 }
20653
20654 // Case: Vertical direction (down)
20655 if (0 === dX && 0 > dY) {
20656 res.x = X;
20657 res.y = Y + H / 2;
20658 // s += "\nDown direction";
20659
20660 return res;
20661 }
20662
20663 // Case: Intersects the right border
20664 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20665 res.x = X + W / 2;
20666 res.y = Y + W * dY / 2 / dX;
20667 // s += "\nRightborder";
20668
20669 return res;
20670 }
20671
20672 // Case: Intersects the left border
20673 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20674 res.x = X - W / 2;
20675 res.y = Y - W * dY / 2 / dX;
20676 // s += "\nLeftborder";
20677
20678 return res;
20679 }
20680
20681 // Case: Intersects the top border
20682 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20683 res.x = X + H * dX / 2 / dY;
20684 res.y = Y + H / 2;
20685 // s += "\nTop border";
20686
20687 return res;
20688 }
20689
20690 // Case: Intersects the bottom border
20691 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20692 res.x = X - H * dX / 2 / dY;
20693 res.y = Y - H / 2;
20694 // s += "\nBottom border";
20695
20696 return res;
20697 }
20698
20699 // s += "\nClipping point found at " + res.x + ", " + res.y;
20700 // logDebug(s);
20701 return res;
20702};
20703
20704/**
20705 * @brief : Calculates all edge forces
20706 */
20707var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20708 // Iterate over all edges
20709 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20710 // Get edge, source & target nodes
20711 var edge = layoutInfo.layoutEdges[i];
20712 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20713 var source = layoutInfo.layoutNodes[sourceIx];
20714 var targetIx = layoutInfo.idToIndex[edge.targetId];
20715 var target = layoutInfo.layoutNodes[targetIx];
20716
20717 // Get direction of line connecting both node centers
20718 var directionX = target.positionX - source.positionX;
20719 var directionY = target.positionY - source.positionY;
20720
20721 // If both centers are the same, do nothing.
20722 // A random force has already been applied as node repulsion
20723 if (0 === directionX && 0 === directionY) {
20724 continue;
20725 }
20726
20727 // Get clipping points for both nodes
20728 var point1 = findClippingPoint(source, directionX, directionY);
20729 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20730 var lx = point2.x - point1.x;
20731 var ly = point2.y - point1.y;
20732 var l = Math.sqrt(lx * lx + ly * ly);
20733 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20734 if (0 !== l) {
20735 var forceX = force * lx / l;
20736 var forceY = force * ly / l;
20737 } else {
20738 var forceX = 0;
20739 var forceY = 0;
20740 }
20741
20742 // Add this force to target and source nodes
20743 if (!source.isLocked) {
20744 source.offsetX += forceX;
20745 source.offsetY += forceY;
20746 }
20747 if (!target.isLocked) {
20748 target.offsetX -= forceX;
20749 target.offsetY -= forceY;
20750 }
20751
20752 // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20753 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20754 // logDebug(s);
20755 }
20756};
20757
20758/**
20759 * @brief : Computes gravity forces for all nodes
20760 */
20761var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20762 if (options.gravity === 0) {
20763 return;
20764 }
20765 var distThreshold = 1;
20766
20767 // var s = 'calculateGravityForces';
20768 // logDebug(s);
20769 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20770 var graph = layoutInfo.graphSet[i];
20771 var numNodes = graph.length;
20772
20773 // s = "Set: " + graph.toString();
20774 // logDebug(s);
20775
20776 // Compute graph center
20777 if (0 === i) {
20778 var centerX = layoutInfo.clientHeight / 2;
20779 var centerY = layoutInfo.clientWidth / 2;
20780 } else {
20781 // Get Parent node for this graph, and use its position as center
20782 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20783 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20784 var centerX = parent.positionX;
20785 var centerY = parent.positionY;
20786 }
20787 // s = "Center found at: " + centerX + ", " + centerY;
20788 // logDebug(s);
20789
20790 // Apply force to all nodes in graph
20791 for (var j = 0; j < numNodes; j++) {
20792 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20793 // s = "Node: " + node.id;
20794
20795 if (node.isLocked) {
20796 continue;
20797 }
20798 var dx = centerX - node.positionX;
20799 var dy = centerY - node.positionY;
20800 var d = Math.sqrt(dx * dx + dy * dy);
20801 if (d > distThreshold) {
20802 var fx = options.gravity * dx / d;
20803 var fy = options.gravity * dy / d;
20804 node.offsetX += fx;
20805 node.offsetY += fy;
20806 // s += ": Applied force: " + fx + ", " + fy;
20807 }
20808 // logDebug(s);
20809 }
20810 }
20811};
20812
20813/**
20814 * @brief : This function propagates the existing offsets from
20815 * parent nodes to its descendents.
20816 * @arg layoutInfo : layoutInfo Object
20817 * @arg cy : cytoscape Object
20818 * @arg options : Layout options
20819 */
20820var propagateForces = function propagateForces(layoutInfo, options) {
20821 // Inline implementation of a queue, used for traversing the graph in BFS order
20822 var queue = [];
20823 var start = 0; // Points to the start the queue
20824 var end = -1; // Points to the end of the queue
20825
20826 // logDebug('propagateForces');
20827
20828 // Start by visiting the nodes in the root graph
20829 queue.push.apply(queue, layoutInfo.graphSet[0]);
20830 end += layoutInfo.graphSet[0].length;
20831
20832 // Traverse the graph, level by level,
20833 while (start <= end) {
20834 // Get the node to visit and remove it from queue
20835 var nodeId = queue[start++];
20836 var nodeIndex = layoutInfo.idToIndex[nodeId];
20837 var node = layoutInfo.layoutNodes[nodeIndex];
20838 var children = node.children;
20839
20840 // We only need to process the node if it's compound
20841 if (0 < children.length && !node.isLocked) {
20842 var offX = node.offsetX;
20843 var offY = node.offsetY;
20844
20845 // var s = "Propagating offset from parent node : " + node.id +
20846 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20847 // s += "\n Children: " + children.toString();
20848 // logDebug(s);
20849
20850 for (var i = 0; i < children.length; i++) {
20851 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]];
20852 // Propagate offset
20853 childNode.offsetX += offX;
20854 childNode.offsetY += offY;
20855 // Add children to queue to be visited
20856 queue[++end] = children[i];
20857 }
20858
20859 // Reset parent offsets
20860 node.offsetX = 0;
20861 node.offsetY = 0;
20862 }
20863 }
20864};
20865
20866/**
20867 * @brief : Updates the layout model positions, based on
20868 * the accumulated forces
20869 */
20870var updatePositions = function updatePositions(layoutInfo, options) {
20871 // var s = 'Updating positions';
20872 // logDebug(s);
20873
20874 // Reset boundaries for compound nodes
20875 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20876 var n = layoutInfo.layoutNodes[i];
20877 if (0 < n.children.length) {
20878 // logDebug("Resetting boundaries of compound node: " + n.id);
20879 n.maxX = undefined;
20880 n.minX = undefined;
20881 n.maxY = undefined;
20882 n.minY = undefined;
20883 }
20884 }
20885 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20886 var n = layoutInfo.layoutNodes[i];
20887 if (0 < n.children.length || n.isLocked) {
20888 // No need to set compound or locked node position
20889 // logDebug("Skipping position update of node: " + n.id);
20890 continue;
20891 }
20892 // s = "Node: " + n.id + " Previous position: (" +
20893 // n.positionX + ", " + n.positionY + ").";
20894
20895 // Limit displacement in order to improve stability
20896 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20897 n.positionX += tempForce.x;
20898 n.positionY += tempForce.y;
20899 n.offsetX = 0;
20900 n.offsetY = 0;
20901 n.minX = n.positionX - n.width;
20902 n.maxX = n.positionX + n.width;
20903 n.minY = n.positionY - n.height;
20904 n.maxY = n.positionY + n.height;
20905 // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20906 // logDebug(s);
20907
20908 // Update ancestry boudaries
20909 updateAncestryBoundaries(n, layoutInfo);
20910 }
20911
20912 // Update size, position of compund nodes
20913 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20914 var n = layoutInfo.layoutNodes[i];
20915 if (0 < n.children.length && !n.isLocked) {
20916 n.positionX = (n.maxX + n.minX) / 2;
20917 n.positionY = (n.maxY + n.minY) / 2;
20918 n.width = n.maxX - n.minX;
20919 n.height = n.maxY - n.minY;
20920 // s = "Updating position, size of compound node " + n.id;
20921 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20922 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20923 // logDebug(s);
20924 }
20925 }
20926};
20927
20928/**
20929 * @brief : Limits a force (forceX, forceY) to be not
20930 * greater (in modulo) than max.
20931 8 Preserves force direction.
20932 */
20933var limitForce = function limitForce(forceX, forceY, max) {
20934 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20935 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20936 if (force > max) {
20937 var res = {
20938 x: max * forceX / force,
20939 y: max * forceY / force
20940 };
20941 } else {
20942 var res = {
20943 x: forceX,
20944 y: forceY
20945 };
20946 }
20947
20948 // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20949 // logDebug(s);
20950
20951 return res;
20952};
20953
20954/**
20955 * @brief : Function used for keeping track of compound node
20956 * sizes, since they should bound all their subnodes.
20957 */
20958var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20959 // var s = "Propagating new position/size of node " + node.id;
20960 var parentId = node.parentId;
20961 if (null == parentId) {
20962 // If there's no parent, we are done
20963 // s += ". No parent node.";
20964 // logDebug(s);
20965 return;
20966 }
20967
20968 // Get Parent Node
20969 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20970 var flag = false;
20971
20972 // MaxX
20973 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20974 p.maxX = node.maxX + p.padRight;
20975 flag = true;
20976 // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20977 }
20978
20979 // MinX
20980 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20981 p.minX = node.minX - p.padLeft;
20982 flag = true;
20983 // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20984 }
20985
20986 // MaxY
20987 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20988 p.maxY = node.maxY + p.padBottom;
20989 flag = true;
20990 // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20991 }
20992
20993 // MinY
20994 if (null == p.minY || node.minY - p.padTop < p.minY) {
20995 p.minY = node.minY - p.padTop;
20996 flag = true;
20997 // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20998 }
20999
21000 // If updated boundaries, propagate changes upward
21001 if (flag) {
21002 // logDebug(s);
21003 return updateAncestryBoundaries(p, layoutInfo);
21004 }
21005
21006 // s += ". No changes in boundaries/position of parent node " + p.id;
21007 // logDebug(s);
21008 return;
21009};
21010var separateComponents = function separateComponents(layoutInfo, options) {
21011 var nodes = layoutInfo.layoutNodes;
21012 var components = [];
21013 for (var i = 0; i < nodes.length; i++) {
21014 var node = nodes[i];
21015 var cid = node.cmptId;
21016 var component = components[cid] = components[cid] || [];
21017 component.push(node);
21018 }
21019 var totalA = 0;
21020 for (var i = 0; i < components.length; i++) {
21021 var c = components[i];
21022 if (!c) {
21023 continue;
21024 }
21025 c.x1 = Infinity;
21026 c.x2 = -Infinity;
21027 c.y1 = Infinity;
21028 c.y2 = -Infinity;
21029 for (var j = 0; j < c.length; j++) {
21030 var n = c[j];
21031 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
21032 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
21033 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
21034 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
21035 }
21036 c.w = c.x2 - c.x1;
21037 c.h = c.y2 - c.y1;
21038 totalA += c.w * c.h;
21039 }
21040 components.sort(function (c1, c2) {
21041 return c2.w * c2.h - c1.w * c1.h;
21042 });
21043 var x = 0;
21044 var y = 0;
21045 var usedW = 0;
21046 var rowH = 0;
21047 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
21048 for (var i = 0; i < components.length; i++) {
21049 var c = components[i];
21050 if (!c) {
21051 continue;
21052 }
21053 for (var j = 0; j < c.length; j++) {
21054 var n = c[j];
21055 if (!n.isLocked) {
21056 n.positionX += x - c.x1;
21057 n.positionY += y - c.y1;
21058 }
21059 }
21060 x += c.w + options.componentSpacing;
21061 usedW += c.w + options.componentSpacing;
21062 rowH = Math.max(rowH, c.h);
21063 if (usedW > maxRowW) {
21064 y += rowH + options.componentSpacing;
21065 x = 0;
21066 usedW = 0;
21067 rowH = 0;
21068 }
21069 }
21070};
21071
21072var defaults$3 = {
21073 fit: true,
21074 // whether to fit the viewport to the graph
21075 padding: 30,
21076 // padding used on fit
21077 boundingBox: undefined,
21078 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21079 avoidOverlap: true,
21080 // prevents node overlap, may overflow boundingBox if not enough space
21081 avoidOverlapPadding: 10,
21082 // extra spacing around nodes when avoidOverlap: true
21083 nodeDimensionsIncludeLabels: false,
21084 // Excludes the label when calculating node bounding boxes for the layout algorithm
21085 spacingFactor: undefined,
21086 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21087 condense: false,
21088 // uses all available space on false, uses minimal space on true
21089 rows: undefined,
21090 // force num of rows in the grid
21091 cols: undefined,
21092 // force num of columns in the grid
21093 position: function position(node) {},
21094 // returns { row, col } for element
21095 sort: undefined,
21096 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21097 animate: false,
21098 // whether to transition the node positions
21099 animationDuration: 500,
21100 // duration of animation in ms if enabled
21101 animationEasing: undefined,
21102 // easing of animation if enabled
21103 animateFilter: function animateFilter(node, i) {
21104 return true;
21105 },
21106 // 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
21107 ready: undefined,
21108 // callback on layoutready
21109 stop: undefined,
21110 // callback on layoutstop
21111 transform: function transform(node, position) {
21112 return position;
21113 } // transform a given node position. Useful for changing flow direction in discrete layouts
21114};
21115
21116function GridLayout(options) {
21117 this.options = extend({}, defaults$3, options);
21118}
21119GridLayout.prototype.run = function () {
21120 var params = this.options;
21121 var options = params;
21122 var cy = params.cy;
21123 var eles = options.eles;
21124 var nodes = eles.nodes().not(':parent');
21125 if (options.sort) {
21126 nodes = nodes.sort(options.sort);
21127 }
21128 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21129 x1: 0,
21130 y1: 0,
21131 w: cy.width(),
21132 h: cy.height()
21133 });
21134 if (bb.h === 0 || bb.w === 0) {
21135 eles.nodes().layoutPositions(this, options, function (ele) {
21136 return {
21137 x: bb.x1,
21138 y: bb.y1
21139 };
21140 });
21141 } else {
21142 // width/height * splits^2 = cells where splits is number of times to split width
21143 var cells = nodes.size();
21144 var splits = Math.sqrt(cells * bb.h / bb.w);
21145 var rows = Math.round(splits);
21146 var cols = Math.round(bb.w / bb.h * splits);
21147 var small = function small(val) {
21148 if (val == null) {
21149 return Math.min(rows, cols);
21150 } else {
21151 var min = Math.min(rows, cols);
21152 if (min == rows) {
21153 rows = val;
21154 } else {
21155 cols = val;
21156 }
21157 }
21158 };
21159 var large = function large(val) {
21160 if (val == null) {
21161 return Math.max(rows, cols);
21162 } else {
21163 var max = Math.max(rows, cols);
21164 if (max == rows) {
21165 rows = val;
21166 } else {
21167 cols = val;
21168 }
21169 }
21170 };
21171 var oRows = options.rows;
21172 var oCols = options.cols != null ? options.cols : options.columns;
21173
21174 // if rows or columns were set in options, use those values
21175 if (oRows != null && oCols != null) {
21176 rows = oRows;
21177 cols = oCols;
21178 } else if (oRows != null && oCols == null) {
21179 rows = oRows;
21180 cols = Math.ceil(cells / rows);
21181 } else if (oRows == null && oCols != null) {
21182 cols = oCols;
21183 rows = Math.ceil(cells / cols);
21184 }
21185
21186 // otherwise use the automatic values and adjust accordingly
21187
21188 // if rounding was up, see if we can reduce rows or columns
21189 else if (cols * rows > cells) {
21190 var sm = small();
21191 var lg = large();
21192
21193 // reducing the small side takes away the most cells, so try it first
21194 if ((sm - 1) * lg >= cells) {
21195 small(sm - 1);
21196 } else if ((lg - 1) * sm >= cells) {
21197 large(lg - 1);
21198 }
21199 } else {
21200 // if rounding was too low, add rows or columns
21201 while (cols * rows < cells) {
21202 var _sm = small();
21203 var _lg = large();
21204
21205 // try to add to larger side first (adds less in multiplication)
21206 if ((_lg + 1) * _sm >= cells) {
21207 large(_lg + 1);
21208 } else {
21209 small(_sm + 1);
21210 }
21211 }
21212 }
21213 var cellWidth = bb.w / cols;
21214 var cellHeight = bb.h / rows;
21215 if (options.condense) {
21216 cellWidth = 0;
21217 cellHeight = 0;
21218 }
21219 if (options.avoidOverlap) {
21220 for (var i = 0; i < nodes.length; i++) {
21221 var node = nodes[i];
21222 var pos = node._private.position;
21223 if (pos.x == null || pos.y == null) {
21224 // for bb
21225 pos.x = 0;
21226 pos.y = 0;
21227 }
21228 var nbb = node.layoutDimensions(options);
21229 var p = options.avoidOverlapPadding;
21230 var w = nbb.w + p;
21231 var h = nbb.h + p;
21232 cellWidth = Math.max(cellWidth, w);
21233 cellHeight = Math.max(cellHeight, h);
21234 }
21235 }
21236 var cellUsed = {}; // e.g. 'c-0-2' => true
21237
21238 var used = function used(row, col) {
21239 return cellUsed['c-' + row + '-' + col] ? true : false;
21240 };
21241 var use = function use(row, col) {
21242 cellUsed['c-' + row + '-' + col] = true;
21243 };
21244
21245 // to keep track of current cell position
21246 var row = 0;
21247 var col = 0;
21248 var moveToNextCell = function moveToNextCell() {
21249 col++;
21250 if (col >= cols) {
21251 col = 0;
21252 row++;
21253 }
21254 };
21255
21256 // get a cache of all the manual positions
21257 var id2manPos = {};
21258 for (var _i = 0; _i < nodes.length; _i++) {
21259 var _node = nodes[_i];
21260 var rcPos = options.position(_node);
21261 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21262 // must have at least row or col def'd
21263 var _pos = {
21264 row: rcPos.row,
21265 col: rcPos.col
21266 };
21267 if (_pos.col === undefined) {
21268 // find unused col
21269 _pos.col = 0;
21270 while (used(_pos.row, _pos.col)) {
21271 _pos.col++;
21272 }
21273 } else if (_pos.row === undefined) {
21274 // find unused row
21275 _pos.row = 0;
21276 while (used(_pos.row, _pos.col)) {
21277 _pos.row++;
21278 }
21279 }
21280 id2manPos[_node.id()] = _pos;
21281 use(_pos.row, _pos.col);
21282 }
21283 }
21284 var getPos = function getPos(element, i) {
21285 var x, y;
21286 if (element.locked() || element.isParent()) {
21287 return false;
21288 }
21289
21290 // see if we have a manual position set
21291 var rcPos = id2manPos[element.id()];
21292 if (rcPos) {
21293 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21294 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21295 } else {
21296 // otherwise set automatically
21297
21298 while (used(row, col)) {
21299 moveToNextCell();
21300 }
21301 x = col * cellWidth + cellWidth / 2 + bb.x1;
21302 y = row * cellHeight + cellHeight / 2 + bb.y1;
21303 use(row, col);
21304 moveToNextCell();
21305 }
21306 return {
21307 x: x,
21308 y: y
21309 };
21310 };
21311 nodes.layoutPositions(this, options, getPos);
21312 }
21313 return this; // chaining
21314};
21315
21316// default layout options
21317var defaults$2 = {
21318 ready: function ready() {},
21319 // on layoutready
21320 stop: function stop() {} // on layoutstop
21321};
21322
21323// constructor
21324// options : object containing layout options
21325function NullLayout(options) {
21326 this.options = extend({}, defaults$2, options);
21327}
21328
21329// runs the layout
21330NullLayout.prototype.run = function () {
21331 var options = this.options;
21332 var eles = options.eles; // elements to consider in the layout
21333 var layout = this;
21334
21335 // cy is automatically populated for us in the constructor
21336 // (disable eslint for next line as this serves as example layout code to external developers)
21337 // eslint-disable-next-line no-unused-vars
21338 options.cy;
21339 layout.emit('layoutstart');
21340
21341 // puts all nodes at (0, 0)
21342 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21343 eles.nodes().positions(function () {
21344 return {
21345 x: 0,
21346 y: 0
21347 };
21348 });
21349
21350 // trigger layoutready when each node has had its position set at least once
21351 layout.one('layoutready', options.ready);
21352 layout.emit('layoutready');
21353
21354 // trigger layoutstop when the layout stops (e.g. finishes)
21355 layout.one('layoutstop', options.stop);
21356 layout.emit('layoutstop');
21357 return this; // chaining
21358};
21359
21360// called on continuous layouts to stop them before they finish
21361NullLayout.prototype.stop = function () {
21362 return this; // chaining
21363};
21364
21365var defaults$1 = {
21366 positions: undefined,
21367 // map of (node id) => (position obj); or function(node){ return somPos; }
21368 zoom: undefined,
21369 // the zoom level to set (prob want fit = false if set)
21370 pan: undefined,
21371 // the pan level to set (prob want fit = false if set)
21372 fit: true,
21373 // whether to fit to viewport
21374 padding: 30,
21375 // padding on fit
21376 spacingFactor: undefined,
21377 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21378 animate: false,
21379 // whether to transition the node positions
21380 animationDuration: 500,
21381 // duration of animation in ms if enabled
21382 animationEasing: undefined,
21383 // easing of animation if enabled
21384 animateFilter: function animateFilter(node, i) {
21385 return true;
21386 },
21387 // 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
21388 ready: undefined,
21389 // callback on layoutready
21390 stop: undefined,
21391 // callback on layoutstop
21392 transform: function transform(node, position) {
21393 return position;
21394 } // transform a given node position. Useful for changing flow direction in discrete layouts
21395};
21396
21397function PresetLayout(options) {
21398 this.options = extend({}, defaults$1, options);
21399}
21400PresetLayout.prototype.run = function () {
21401 var options = this.options;
21402 var eles = options.eles;
21403 var nodes = eles.nodes();
21404 var posIsFn = fn$6(options.positions);
21405 function getPosition(node) {
21406 if (options.positions == null) {
21407 return copyPosition(node.position());
21408 }
21409 if (posIsFn) {
21410 return options.positions(node);
21411 }
21412 var pos = options.positions[node._private.data.id];
21413 if (pos == null) {
21414 return null;
21415 }
21416 return pos;
21417 }
21418 nodes.layoutPositions(this, options, function (node, i) {
21419 var position = getPosition(node);
21420 if (node.locked() || position == null) {
21421 return false;
21422 }
21423 return position;
21424 });
21425 return this; // chaining
21426};
21427
21428var defaults = {
21429 fit: true,
21430 // whether to fit to viewport
21431 padding: 30,
21432 // fit padding
21433 boundingBox: undefined,
21434 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21435 animate: false,
21436 // whether to transition the node positions
21437 animationDuration: 500,
21438 // duration of animation in ms if enabled
21439 animationEasing: undefined,
21440 // easing of animation if enabled
21441 animateFilter: function animateFilter(node, i) {
21442 return true;
21443 },
21444 // 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
21445 ready: undefined,
21446 // callback on layoutready
21447 stop: undefined,
21448 // callback on layoutstop
21449 transform: function transform(node, position) {
21450 return position;
21451 } // transform a given node position. Useful for changing flow direction in discrete layouts
21452};
21453
21454function RandomLayout(options) {
21455 this.options = extend({}, defaults, options);
21456}
21457RandomLayout.prototype.run = function () {
21458 var options = this.options;
21459 var cy = options.cy;
21460 var eles = options.eles;
21461 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21462 x1: 0,
21463 y1: 0,
21464 w: cy.width(),
21465 h: cy.height()
21466 });
21467 var getPos = function getPos(node, i) {
21468 return {
21469 x: bb.x1 + Math.round(Math.random() * bb.w),
21470 y: bb.y1 + Math.round(Math.random() * bb.h)
21471 };
21472 };
21473 eles.nodes().layoutPositions(this, options, getPos);
21474 return this; // chaining
21475};
21476
21477var layout = [{
21478 name: 'breadthfirst',
21479 impl: BreadthFirstLayout
21480}, {
21481 name: 'circle',
21482 impl: CircleLayout
21483}, {
21484 name: 'concentric',
21485 impl: ConcentricLayout
21486}, {
21487 name: 'cose',
21488 impl: CoseLayout
21489}, {
21490 name: 'grid',
21491 impl: GridLayout
21492}, {
21493 name: 'null',
21494 impl: NullLayout
21495}, {
21496 name: 'preset',
21497 impl: PresetLayout
21498}, {
21499 name: 'random',
21500 impl: RandomLayout
21501}];
21502
21503function NullRenderer(options) {
21504 this.options = options;
21505 this.notifications = 0; // for testing
21506}
21507
21508var noop = function noop() {};
21509var throwImgErr = function throwImgErr() {
21510 throw new Error('A headless instance can not render images');
21511};
21512NullRenderer.prototype = {
21513 recalculateRenderedStyle: noop,
21514 notify: function notify() {
21515 this.notifications++;
21516 },
21517 init: noop,
21518 isHeadless: function isHeadless() {
21519 return true;
21520 },
21521 png: throwImgErr,
21522 jpg: throwImgErr
21523};
21524
21525var BRp$f = {};
21526BRp$f.arrowShapeWidth = 0.3;
21527BRp$f.registerArrowShapes = function () {
21528 var arrowShapes = this.arrowShapes = {};
21529 var renderer = this;
21530
21531 // Contract for arrow shapes:
21532 // 0, 0 is arrow tip
21533 // (0, 1) is direction towards node
21534 // (1, 0) is right
21535 //
21536 // functional api:
21537 // collide: check x, y in shape
21538 // roughCollide: called before collide, no false negatives
21539 // draw: draw
21540 // spacing: dist(arrowTip, nodeBoundary)
21541 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21542
21543 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21544 var x1 = translation.x - size / 2 - padding;
21545 var x2 = translation.x + size / 2 + padding;
21546 var y1 = translation.y - size / 2 - padding;
21547 var y2 = translation.y + size / 2 + padding;
21548 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21549 return inside;
21550 };
21551 var transform = function transform(x, y, size, angle, translation) {
21552 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21553 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21554 var xScaled = xRotated * size;
21555 var yScaled = yRotated * size;
21556 var xTranslated = xScaled + translation.x;
21557 var yTranslated = yScaled + translation.y;
21558 return {
21559 x: xTranslated,
21560 y: yTranslated
21561 };
21562 };
21563 var transformPoints = function transformPoints(pts, size, angle, translation) {
21564 var retPts = [];
21565 for (var i = 0; i < pts.length; i += 2) {
21566 var x = pts[i];
21567 var y = pts[i + 1];
21568 retPts.push(transform(x, y, size, angle, translation));
21569 }
21570 return retPts;
21571 };
21572 var pointsToArr = function pointsToArr(pts) {
21573 var ret = [];
21574 for (var i = 0; i < pts.length; i++) {
21575 var p = pts[i];
21576 ret.push(p.x, p.y);
21577 }
21578 return ret;
21579 };
21580 var standardGap = function standardGap(edge) {
21581 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21582 };
21583 var defineArrowShape = function defineArrowShape(name, defn) {
21584 if (string(defn)) {
21585 defn = arrowShapes[defn];
21586 }
21587 arrowShapes[name] = extend({
21588 name: name,
21589 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21590 collide: function collide(x, y, size, angle, translation, padding) {
21591 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21592 var inside = pointInsidePolygonPoints(x, y, points);
21593 return inside;
21594 },
21595 roughCollide: bbCollide,
21596 draw: function draw(context, size, angle, translation) {
21597 var points = transformPoints(this.points, size, angle, translation);
21598 renderer.arrowShapeImpl('polygon')(context, points);
21599 },
21600 spacing: function spacing(edge) {
21601 return 0;
21602 },
21603 gap: standardGap
21604 }, defn);
21605 };
21606 defineArrowShape('none', {
21607 collide: falsify,
21608 roughCollide: falsify,
21609 draw: noop$1,
21610 spacing: zeroify,
21611 gap: zeroify
21612 });
21613 defineArrowShape('triangle', {
21614 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21615 });
21616 defineArrowShape('arrow', 'triangle');
21617 defineArrowShape('triangle-backcurve', {
21618 points: arrowShapes['triangle'].points,
21619 controlPoint: [0, -0.15],
21620 roughCollide: bbCollide,
21621 draw: function draw(context, size, angle, translation, edgeWidth) {
21622 var ptsTrans = transformPoints(this.points, size, angle, translation);
21623 var ctrlPt = this.controlPoint;
21624 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21625 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21626 },
21627 gap: function gap(edge) {
21628 return standardGap(edge) * 0.8;
21629 }
21630 });
21631 defineArrowShape('triangle-tee', {
21632 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21633 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21634 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21635 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21636 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21637 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21638 return inside;
21639 },
21640 draw: function draw(context, size, angle, translation, edgeWidth) {
21641 var triPts = transformPoints(this.points, size, angle, translation);
21642 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21643 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21644 }
21645 });
21646 defineArrowShape('circle-triangle', {
21647 radius: 0.15,
21648 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21649 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21650 var t = translation;
21651 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21652 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21653 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21654 },
21655 draw: function draw(context, size, angle, translation, edgeWidth) {
21656 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21657 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21658 },
21659 spacing: function spacing(edge) {
21660 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21661 }
21662 });
21663 defineArrowShape('triangle-cross', {
21664 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21665 baseCrossLinePts: [-0.15, -0.4,
21666 // first half of the rectangle
21667 -0.15, -0.4, 0.15, -0.4,
21668 // second half of the rectangle
21669 0.15, -0.4],
21670 crossLinePts: function crossLinePts(size, edgeWidth) {
21671 // shift points so that the distance between the cross points matches edge width
21672 var p = this.baseCrossLinePts.slice();
21673 var shiftFactor = edgeWidth / size;
21674 var y0 = 3;
21675 var y1 = 5;
21676 p[y0] = p[y0] - shiftFactor;
21677 p[y1] = p[y1] - shiftFactor;
21678 return p;
21679 },
21680 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21681 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21682 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21683 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21684 return inside;
21685 },
21686 draw: function draw(context, size, angle, translation, edgeWidth) {
21687 var triPts = transformPoints(this.points, size, angle, translation);
21688 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21689 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21690 }
21691 });
21692 defineArrowShape('vee', {
21693 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21694 gap: function gap(edge) {
21695 return standardGap(edge) * 0.525;
21696 }
21697 });
21698 defineArrowShape('circle', {
21699 radius: 0.15,
21700 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21701 var t = translation;
21702 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21703 return inside;
21704 },
21705 draw: function draw(context, size, angle, translation, edgeWidth) {
21706 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21707 },
21708 spacing: function spacing(edge) {
21709 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21710 }
21711 });
21712 defineArrowShape('tee', {
21713 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21714 spacing: function spacing(edge) {
21715 return 1;
21716 },
21717 gap: function gap(edge) {
21718 return 1;
21719 }
21720 });
21721 defineArrowShape('square', {
21722 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21723 });
21724 defineArrowShape('diamond', {
21725 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21726 gap: function gap(edge) {
21727 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21728 }
21729 });
21730 defineArrowShape('chevron', {
21731 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21732 gap: function gap(edge) {
21733 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21734 }
21735 });
21736};
21737
21738var BRp$e = {};
21739
21740// Project mouse
21741BRp$e.projectIntoViewport = function (clientX, clientY) {
21742 var cy = this.cy;
21743 var offsets = this.findContainerClientCoords();
21744 var offsetLeft = offsets[0];
21745 var offsetTop = offsets[1];
21746 var scale = offsets[4];
21747 var pan = cy.pan();
21748 var zoom = cy.zoom();
21749 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21750 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21751 return [x, y];
21752};
21753BRp$e.findContainerClientCoords = function () {
21754 if (this.containerBB) {
21755 return this.containerBB;
21756 }
21757 var container = this.container;
21758 var rect = container.getBoundingClientRect();
21759 var style = this.cy.window().getComputedStyle(container);
21760 var styleValue = function styleValue(name) {
21761 return parseFloat(style.getPropertyValue(name));
21762 };
21763 var padding = {
21764 left: styleValue('padding-left'),
21765 right: styleValue('padding-right'),
21766 top: styleValue('padding-top'),
21767 bottom: styleValue('padding-bottom')
21768 };
21769 var border = {
21770 left: styleValue('border-left-width'),
21771 right: styleValue('border-right-width'),
21772 top: styleValue('border-top-width'),
21773 bottom: styleValue('border-bottom-width')
21774 };
21775 var clientWidth = container.clientWidth;
21776 var clientHeight = container.clientHeight;
21777 var paddingHor = padding.left + padding.right;
21778 var paddingVer = padding.top + padding.bottom;
21779 var borderHor = border.left + border.right;
21780 var scale = rect.width / (clientWidth + borderHor);
21781 var unscaledW = clientWidth - paddingHor;
21782 var unscaledH = clientHeight - paddingVer;
21783 var left = rect.left + padding.left + border.left;
21784 var top = rect.top + padding.top + border.top;
21785 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21786};
21787BRp$e.invalidateContainerClientCoordsCache = function () {
21788 this.containerBB = null;
21789};
21790BRp$e.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21791 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21792};
21793BRp$e.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21794 var self = this;
21795 var r = this;
21796 var eles = r.getCachedZSortedEles();
21797 var near = []; // 1 node max, 1 edge max
21798 var zoom = r.cy.zoom();
21799 var hasCompounds = r.cy.hasCompoundNodes();
21800 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21801 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21802 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21803 var minSqDist = Infinity;
21804 var nearEdge;
21805 var nearNode;
21806 if (interactiveElementsOnly) {
21807 eles = eles.interactive;
21808 }
21809 function addEle(ele, sqDist) {
21810 if (ele.isNode()) {
21811 if (nearNode) {
21812 return; // can't replace node
21813 } else {
21814 nearNode = ele;
21815 near.push(ele);
21816 }
21817 }
21818 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21819 if (nearEdge) {
21820 // then replace existing edge
21821 // can replace only if same z-index
21822 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) {
21823 for (var i = 0; i < near.length; i++) {
21824 if (near[i].isEdge()) {
21825 near[i] = ele;
21826 nearEdge = ele;
21827 minSqDist = sqDist != null ? sqDist : minSqDist;
21828 break;
21829 }
21830 }
21831 }
21832 } else {
21833 near.push(ele);
21834 nearEdge = ele;
21835 minSqDist = sqDist != null ? sqDist : minSqDist;
21836 }
21837 }
21838 }
21839 function checkNode(node) {
21840 var width = node.outerWidth() + 2 * nodeThreshold;
21841 var height = node.outerHeight() + 2 * nodeThreshold;
21842 var hw = width / 2;
21843 var hh = height / 2;
21844 var pos = node.position();
21845 var cornerRadius = node.pstyle('corner-radius').value === 'auto' ? 'auto' : node.pstyle('corner-radius').pfValue;
21846 var rs = node._private.rscratch;
21847 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21848 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21849 ) {
21850 var shape = r.nodeShapes[self.getNodeShape(node)];
21851 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y, cornerRadius, rs)) {
21852 addEle(node, 0);
21853 return true;
21854 }
21855 }
21856 }
21857 function checkEdge(edge) {
21858 var _p = edge._private;
21859 var rs = _p.rscratch;
21860 var styleWidth = edge.pstyle('width').pfValue;
21861 var scale = edge.pstyle('arrow-scale').value;
21862 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21863 var widthSq = width * width;
21864 var width2 = width * 2;
21865 var src = _p.source;
21866 var tgt = _p.target;
21867 var sqDist;
21868 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21869 var pts = rs.allpts;
21870 for (var i = 0; i + 3 < pts.length; i += 2) {
21871 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]))) {
21872 addEle(edge, sqDist);
21873 return true;
21874 }
21875 }
21876 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21877 var pts = rs.allpts;
21878 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21879 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]))) {
21880 addEle(edge, sqDist);
21881 return true;
21882 }
21883 }
21884 }
21885
21886 // if we're close to the edge but didn't hit it, maybe we hit its arrows
21887
21888 var src = src || _p.source;
21889 var tgt = tgt || _p.target;
21890 var arSize = self.getArrowWidth(styleWidth, scale);
21891 var arrows = [{
21892 name: 'source',
21893 x: rs.arrowStartX,
21894 y: rs.arrowStartY,
21895 angle: rs.srcArrowAngle
21896 }, {
21897 name: 'target',
21898 x: rs.arrowEndX,
21899 y: rs.arrowEndY,
21900 angle: rs.tgtArrowAngle
21901 }, {
21902 name: 'mid-source',
21903 x: rs.midX,
21904 y: rs.midY,
21905 angle: rs.midsrcArrowAngle
21906 }, {
21907 name: 'mid-target',
21908 x: rs.midX,
21909 y: rs.midY,
21910 angle: rs.midtgtArrowAngle
21911 }];
21912 for (var i = 0; i < arrows.length; i++) {
21913 var ar = arrows[i];
21914 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21915 var edgeWidth = edge.pstyle('width').pfValue;
21916 if (shape.roughCollide(x, y, arSize, ar.angle, {
21917 x: ar.x,
21918 y: ar.y
21919 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21920 x: ar.x,
21921 y: ar.y
21922 }, edgeWidth, edgeThreshold)) {
21923 addEle(edge);
21924 return true;
21925 }
21926 }
21927
21928 // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21929 if (hasCompounds && near.length > 0) {
21930 checkNode(src);
21931 checkNode(tgt);
21932 }
21933 }
21934 function preprop(obj, name, pre) {
21935 return getPrefixedProperty(obj, name, pre);
21936 }
21937 function checkLabel(ele, prefix) {
21938 var _p = ele._private;
21939 var th = labelThreshold;
21940 var prefixDash;
21941 if (prefix) {
21942 prefixDash = prefix + '-';
21943 } else {
21944 prefixDash = '';
21945 }
21946 ele.boundingBox();
21947 var bb = _p.labelBounds[prefix || 'main'];
21948 var text = ele.pstyle(prefixDash + 'label').value;
21949 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21950 if (!eventsEnabled || !text) {
21951 return;
21952 }
21953 var lx = preprop(_p.rscratch, 'labelX', prefix);
21954 var ly = preprop(_p.rscratch, 'labelY', prefix);
21955 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21956 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21957 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21958 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21959 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21960 var ly1 = bb.y1 - th - oy;
21961 var ly2 = bb.y2 + th - oy;
21962 if (theta) {
21963 var cos = Math.cos(theta);
21964 var sin = Math.sin(theta);
21965 var rotate = function rotate(x, y) {
21966 x = x - lx;
21967 y = y - ly;
21968 return {
21969 x: x * cos - y * sin + lx,
21970 y: x * sin + y * cos + ly
21971 };
21972 };
21973 var px1y1 = rotate(lx1, ly1);
21974 var px1y2 = rotate(lx1, ly2);
21975 var px2y1 = rotate(lx2, ly1);
21976 var px2y2 = rotate(lx2, ly2);
21977 var points = [
21978 // with the margin added after the rotation is applied
21979 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21980 if (pointInsidePolygonPoints(x, y, points)) {
21981 addEle(ele);
21982 return true;
21983 }
21984 } else {
21985 // do a cheaper bb check
21986 if (inBoundingBox(bb, x, y)) {
21987 addEle(ele);
21988 return true;
21989 }
21990 }
21991 }
21992 for (var i = eles.length - 1; i >= 0; i--) {
21993 // reverse order for precedence
21994 var ele = eles[i];
21995 if (ele.isNode()) {
21996 checkNode(ele) || checkLabel(ele);
21997 } else {
21998 // then edge
21999 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
22000 }
22001 }
22002 return near;
22003};
22004
22005// 'Give me everything from this box'
22006BRp$e.getAllInBox = function (x1, y1, x2, y2) {
22007 var eles = this.getCachedZSortedEles().interactive;
22008 var box = [];
22009 var x1c = Math.min(x1, x2);
22010 var x2c = Math.max(x1, x2);
22011 var y1c = Math.min(y1, y2);
22012 var y2c = Math.max(y1, y2);
22013 x1 = x1c;
22014 x2 = x2c;
22015 y1 = y1c;
22016 y2 = y2c;
22017 var boxBb = makeBoundingBox({
22018 x1: x1,
22019 y1: y1,
22020 x2: x2,
22021 y2: y2
22022 });
22023 for (var e = 0; e < eles.length; e++) {
22024 var ele = eles[e];
22025 if (ele.isNode()) {
22026 var node = ele;
22027 var nodeBb = node.boundingBox({
22028 includeNodes: true,
22029 includeEdges: false,
22030 includeLabels: false
22031 });
22032 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22033 box.push(node);
22034 }
22035 } else {
22036 var edge = ele;
22037 var _p = edge._private;
22038 var rs = _p.rscratch;
22039 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22040 continue;
22041 }
22042 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22043 continue;
22044 }
22045 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22046 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22047 var allInside = true;
22048 for (var i = 0; i < pts.length; i++) {
22049 if (!pointInBoundingBox(boxBb, pts[i])) {
22050 allInside = false;
22051 break;
22052 }
22053 }
22054 if (allInside) {
22055 box.push(edge);
22056 }
22057 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22058 box.push(edge);
22059 }
22060 }
22061 }
22062 return box;
22063};
22064
22065var BRp$d = {};
22066BRp$d.calculateArrowAngles = function (edge) {
22067 var rs = edge._private.rscratch;
22068 var isHaystack = rs.edgeType === 'haystack';
22069 var isBezier = rs.edgeType === 'bezier';
22070 var isMultibezier = rs.edgeType === 'multibezier';
22071 var isSegments = rs.edgeType === 'segments';
22072 var isCompound = rs.edgeType === 'compound';
22073 var isSelf = rs.edgeType === 'self';
22074
22075 // Displacement gives direction for arrowhead orientation
22076 var dispX, dispY;
22077 var startX, startY, endX, endY, midX, midY;
22078 if (isHaystack) {
22079 startX = rs.haystackPts[0];
22080 startY = rs.haystackPts[1];
22081 endX = rs.haystackPts[2];
22082 endY = rs.haystackPts[3];
22083 } else {
22084 startX = rs.arrowStartX;
22085 startY = rs.arrowStartY;
22086 endX = rs.arrowEndX;
22087 endY = rs.arrowEndY;
22088 }
22089 midX = rs.midX;
22090 midY = rs.midY;
22091
22092 // source
22093 //
22094
22095 if (isSegments) {
22096 dispX = startX - rs.segpts[0];
22097 dispY = startY - rs.segpts[1];
22098 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22099 var pts = rs.allpts;
22100 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22101 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22102 dispX = startX - bX;
22103 dispY = startY - bY;
22104 } else {
22105 dispX = startX - midX;
22106 dispY = startY - midY;
22107 }
22108 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY);
22109
22110 // mid target
22111 //
22112
22113 var midX = rs.midX;
22114 var midY = rs.midY;
22115 if (isHaystack) {
22116 midX = (startX + endX) / 2;
22117 midY = (startY + endY) / 2;
22118 }
22119 dispX = endX - startX;
22120 dispY = endY - startY;
22121 if (isSegments) {
22122 var pts = rs.allpts;
22123 if (pts.length / 2 % 2 === 0) {
22124 var i2 = pts.length / 2;
22125 var i1 = i2 - 2;
22126 dispX = pts[i2] - pts[i1];
22127 dispY = pts[i2 + 1] - pts[i1 + 1];
22128 } else if (rs.isRound) {
22129 dispX = rs.midVector[1];
22130 dispY = -rs.midVector[0];
22131 } else {
22132 var i2 = pts.length / 2 - 1;
22133 var i1 = i2 - 2;
22134 dispX = pts[i2] - pts[i1];
22135 dispY = pts[i2 + 1] - pts[i1 + 1];
22136 }
22137 } else if (isMultibezier || isCompound || isSelf) {
22138 var pts = rs.allpts;
22139 var cpts = rs.ctrlpts;
22140 var bp0x, bp0y;
22141 var bp1x, bp1y;
22142 if (cpts.length / 2 % 2 === 0) {
22143 var p0 = pts.length / 2 - 1; // startpt
22144 var ic = p0 + 2;
22145 var p1 = ic + 2;
22146 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22147 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22148 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22149 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22150 } else {
22151 var ic = pts.length / 2 - 1; // ctrpt
22152 var p0 = ic - 2; // startpt
22153 var p1 = ic + 2; // endpt
22154
22155 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22156 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22157 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22158 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22159 }
22160 dispX = bp1x - bp0x;
22161 dispY = bp1y - bp0y;
22162 }
22163 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22164 rs.midDispX = dispX;
22165 rs.midDispY = dispY;
22166
22167 // mid source
22168 //
22169
22170 dispX *= -1;
22171 dispY *= -1;
22172 if (isSegments) {
22173 var pts = rs.allpts;
22174 if (pts.length / 2 % 2 === 0) ; else if (!rs.isRound) {
22175 var i2 = pts.length / 2 - 1;
22176 var i3 = i2 + 2;
22177 dispX = -(pts[i3] - pts[i2]);
22178 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22179 }
22180 }
22181 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY);
22182
22183 // target
22184 //
22185
22186 if (isSegments) {
22187 dispX = endX - rs.segpts[rs.segpts.length - 2];
22188 dispY = endY - rs.segpts[rs.segpts.length - 1];
22189 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22190 var pts = rs.allpts;
22191 var l = pts.length;
22192 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22193 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22194 dispX = endX - bX;
22195 dispY = endY - bY;
22196 } else {
22197 dispX = endX - midX;
22198 dispY = endY - midY;
22199 }
22200 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22201};
22202BRp$d.getArrowWidth = BRp$d.getArrowHeight = function (edgeWidth, scale) {
22203 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22204 var cachedVal = cache[edgeWidth + ', ' + scale];
22205 if (cachedVal) {
22206 return cachedVal;
22207 }
22208 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22209 cache[edgeWidth + ', ' + scale] = cachedVal;
22210 return cachedVal;
22211};
22212
22213/**
22214 * Explained by Blindman67 at https://stackoverflow.com/a/44856925/11028828
22215 */
22216
22217// Declare reused variable to avoid reallocating variables every time the function is called
22218var x,
22219 y,
22220 v1 = {},
22221 v2 = {},
22222 sinA,
22223 sinA90,
22224 radDirection,
22225 drawDirection,
22226 angle,
22227 halfAngle,
22228 cRadius,
22229 lenOut,
22230 radius,
22231 limit;
22232var startX, startY, stopX, stopY;
22233var lastPoint;
22234
22235// convert 2 points into vector form, polar form, and normalised
22236var asVec = function asVec(p, pp, v) {
22237 v.x = pp.x - p.x;
22238 v.y = pp.y - p.y;
22239 v.len = Math.sqrt(v.x * v.x + v.y * v.y);
22240 v.nx = v.x / v.len;
22241 v.ny = v.y / v.len;
22242 v.ang = Math.atan2(v.ny, v.nx);
22243};
22244var invertVec = function invertVec(originalV, invertedV) {
22245 invertedV.x = originalV.x * -1;
22246 invertedV.y = originalV.y * -1;
22247 invertedV.nx = originalV.nx * -1;
22248 invertedV.ny = originalV.ny * -1;
22249 invertedV.ang = originalV.ang > 0 ? -(Math.PI - originalV.ang) : Math.PI + originalV.ang;
22250};
22251var calcCornerArc = function calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius) {
22252 //-----------------------------------------
22253 // Part 1
22254 previousPoint !== lastPoint ? asVec(currentPoint, previousPoint, v1) : invertVec(v2, v1); // Avoid recalculating vec if it is the invert of the last one calculated
22255 asVec(currentPoint, nextPoint, v2);
22256 sinA = v1.nx * v2.ny - v1.ny * v2.nx;
22257 sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny;
22258 angle = Math.asin(Math.max(-1, Math.min(1, sinA)));
22259 if (Math.abs(angle) < 1e-6) {
22260 x = currentPoint.x;
22261 y = currentPoint.y;
22262 cRadius = radius = 0;
22263 return;
22264 }
22265 //-----------------------------------------
22266 radDirection = 1;
22267 drawDirection = false;
22268 if (sinA90 < 0) {
22269 if (angle < 0) {
22270 angle = Math.PI + angle;
22271 } else {
22272 angle = Math.PI - angle;
22273 radDirection = -1;
22274 drawDirection = true;
22275 }
22276 } else {
22277 if (angle > 0) {
22278 radDirection = -1;
22279 drawDirection = true;
22280 }
22281 }
22282 if (currentPoint.radius !== undefined) {
22283 radius = currentPoint.radius;
22284 } else {
22285 radius = radiusMax;
22286 }
22287 //-----------------------------------------
22288 // Part 2
22289 halfAngle = angle / 2;
22290 //-----------------------------------------
22291
22292 limit = Math.min(v1.len / 2, v2.len / 2);
22293 if (isArcRadius) {
22294 //-----------------------------------------
22295 // Part 3
22296 lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));
22297
22298 //-----------------------------------------
22299 // Special part A
22300 if (lenOut > limit) {
22301 lenOut = limit;
22302 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
22303 } else {
22304 cRadius = radius;
22305 }
22306 } else {
22307 lenOut = Math.min(limit, radius);
22308 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
22309 }
22310 //-----------------------------------------
22311
22312 //-----------------------------------------
22313 // Part 4
22314 stopX = currentPoint.x + v2.nx * lenOut;
22315 stopY = currentPoint.y + v2.ny * lenOut;
22316 //-----------------------------------------
22317 // Part 5
22318 x = stopX - v2.ny * cRadius * radDirection;
22319 y = stopY + v2.nx * cRadius * radDirection;
22320 //-----------------------------------------
22321 // Additional Part : calculate start point E
22322 startX = currentPoint.x + v1.nx * lenOut;
22323 startY = currentPoint.y + v1.ny * lenOut;
22324
22325 // Save last point to avoid recalculating vector when not needed
22326 lastPoint = currentPoint;
22327};
22328
22329/**
22330 * Draw corner provided by {@link getRoundCorner}
22331 *
22332 * @param ctx :CanvasRenderingContext2D
22333 * @param roundCorner {{cx:number, cy:number, radius:number, endAngle: number, startAngle: number, counterClockwise: boolean}}
22334 */
22335function drawPreparedRoundCorner(ctx, roundCorner) {
22336 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);
22337}
22338
22339/**
22340 * Get round corner from a point and its previous and next neighbours in a path
22341 *
22342 * @param previousPoint {{x: number, y:number, radius: number?}}
22343 * @param currentPoint {{x: number, y:number, radius: number?}}
22344 * @param nextPoint {{x: number, y:number, radius: number?}}
22345 * @param radiusMax :number
22346 * @param isArcRadius :boolean
22347 * @return {{
22348 * cx:number, cy:number, radius:number,
22349 * startX:number, startY:number,
22350 * stopX:number, stopY: number,
22351 * endAngle: number, startAngle: number, counterClockwise: boolean
22352 * }}
22353 */
22354function getRoundCorner(previousPoint, currentPoint, nextPoint, radiusMax) {
22355 var isArcRadius = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
22356 if (radiusMax === 0 || currentPoint.radius === 0) return {
22357 cx: currentPoint.x,
22358 cy: currentPoint.y,
22359 radius: 0,
22360 startX: currentPoint.x,
22361 startY: currentPoint.y,
22362 stopX: currentPoint.x,
22363 stopY: currentPoint.y,
22364 startAngle: undefined,
22365 endAngle: undefined,
22366 counterClockwise: undefined
22367 };
22368 calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius);
22369 return {
22370 cx: x,
22371 cy: y,
22372 radius: cRadius,
22373 startX: startX,
22374 startY: startY,
22375 stopX: stopX,
22376 stopY: stopY,
22377 startAngle: v1.ang + Math.PI / 2 * radDirection,
22378 endAngle: v2.ang - Math.PI / 2 * radDirection,
22379 counterClockwise: drawDirection
22380 };
22381}
22382
22383var BRp$c = {};
22384BRp$c.findMidptPtsEtc = function (edge, pairInfo) {
22385 var posPts = pairInfo.posPts,
22386 intersectionPts = pairInfo.intersectionPts,
22387 vectorNormInverse = pairInfo.vectorNormInverse;
22388 var midptPts;
22389
22390 // n.b. assumes all edges in bezier bundle have same endpoints specified
22391 var srcManEndpt = edge.pstyle('source-endpoint');
22392 var tgtManEndpt = edge.pstyle('target-endpoint');
22393 var haveManualEndPts = srcManEndpt.units != null && tgtManEndpt.units != null;
22394 var recalcVectorNormInverse = function recalcVectorNormInverse(x1, y1, x2, y2) {
22395 var dy = y2 - y1;
22396 var dx = x2 - x1;
22397 var l = Math.sqrt(dx * dx + dy * dy);
22398 return {
22399 x: -dy / l,
22400 y: dx / l
22401 };
22402 };
22403 var edgeDistances = edge.pstyle('edge-distances').value;
22404 switch (edgeDistances) {
22405 case 'node-position':
22406 midptPts = posPts;
22407 break;
22408 case 'intersection':
22409 midptPts = intersectionPts;
22410 break;
22411 case 'endpoints':
22412 {
22413 if (haveManualEndPts) {
22414 var _this$manualEndptToPx = this.manualEndptToPx(edge.source()[0], srcManEndpt),
22415 _this$manualEndptToPx2 = _slicedToArray(_this$manualEndptToPx, 2),
22416 x1 = _this$manualEndptToPx2[0],
22417 y1 = _this$manualEndptToPx2[1];
22418 var _this$manualEndptToPx3 = this.manualEndptToPx(edge.target()[0], tgtManEndpt),
22419 _this$manualEndptToPx4 = _slicedToArray(_this$manualEndptToPx3, 2),
22420 x2 = _this$manualEndptToPx4[0],
22421 y2 = _this$manualEndptToPx4[1];
22422 var endPts = {
22423 x1: x1,
22424 y1: y1,
22425 x2: x2,
22426 y2: y2
22427 };
22428 vectorNormInverse = recalcVectorNormInverse(x1, y1, x2, y2);
22429 midptPts = endPts;
22430 } else {
22431 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)."));
22432 midptPts = intersectionPts; // back to default
22433 }
22434
22435 break;
22436 }
22437 }
22438 return {
22439 midptPts: midptPts,
22440 vectorNormInverse: vectorNormInverse
22441 };
22442};
22443BRp$c.findHaystackPoints = function (edges) {
22444 for (var i = 0; i < edges.length; i++) {
22445 var edge = edges[i];
22446 var _p = edge._private;
22447 var rs = _p.rscratch;
22448 if (!rs.haystack) {
22449 var angle = Math.random() * 2 * Math.PI;
22450 rs.source = {
22451 x: Math.cos(angle),
22452 y: Math.sin(angle)
22453 };
22454 angle = Math.random() * 2 * Math.PI;
22455 rs.target = {
22456 x: Math.cos(angle),
22457 y: Math.sin(angle)
22458 };
22459 }
22460 var src = _p.source;
22461 var tgt = _p.target;
22462 var srcPos = src.position();
22463 var tgtPos = tgt.position();
22464 var srcW = src.width();
22465 var tgtW = tgt.width();
22466 var srcH = src.height();
22467 var tgtH = tgt.height();
22468 var radius = edge.pstyle('haystack-radius').value;
22469 var halfRadius = radius / 2; // b/c have to half width/height
22470
22471 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];
22472 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22473 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2;
22474
22475 // always override as haystack in case set to different type previously
22476 rs.edgeType = 'haystack';
22477 rs.haystack = true;
22478 this.storeEdgeProjections(edge);
22479 this.calculateArrowAngles(edge);
22480 this.recalculateEdgeLabelProjections(edge);
22481 this.calculateLabelAngles(edge);
22482 }
22483};
22484BRp$c.findSegmentsPoints = function (edge, pairInfo) {
22485 // Segments (multiple straight lines)
22486
22487 var rs = edge._private.rscratch;
22488 var segmentWs = edge.pstyle('segment-weights');
22489 var segmentDs = edge.pstyle('segment-distances');
22490 var segmentRs = edge.pstyle('segment-radii');
22491 var segmentTs = edge.pstyle('radius-type');
22492 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22493 var lastRadius = segmentRs.pfValue[segmentRs.pfValue.length - 1];
22494 var lastRadiusType = segmentTs.pfValue[segmentTs.pfValue.length - 1];
22495 rs.edgeType = 'segments';
22496 rs.segpts = [];
22497 rs.radii = [];
22498 rs.isArcRadius = [];
22499 for (var s = 0; s < segmentsN; s++) {
22500 var w = segmentWs.pfValue[s];
22501 var d = segmentDs.pfValue[s];
22502 var w1 = 1 - w;
22503 var w2 = w;
22504 var _this$findMidptPtsEtc = this.findMidptPtsEtc(edge, pairInfo),
22505 midptPts = _this$findMidptPtsEtc.midptPts,
22506 vectorNormInverse = _this$findMidptPtsEtc.vectorNormInverse;
22507 var adjustedMidpt = {
22508 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22509 y: midptPts.y1 * w1 + midptPts.y2 * w2
22510 };
22511 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22512 rs.radii.push(segmentRs.pfValue[s] !== undefined ? segmentRs.pfValue[s] : lastRadius);
22513 rs.isArcRadius.push((segmentTs.pfValue[s] !== undefined ? segmentTs.pfValue[s] : lastRadiusType) === 'arc-radius');
22514 }
22515};
22516BRp$c.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22517 // Self-edge
22518
22519 var rs = edge._private.rscratch;
22520 var dirCounts = pairInfo.dirCounts,
22521 srcPos = pairInfo.srcPos;
22522 var ctrlptDists = edge.pstyle('control-point-distances');
22523 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22524 var loopDir = edge.pstyle('loop-direction').pfValue;
22525 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22526 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22527 rs.edgeType = 'self';
22528 var j = i;
22529 var loopDist = stepSize;
22530 if (edgeIsUnbundled) {
22531 j = 0;
22532 loopDist = ctrlptDist;
22533 }
22534 var loopAngle = loopDir - Math.PI / 2;
22535 var outAngle = loopAngle - loopSwp / 2;
22536 var inAngle = loopAngle + loopSwp / 2;
22537
22538 // increase by step size for overlapping loops, keyed on direction and sweep values
22539 var dc = String(loopDir + '_' + loopSwp);
22540 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22541 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)];
22542};
22543BRp$c.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22544 // Compound edge
22545
22546 var rs = edge._private.rscratch;
22547 rs.edgeType = 'compound';
22548 var srcPos = pairInfo.srcPos,
22549 tgtPos = pairInfo.tgtPos,
22550 srcW = pairInfo.srcW,
22551 srcH = pairInfo.srcH,
22552 tgtW = pairInfo.tgtW,
22553 tgtH = pairInfo.tgtH;
22554 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22555 var ctrlptDists = edge.pstyle('control-point-distances');
22556 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22557 var j = i;
22558 var loopDist = stepSize;
22559 if (edgeIsUnbundled) {
22560 j = 0;
22561 loopDist = ctrlptDist;
22562 }
22563 var loopW = 50;
22564 var loopaPos = {
22565 x: srcPos.x - srcW / 2,
22566 y: srcPos.y - srcH / 2
22567 };
22568 var loopbPos = {
22569 x: tgtPos.x - tgtW / 2,
22570 y: tgtPos.y - tgtH / 2
22571 };
22572 var loopPos = {
22573 x: Math.min(loopaPos.x, loopbPos.x),
22574 y: Math.min(loopaPos.y, loopbPos.y)
22575 };
22576
22577 // avoids cases with impossible beziers
22578 var minCompoundStretch = 0.5;
22579 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22580 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22581 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];
22582};
22583BRp$c.findStraightEdgePoints = function (edge) {
22584 // Straight edge within bundle
22585
22586 edge._private.rscratch.edgeType = 'straight';
22587};
22588BRp$c.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22589 var rs = edge._private.rscratch;
22590 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22591 var ctrlptDists = edge.pstyle('control-point-distances');
22592 var ctrlptWs = edge.pstyle('control-point-weights');
22593 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22594 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22595 var ctrlptWeight = ctrlptWs.value[0];
22596
22597 // (Multi)bezier
22598
22599 var multi = edgeIsUnbundled;
22600 rs.edgeType = multi ? 'multibezier' : 'bezier';
22601 rs.ctrlpts = [];
22602 for (var b = 0; b < bezierN; b++) {
22603 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22604 var manctrlptDist = void 0;
22605 var sign = signum(normctrlptDist);
22606 if (multi) {
22607 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22608 ctrlptWeight = ctrlptWs.value[b];
22609 }
22610 if (edgeIsUnbundled) {
22611 // multi or single unbundled
22612 manctrlptDist = ctrlptDist;
22613 } else {
22614 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22615 }
22616 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22617 var w1 = 1 - ctrlptWeight;
22618 var w2 = ctrlptWeight;
22619 var _this$findMidptPtsEtc2 = this.findMidptPtsEtc(edge, pairInfo),
22620 midptPts = _this$findMidptPtsEtc2.midptPts,
22621 vectorNormInverse = _this$findMidptPtsEtc2.vectorNormInverse;
22622 var adjustedMidpt = {
22623 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22624 y: midptPts.y1 * w1 + midptPts.y2 * w2
22625 };
22626 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22627 }
22628};
22629BRp$c.findTaxiPoints = function (edge, pairInfo) {
22630 // Taxicab geometry with two turns maximum
22631
22632 var rs = edge._private.rscratch;
22633 rs.edgeType = 'segments';
22634 var VERTICAL = 'vertical';
22635 var HORIZONTAL = 'horizontal';
22636 var LEFTWARD = 'leftward';
22637 var RIGHTWARD = 'rightward';
22638 var DOWNWARD = 'downward';
22639 var UPWARD = 'upward';
22640 var AUTO = 'auto';
22641 var posPts = pairInfo.posPts,
22642 srcW = pairInfo.srcW,
22643 srcH = pairInfo.srcH,
22644 tgtW = pairInfo.tgtW,
22645 tgtH = pairInfo.tgtH;
22646 var edgeDistances = edge.pstyle('edge-distances').value;
22647 var dIncludesNodeBody = edgeDistances !== 'node-position';
22648 var taxiDir = edge.pstyle('taxi-direction').value;
22649 var rawTaxiDir = taxiDir; // unprocessed value
22650 var taxiTurn = edge.pstyle('taxi-turn');
22651 var turnIsPercent = taxiTurn.units === '%';
22652 var taxiTurnPfVal = taxiTurn.pfValue;
22653 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22654 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22655 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22656 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22657 var pdx = posPts.x2 - posPts.x1;
22658 var pdy = posPts.y2 - posPts.y1;
22659
22660 // take away the effective w/h from the magnitude of the delta value
22661 var subDWH = function subDWH(dxy, dwh) {
22662 if (dxy > 0) {
22663 return Math.max(dxy - dwh, 0);
22664 } else {
22665 return Math.min(dxy + dwh, 0);
22666 }
22667 };
22668 var dx = subDWH(pdx, dw);
22669 var dy = subDWH(pdy, dh);
22670 var isExplicitDir = false;
22671 if (rawTaxiDir === AUTO) {
22672 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22673 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22674 taxiDir = VERTICAL;
22675 isExplicitDir = true;
22676 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22677 taxiDir = HORIZONTAL;
22678 isExplicitDir = true;
22679 }
22680 var isVert = taxiDir === VERTICAL;
22681 var l = isVert ? dy : dx;
22682 var pl = isVert ? pdy : pdx;
22683 var sgnL = signum(pl);
22684 var forcedDir = false;
22685 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22686 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22687 sgnL *= -1;
22688 l = sgnL * Math.abs(l);
22689 forcedDir = true;
22690 }
22691 var d;
22692 if (turnIsPercent) {
22693 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22694 d = p * l;
22695 } else {
22696 var k = taxiTurnPfVal < 0 ? l : 0;
22697 d = k + taxiTurnPfVal * sgnL;
22698 }
22699 var getIsTooClose = function getIsTooClose(d) {
22700 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22701 };
22702 var isTooCloseSrc = getIsTooClose(d);
22703 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22704 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22705 if (isTooClose && !forcedDir) {
22706 // non-ideal routing
22707 if (isVert) {
22708 // vertical fallbacks
22709 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22710 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22711 if (lShapeInsideSrc) {
22712 // horizontal Z-shape (direction not respected)
22713 var x = (posPts.x1 + posPts.x2) / 2;
22714 var y1 = posPts.y1,
22715 y2 = posPts.y2;
22716 rs.segpts = [x, y1, x, y2];
22717 } else if (lShapeInsideTgt) {
22718 // vertical Z-shape (distance not respected)
22719 var y = (posPts.y1 + posPts.y2) / 2;
22720 var x1 = posPts.x1,
22721 x2 = posPts.x2;
22722 rs.segpts = [x1, y, x2, y];
22723 } else {
22724 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22725 rs.segpts = [posPts.x1, posPts.y2];
22726 }
22727 } else {
22728 // horizontal fallbacks
22729 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22730 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22731 if (_lShapeInsideSrc) {
22732 // vertical Z-shape (direction not respected)
22733 var _y = (posPts.y1 + posPts.y2) / 2;
22734 var _x = posPts.x1,
22735 _x2 = posPts.x2;
22736 rs.segpts = [_x, _y, _x2, _y];
22737 } else if (_lShapeInsideTgt) {
22738 // horizontal Z-shape (turn distance not respected)
22739 var _x3 = (posPts.x1 + posPts.x2) / 2;
22740 var _y2 = posPts.y1,
22741 _y3 = posPts.y2;
22742 rs.segpts = [_x3, _y2, _x3, _y3];
22743 } else {
22744 // L-shape (turn distance not respected, but works well for tree siblings)
22745 rs.segpts = [posPts.x2, posPts.y1];
22746 }
22747 }
22748 } else {
22749 // ideal routing
22750 if (isVert) {
22751 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22752 var _x4 = posPts.x1,
22753 _x5 = posPts.x2;
22754 rs.segpts = [_x4, _y4, _x5, _y4];
22755 } else {
22756 // horizontal
22757 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22758 var _y5 = posPts.y1,
22759 _y6 = posPts.y2;
22760 rs.segpts = [_x6, _y5, _x6, _y6];
22761 }
22762 }
22763 if (rs.isRound) {
22764 var radius = edge.pstyle('taxi-radius').value;
22765 var isArcRadius = edge.pstyle('radius-type').value[0] === 'arc-radius';
22766 rs.radii = new Array(rs.segpts.length / 2).fill(radius);
22767 rs.isArcRadius = new Array(rs.segpts.length / 2).fill(isArcRadius);
22768 }
22769};
22770BRp$c.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22771 var rs = edge._private.rscratch;
22772
22773 // can only correct beziers for now...
22774 if (rs.edgeType === 'bezier') {
22775 var srcPos = pairInfo.srcPos,
22776 tgtPos = pairInfo.tgtPos,
22777 srcW = pairInfo.srcW,
22778 srcH = pairInfo.srcH,
22779 tgtW = pairInfo.tgtW,
22780 tgtH = pairInfo.tgtH,
22781 srcShape = pairInfo.srcShape,
22782 tgtShape = pairInfo.tgtShape,
22783 srcCornerRadius = pairInfo.srcCornerRadius,
22784 tgtCornerRadius = pairInfo.tgtCornerRadius,
22785 srcRs = pairInfo.srcRs,
22786 tgtRs = pairInfo.tgtRs;
22787 var badStart = !number$1(rs.startX) || !number$1(rs.startY);
22788 var badAStart = !number$1(rs.arrowStartX) || !number$1(rs.arrowStartY);
22789 var badEnd = !number$1(rs.endX) || !number$1(rs.endY);
22790 var badAEnd = !number$1(rs.arrowEndX) || !number$1(rs.arrowEndY);
22791 var minCpADistFactor = 3;
22792 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22793 var minCpADist = minCpADistFactor * arrowW;
22794 var startACpDist = dist({
22795 x: rs.ctrlpts[0],
22796 y: rs.ctrlpts[1]
22797 }, {
22798 x: rs.startX,
22799 y: rs.startY
22800 });
22801 var closeStartACp = startACpDist < minCpADist;
22802 var endACpDist = dist({
22803 x: rs.ctrlpts[0],
22804 y: rs.ctrlpts[1]
22805 }, {
22806 x: rs.endX,
22807 y: rs.endY
22808 });
22809 var closeEndACp = endACpDist < minCpADist;
22810 var overlapping = false;
22811 if (badStart || badAStart || closeStartACp) {
22812 overlapping = true;
22813
22814 // project control point along line from src centre to outside the src shape
22815 // (otherwise intersection will yield nothing)
22816 var cpD = {
22817 // delta
22818 x: rs.ctrlpts[0] - srcPos.x,
22819 y: rs.ctrlpts[1] - srcPos.y
22820 };
22821 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22822 var cpM = {
22823 // normalised delta
22824 x: cpD.x / cpL,
22825 y: cpD.y / cpL
22826 };
22827 var radius = Math.max(srcW, srcH);
22828 var cpProj = {
22829 // *2 radius guarantees outside shape
22830 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22831 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22832 };
22833 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0, srcCornerRadius, srcRs);
22834 if (closeStartACp) {
22835 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22836 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22837 } else {
22838 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22839 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22840 }
22841 }
22842 if (badEnd || badAEnd || closeEndACp) {
22843 overlapping = true;
22844
22845 // project control point along line from tgt centre to outside the tgt shape
22846 // (otherwise intersection will yield nothing)
22847 var _cpD = {
22848 // delta
22849 x: rs.ctrlpts[0] - tgtPos.x,
22850 y: rs.ctrlpts[1] - tgtPos.y
22851 };
22852 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22853 var _cpM = {
22854 // normalised delta
22855 x: _cpD.x / _cpL,
22856 y: _cpD.y / _cpL
22857 };
22858 var _radius = Math.max(srcW, srcH);
22859 var _cpProj = {
22860 // *2 radius guarantees outside shape
22861 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22862 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22863 };
22864 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0, tgtCornerRadius, tgtRs);
22865 if (closeEndACp) {
22866 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22867 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22868 } else {
22869 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22870 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22871 }
22872 }
22873 if (overlapping) {
22874 // recalc endpts
22875 this.findEndpoints(edge);
22876 }
22877 }
22878};
22879BRp$c.storeAllpts = function (edge) {
22880 var rs = edge._private.rscratch;
22881 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22882 rs.allpts = [];
22883 rs.allpts.push(rs.startX, rs.startY);
22884 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22885 // ctrl pt itself
22886 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]);
22887
22888 // the midpt between ctrlpts as intermediate destination pts
22889 if (b + 3 < rs.ctrlpts.length) {
22890 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22891 }
22892 }
22893 rs.allpts.push(rs.endX, rs.endY);
22894 var m, mt;
22895 if (rs.ctrlpts.length / 2 % 2 === 0) {
22896 m = rs.allpts.length / 2 - 1;
22897 rs.midX = rs.allpts[m];
22898 rs.midY = rs.allpts[m + 1];
22899 } else {
22900 m = rs.allpts.length / 2 - 3;
22901 mt = 0.5;
22902 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22903 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22904 }
22905 } else if (rs.edgeType === 'straight') {
22906 // need to calc these after endpts
22907 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY];
22908
22909 // default midpt for labels etc
22910 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22911 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22912 } else if (rs.edgeType === 'segments') {
22913 rs.allpts = [];
22914 rs.allpts.push(rs.startX, rs.startY);
22915 rs.allpts.push.apply(rs.allpts, rs.segpts);
22916 rs.allpts.push(rs.endX, rs.endY);
22917 if (rs.isRound) {
22918 rs.roundCorners = [];
22919 for (var i = 2; i + 3 < rs.allpts.length; i += 2) {
22920 var radius = rs.radii[i / 2 - 1];
22921 var isArcRadius = rs.isArcRadius[i / 2 - 1];
22922 rs.roundCorners.push(getRoundCorner({
22923 x: rs.allpts[i - 2],
22924 y: rs.allpts[i - 1]
22925 }, {
22926 x: rs.allpts[i],
22927 y: rs.allpts[i + 1],
22928 radius: radius
22929 }, {
22930 x: rs.allpts[i + 2],
22931 y: rs.allpts[i + 3]
22932 }, radius, isArcRadius));
22933 }
22934 }
22935 if (rs.segpts.length % 4 === 0) {
22936 var i2 = rs.segpts.length / 2;
22937 var i1 = i2 - 2;
22938 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22939 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22940 } else {
22941 var _i = rs.segpts.length / 2 - 1;
22942 if (!rs.isRound) {
22943 rs.midX = rs.segpts[_i];
22944 rs.midY = rs.segpts[_i + 1];
22945 } else {
22946 var point = {
22947 x: rs.segpts[_i],
22948 y: rs.segpts[_i + 1]
22949 };
22950 var corner = rs.roundCorners[_i / 2];
22951 var v = [point.x - corner.cx, point.y - corner.cy];
22952 var factor = corner.radius / Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2));
22953 v = v.map(function (c) {
22954 return c * factor;
22955 });
22956 rs.midX = corner.cx + v[0];
22957 rs.midY = corner.cy + v[1];
22958 rs.midVector = v;
22959 }
22960 }
22961 }
22962};
22963BRp$c.checkForInvalidEdgeWarning = function (edge) {
22964 var rs = edge[0]._private.rscratch;
22965 if (rs.nodesOverlap || number$1(rs.startX) && number$1(rs.startY) && number$1(rs.endX) && number$1(rs.endY)) {
22966 rs.loggedErr = false;
22967 } else {
22968 if (!rs.loggedErr) {
22969 rs.loggedErr = true;
22970 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.');
22971 }
22972 }
22973};
22974BRp$c.findEdgeControlPoints = function (edges) {
22975 var _this = this;
22976 if (!edges || edges.length === 0) {
22977 return;
22978 }
22979 var r = this;
22980 var cy = r.cy;
22981 var hasCompounds = cy.hasCompoundNodes();
22982 var hashTable = {
22983 map: new Map$2(),
22984 get: function get(pairId) {
22985 var map2 = this.map.get(pairId[0]);
22986 if (map2 != null) {
22987 return map2.get(pairId[1]);
22988 } else {
22989 return null;
22990 }
22991 },
22992 set: function set(pairId, val) {
22993 var map2 = this.map.get(pairId[0]);
22994 if (map2 == null) {
22995 map2 = new Map$2();
22996 this.map.set(pairId[0], map2);
22997 }
22998 map2.set(pairId[1], val);
22999 }
23000 };
23001 var pairIds = [];
23002 var haystackEdges = [];
23003
23004 // create a table of edge (src, tgt) => list of edges between them
23005 for (var i = 0; i < edges.length; i++) {
23006 var edge = edges[i];
23007 var _p = edge._private;
23008 var curveStyle = edge.pstyle('curve-style').value;
23009
23010 // ignore edges who are not to be displayed
23011 // they shouldn't take up space
23012 if (edge.removed() || !edge.takesUpSpace()) {
23013 continue;
23014 }
23015 if (curveStyle === 'haystack') {
23016 haystackEdges.push(edge);
23017 continue;
23018 }
23019 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle.endsWith('segments') || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle.endsWith('taxi');
23020 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
23021 var src = _p.source;
23022 var tgt = _p.target;
23023 var srcIndex = src.poolIndex();
23024 var tgtIndex = tgt.poolIndex();
23025 var pairId = [srcIndex, tgtIndex].sort();
23026 var tableEntry = hashTable.get(pairId);
23027 if (tableEntry == null) {
23028 tableEntry = {
23029 eles: []
23030 };
23031 hashTable.set(pairId, tableEntry);
23032 pairIds.push(pairId);
23033 }
23034 tableEntry.eles.push(edge);
23035 if (edgeIsUnbundled) {
23036 tableEntry.hasUnbundled = true;
23037 }
23038 if (edgeIsBezier) {
23039 tableEntry.hasBezier = true;
23040 }
23041 }
23042
23043 // for each pair (src, tgt), create the ctrl pts
23044 // Nested for loop is OK; total number of iterations for both loops = edgeCount
23045 var _loop = function _loop(p) {
23046 var pairId = pairIds[p];
23047 var pairInfo = hashTable.get(pairId);
23048 var swappedpairInfo = void 0;
23049 if (!pairInfo.hasUnbundled) {
23050 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
23051 return e.isBundledBezier();
23052 });
23053 clearArray(pairInfo.eles);
23054 pllEdges.forEach(function (edge) {
23055 return pairInfo.eles.push(edge);
23056 });
23057
23058 // for each pair id, the edges should be sorted by index
23059 pairInfo.eles.sort(function (edge1, edge2) {
23060 return edge1.poolIndex() - edge2.poolIndex();
23061 });
23062 }
23063 var firstEdge = pairInfo.eles[0];
23064 var src = firstEdge.source();
23065 var tgt = firstEdge.target();
23066
23067 // make sure src/tgt distinction is consistent w.r.t. pairId
23068 if (src.poolIndex() > tgt.poolIndex()) {
23069 var temp = src;
23070 src = tgt;
23071 tgt = temp;
23072 }
23073 var srcPos = pairInfo.srcPos = src.position();
23074 var tgtPos = pairInfo.tgtPos = tgt.position();
23075 var srcW = pairInfo.srcW = src.outerWidth();
23076 var srcH = pairInfo.srcH = src.outerHeight();
23077 var tgtW = pairInfo.tgtW = tgt.outerWidth();
23078 var tgtH = pairInfo.tgtH = tgt.outerHeight();
23079 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
23080 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
23081 var srcCornerRadius = pairInfo.srcCornerRadius = src.pstyle('corner-radius').value === 'auto' ? 'auto' : src.pstyle('corner-radius').pfValue;
23082 var tgtCornerRadius = pairInfo.tgtCornerRadius = tgt.pstyle('corner-radius').value === 'auto' ? 'auto' : tgt.pstyle('corner-radius').pfValue;
23083 var tgtRs = pairInfo.tgtRs = tgt._private.rscratch;
23084 var srcRs = pairInfo.srcRs = src._private.rscratch;
23085 pairInfo.dirCounts = {
23086 'north': 0,
23087 'west': 0,
23088 'south': 0,
23089 'east': 0,
23090 'northwest': 0,
23091 'southwest': 0,
23092 'northeast': 0,
23093 'southeast': 0
23094 };
23095 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
23096 var _edge = pairInfo.eles[_i2];
23097 var rs = _edge[0]._private.rscratch;
23098 var _curveStyle = _edge.pstyle('curve-style').value;
23099 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle.endsWith('segments') || _curveStyle.endsWith('taxi');
23100
23101 // whether the normalised pair order is the reverse of the edge's src-tgt order
23102 var edgeIsSwapped = !src.same(_edge.source());
23103 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
23104 pairInfo.calculatedIntersection = true;
23105
23106 // pt outside src shape to calc distance/displacement from src to tgt
23107 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0, srcCornerRadius, srcRs);
23108 var srcIntn = pairInfo.srcIntn = srcOutside;
23109
23110 // pt outside tgt shape to calc distance/displacement from src to tgt
23111 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0, tgtCornerRadius, tgtRs);
23112 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
23113 var intersectionPts = pairInfo.intersectionPts = {
23114 x1: srcOutside[0],
23115 x2: tgtOutside[0],
23116 y1: srcOutside[1],
23117 y2: tgtOutside[1]
23118 };
23119 var posPts = pairInfo.posPts = {
23120 x1: srcPos.x,
23121 x2: tgtPos.x,
23122 y1: srcPos.y,
23123 y2: tgtPos.y
23124 };
23125 var dy = tgtOutside[1] - srcOutside[1];
23126 var dx = tgtOutside[0] - srcOutside[0];
23127 var l = Math.sqrt(dx * dx + dy * dy);
23128 var vector = pairInfo.vector = {
23129 x: dx,
23130 y: dy
23131 };
23132 var vectorNorm = pairInfo.vectorNorm = {
23133 x: vector.x / l,
23134 y: vector.y / l
23135 };
23136 var vectorNormInverse = {
23137 x: -vectorNorm.y,
23138 y: vectorNorm.x
23139 };
23140
23141 // if node shapes overlap, then no ctrl pts to draw
23142 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);
23143 pairInfo.vectorNormInverse = vectorNormInverse;
23144 swappedpairInfo = {
23145 nodesOverlap: pairInfo.nodesOverlap,
23146 dirCounts: pairInfo.dirCounts,
23147 calculatedIntersection: true,
23148 hasBezier: pairInfo.hasBezier,
23149 hasUnbundled: pairInfo.hasUnbundled,
23150 eles: pairInfo.eles,
23151 srcPos: tgtPos,
23152 tgtPos: srcPos,
23153 srcW: tgtW,
23154 srcH: tgtH,
23155 tgtW: srcW,
23156 tgtH: srcH,
23157 srcIntn: tgtIntn,
23158 tgtIntn: srcIntn,
23159 srcShape: tgtShape,
23160 tgtShape: srcShape,
23161 posPts: {
23162 x1: posPts.x2,
23163 y1: posPts.y2,
23164 x2: posPts.x1,
23165 y2: posPts.y1
23166 },
23167 intersectionPts: {
23168 x1: intersectionPts.x2,
23169 y1: intersectionPts.y2,
23170 x2: intersectionPts.x1,
23171 y2: intersectionPts.y1
23172 },
23173 vector: {
23174 x: -vector.x,
23175 y: -vector.y
23176 },
23177 vectorNorm: {
23178 x: -vectorNorm.x,
23179 y: -vectorNorm.y
23180 },
23181 vectorNormInverse: {
23182 x: -vectorNormInverse.x,
23183 y: -vectorNormInverse.y
23184 }
23185 };
23186 }
23187 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
23188 rs.nodesOverlap = passedPairInfo.nodesOverlap;
23189 rs.srcIntn = passedPairInfo.srcIntn;
23190 rs.tgtIntn = passedPairInfo.tgtIntn;
23191 rs.isRound = _curveStyle.startsWith('round');
23192 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
23193 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23194 } else if (src === tgt) {
23195 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23196 } else if (_curveStyle.endsWith('segments')) {
23197 _this.findSegmentsPoints(_edge, passedPairInfo);
23198 } else if (_curveStyle.endsWith('taxi')) {
23199 _this.findTaxiPoints(_edge, passedPairInfo);
23200 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
23201 _this.findStraightEdgePoints(_edge);
23202 } else {
23203 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
23204 }
23205 _this.findEndpoints(_edge);
23206 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
23207 _this.checkForInvalidEdgeWarning(_edge);
23208 _this.storeAllpts(_edge);
23209 _this.storeEdgeProjections(_edge);
23210 _this.calculateArrowAngles(_edge);
23211 _this.recalculateEdgeLabelProjections(_edge);
23212 _this.calculateLabelAngles(_edge);
23213 } // for pair edges
23214 };
23215 for (var p = 0; p < pairIds.length; p++) {
23216 _loop(p);
23217 } // for pair ids
23218
23219 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
23220 this.findHaystackPoints(haystackEdges);
23221};
23222function getPts(pts) {
23223 var retPts = [];
23224 if (pts == null) {
23225 return;
23226 }
23227 for (var i = 0; i < pts.length; i += 2) {
23228 var x = pts[i];
23229 var y = pts[i + 1];
23230 retPts.push({
23231 x: x,
23232 y: y
23233 });
23234 }
23235 return retPts;
23236}
23237BRp$c.getSegmentPoints = function (edge) {
23238 var rs = edge[0]._private.rscratch;
23239 var type = rs.edgeType;
23240 if (type === 'segments') {
23241 this.recalculateRenderedStyle(edge);
23242 return getPts(rs.segpts);
23243 }
23244};
23245BRp$c.getControlPoints = function (edge) {
23246 var rs = edge[0]._private.rscratch;
23247 var type = rs.edgeType;
23248 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23249 this.recalculateRenderedStyle(edge);
23250 return getPts(rs.ctrlpts);
23251 }
23252};
23253BRp$c.getEdgeMidpoint = function (edge) {
23254 var rs = edge[0]._private.rscratch;
23255 this.recalculateRenderedStyle(edge);
23256 return {
23257 x: rs.midX,
23258 y: rs.midY
23259 };
23260};
23261
23262var BRp$b = {};
23263BRp$b.manualEndptToPx = function (node, prop) {
23264 var r = this;
23265 var npos = node.position();
23266 var w = node.outerWidth();
23267 var h = node.outerHeight();
23268 var rs = node._private.rscratch;
23269 if (prop.value.length === 2) {
23270 var p = [prop.pfValue[0], prop.pfValue[1]];
23271 if (prop.units[0] === '%') {
23272 p[0] = p[0] * w;
23273 }
23274 if (prop.units[1] === '%') {
23275 p[1] = p[1] * h;
23276 }
23277 p[0] += npos.x;
23278 p[1] += npos.y;
23279 return p;
23280 } else {
23281 var angle = prop.pfValue[0];
23282 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23283
23284 var l = 2 * Math.max(w, h);
23285 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23286 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);
23287 }
23288};
23289BRp$b.findEndpoints = function (edge) {
23290 var r = this;
23291 var intersect;
23292 var source = edge.source()[0];
23293 var target = edge.target()[0];
23294 var srcPos = source.position();
23295 var tgtPos = target.position();
23296 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23297 var srcArShape = edge.pstyle('source-arrow-shape').value;
23298 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23299 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23300 var srcRs = source._private.rscratch;
23301 var tgtRs = target._private.rscratch;
23302 var curveStyle = edge.pstyle('curve-style').value;
23303 var rs = edge._private.rscratch;
23304 var et = rs.edgeType;
23305 var taxi = curveStyle === 'taxi';
23306 var self = et === 'self' || et === 'compound';
23307 var bezier = et === 'bezier' || et === 'multibezier' || self;
23308 var multi = et !== 'bezier';
23309 var lines = et === 'straight' || et === 'segments';
23310 var segments = et === 'segments';
23311 var hasEndpts = bezier || multi || lines;
23312 var overrideEndpts = self || taxi;
23313 var srcManEndpt = edge.pstyle('source-endpoint');
23314 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23315 var srcCornerRadius = source.pstyle('corner-radius').value === 'auto' ? 'auto' : source.pstyle('corner-radius').pfValue;
23316 var tgtManEndpt = edge.pstyle('target-endpoint');
23317 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23318 var tgtCornerRadius = target.pstyle('corner-radius').value === 'auto' ? 'auto' : target.pstyle('corner-radius').pfValue;
23319 rs.srcManEndpt = srcManEndpt;
23320 rs.tgtManEndpt = tgtManEndpt;
23321 var p1; // last known point of edge on target side
23322 var p2; // last known point of edge on source side
23323
23324 var p1_i; // point to intersect with target shape
23325 var p2_i; // point to intersect with source shape
23326
23327 if (bezier) {
23328 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23329 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23330 p1 = cpEnd;
23331 p2 = cpStart;
23332 } else if (lines) {
23333 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23334 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23335 p1 = tgtArrowFromPt;
23336 p2 = srcArrowFromPt;
23337 }
23338 if (tgtManEndptVal === 'inside-to-node') {
23339 intersect = [tgtPos.x, tgtPos.y];
23340 } else if (tgtManEndpt.units) {
23341 intersect = this.manualEndptToPx(target, tgtManEndpt);
23342 } else if (tgtManEndptVal === 'outside-to-line') {
23343 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23344 } else {
23345 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23346 p1_i = p1;
23347 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23348 p1_i = [srcPos.x, srcPos.y];
23349 }
23350 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0, tgtCornerRadius, tgtRs);
23351 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23352 var trs = target._private.rscratch;
23353 var lw = trs.labelWidth;
23354 var lh = trs.labelHeight;
23355 var lx = trs.labelX;
23356 var ly = trs.labelY;
23357 var lw2 = lw / 2;
23358 var lh2 = lh / 2;
23359 var va = target.pstyle('text-valign').value;
23360 if (va === 'top') {
23361 ly -= lh2;
23362 } else if (va === 'bottom') {
23363 ly += lh2;
23364 }
23365 var ha = target.pstyle('text-halign').value;
23366 if (ha === 'left') {
23367 lx -= lw2;
23368 } else if (ha === 'right') {
23369 lx += lw2;
23370 }
23371 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);
23372 if (labelIntersect.length > 0) {
23373 var refPt = srcPos;
23374 var intSqdist = sqdist(refPt, array2point(intersect));
23375 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23376 var minSqDist = intSqdist;
23377 if (labIntSqdist < intSqdist) {
23378 intersect = labelIntersect;
23379 minSqDist = labIntSqdist;
23380 }
23381 if (labelIntersect.length > 2) {
23382 var labInt2SqDist = sqdist(refPt, {
23383 x: labelIntersect[2],
23384 y: labelIntersect[3]
23385 });
23386 if (labInt2SqDist < minSqDist) {
23387 intersect = [labelIntersect[2], labelIntersect[3]];
23388 }
23389 }
23390 }
23391 }
23392 }
23393 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23394 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23395 rs.endX = edgeEnd[0];
23396 rs.endY = edgeEnd[1];
23397 rs.arrowEndX = arrowEnd[0];
23398 rs.arrowEndY = arrowEnd[1];
23399 if (srcManEndptVal === 'inside-to-node') {
23400 intersect = [srcPos.x, srcPos.y];
23401 } else if (srcManEndpt.units) {
23402 intersect = this.manualEndptToPx(source, srcManEndpt);
23403 } else if (srcManEndptVal === 'outside-to-line') {
23404 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23405 } else {
23406 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23407 p2_i = p2;
23408 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23409 p2_i = [tgtPos.x, tgtPos.y];
23410 }
23411 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0, srcCornerRadius, srcRs);
23412 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23413 var srs = source._private.rscratch;
23414 var _lw = srs.labelWidth;
23415 var _lh = srs.labelHeight;
23416 var _lx = srs.labelX;
23417 var _ly = srs.labelY;
23418 var _lw2 = _lw / 2;
23419 var _lh2 = _lh / 2;
23420 var _va = source.pstyle('text-valign').value;
23421 if (_va === 'top') {
23422 _ly -= _lh2;
23423 } else if (_va === 'bottom') {
23424 _ly += _lh2;
23425 }
23426 var _ha = source.pstyle('text-halign').value;
23427 if (_ha === 'left') {
23428 _lx -= _lw2;
23429 } else if (_ha === 'right') {
23430 _lx += _lw2;
23431 }
23432 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);
23433 if (_labelIntersect.length > 0) {
23434 var _refPt = tgtPos;
23435 var _intSqdist = sqdist(_refPt, array2point(intersect));
23436 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23437 var _minSqDist = _intSqdist;
23438 if (_labIntSqdist < _intSqdist) {
23439 intersect = [_labelIntersect[0], _labelIntersect[1]];
23440 _minSqDist = _labIntSqdist;
23441 }
23442 if (_labelIntersect.length > 2) {
23443 var _labInt2SqDist = sqdist(_refPt, {
23444 x: _labelIntersect[2],
23445 y: _labelIntersect[3]
23446 });
23447 if (_labInt2SqDist < _minSqDist) {
23448 intersect = [_labelIntersect[2], _labelIntersect[3]];
23449 }
23450 }
23451 }
23452 }
23453 }
23454 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23455 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23456 rs.startX = edgeStart[0];
23457 rs.startY = edgeStart[1];
23458 rs.arrowStartX = arrowStart[0];
23459 rs.arrowStartY = arrowStart[1];
23460 if (hasEndpts) {
23461 if (!number$1(rs.startX) || !number$1(rs.startY) || !number$1(rs.endX) || !number$1(rs.endY)) {
23462 rs.badLine = true;
23463 } else {
23464 rs.badLine = false;
23465 }
23466 }
23467};
23468BRp$b.getSourceEndpoint = function (edge) {
23469 var rs = edge[0]._private.rscratch;
23470 this.recalculateRenderedStyle(edge);
23471 switch (rs.edgeType) {
23472 case 'haystack':
23473 return {
23474 x: rs.haystackPts[0],
23475 y: rs.haystackPts[1]
23476 };
23477 default:
23478 return {
23479 x: rs.arrowStartX,
23480 y: rs.arrowStartY
23481 };
23482 }
23483};
23484BRp$b.getTargetEndpoint = function (edge) {
23485 var rs = edge[0]._private.rscratch;
23486 this.recalculateRenderedStyle(edge);
23487 switch (rs.edgeType) {
23488 case 'haystack':
23489 return {
23490 x: rs.haystackPts[2],
23491 y: rs.haystackPts[3]
23492 };
23493 default:
23494 return {
23495 x: rs.arrowEndX,
23496 y: rs.arrowEndY
23497 };
23498 }
23499};
23500
23501var BRp$a = {};
23502function pushBezierPts(r, edge, pts) {
23503 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23504 return qbezierAt(p1, p2, p3, t);
23505 };
23506 var _p = edge._private;
23507 var bpts = _p.rstyle.bezierPts;
23508 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23509 var p = r.bezierProjPcts[i];
23510 bpts.push({
23511 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23512 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23513 });
23514 }
23515}
23516BRp$a.storeEdgeProjections = function (edge) {
23517 var _p = edge._private;
23518 var rs = _p.rscratch;
23519 var et = rs.edgeType;
23520
23521 // clear the cached points state
23522 _p.rstyle.bezierPts = null;
23523 _p.rstyle.linePts = null;
23524 _p.rstyle.haystackPts = null;
23525 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23526 _p.rstyle.bezierPts = [];
23527 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23528 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23529 }
23530 } else if (et === 'segments') {
23531 var lpts = _p.rstyle.linePts = [];
23532 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23533 lpts.push({
23534 x: rs.allpts[i],
23535 y: rs.allpts[i + 1]
23536 });
23537 }
23538 } else if (et === 'haystack') {
23539 var hpts = rs.haystackPts;
23540 _p.rstyle.haystackPts = [{
23541 x: hpts[0],
23542 y: hpts[1]
23543 }, {
23544 x: hpts[2],
23545 y: hpts[3]
23546 }];
23547 }
23548 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23549};
23550BRp$a.recalculateEdgeProjections = function (edges) {
23551 this.findEdgeControlPoints(edges);
23552};
23553
23554var BRp$9 = {};
23555BRp$9.recalculateNodeLabelProjection = function (node) {
23556 var content = node.pstyle('label').strValue;
23557 if (emptyString(content)) {
23558 return;
23559 }
23560 var textX, textY;
23561 var _p = node._private;
23562 var nodeWidth = node.width();
23563 var nodeHeight = node.height();
23564 var padding = node.padding();
23565 var nodePos = node.position();
23566 var textHalign = node.pstyle('text-halign').strValue;
23567 var textValign = node.pstyle('text-valign').strValue;
23568 var rs = _p.rscratch;
23569 var rstyle = _p.rstyle;
23570 switch (textHalign) {
23571 case 'left':
23572 textX = nodePos.x - nodeWidth / 2 - padding;
23573 break;
23574 case 'right':
23575 textX = nodePos.x + nodeWidth / 2 + padding;
23576 break;
23577 default:
23578 // e.g. center
23579 textX = nodePos.x;
23580 }
23581 switch (textValign) {
23582 case 'top':
23583 textY = nodePos.y - nodeHeight / 2 - padding;
23584 break;
23585 case 'bottom':
23586 textY = nodePos.y + nodeHeight / 2 + padding;
23587 break;
23588 default:
23589 // e.g. middle
23590 textY = nodePos.y;
23591 }
23592 rs.labelX = textX;
23593 rs.labelY = textY;
23594 rstyle.labelX = textX;
23595 rstyle.labelY = textY;
23596 this.calculateLabelAngles(node);
23597 this.applyLabelDimensions(node);
23598};
23599var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23600 var angle = Math.atan(dy / dx);
23601 if (dx === 0 && angle < 0) {
23602 angle = angle * -1;
23603 }
23604 return angle;
23605};
23606var lineAngle = function lineAngle(p0, p1) {
23607 var dx = p1.x - p0.x;
23608 var dy = p1.y - p0.y;
23609 return lineAngleFromDelta(dx, dy);
23610};
23611var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23612 var t0 = bound(0, t - 0.001, 1);
23613 var t1 = bound(0, t + 0.001, 1);
23614 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23615 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23616 return lineAngle(lp0, lp1);
23617};
23618BRp$9.recalculateEdgeLabelProjections = function (edge) {
23619 var p;
23620 var _p = edge._private;
23621 var rs = _p.rscratch;
23622 var r = this;
23623 var content = {
23624 mid: edge.pstyle('label').strValue,
23625 source: edge.pstyle('source-label').strValue,
23626 target: edge.pstyle('target-label').strValue
23627 };
23628 if (content.mid || content.source || content.target) ; else {
23629 return; // no labels => no calcs
23630 }
23631
23632 // add center point to style so bounding box calculations can use it
23633 //
23634 p = {
23635 x: rs.midX,
23636 y: rs.midY
23637 };
23638 var setRs = function setRs(propName, prefix, value) {
23639 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23640 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23641 };
23642 setRs('labelX', null, p.x);
23643 setRs('labelY', null, p.y);
23644 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23645 setRs('labelAutoAngle', null, midAngle);
23646 var createControlPointInfo = function createControlPointInfo() {
23647 if (createControlPointInfo.cache) {
23648 return createControlPointInfo.cache;
23649 } // use cache so only 1x per edge
23650
23651 var ctrlpts = [];
23652
23653 // store each ctrlpt info init
23654 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23655 var p0 = {
23656 x: rs.allpts[i],
23657 y: rs.allpts[i + 1]
23658 };
23659 var p1 = {
23660 x: rs.allpts[i + 2],
23661 y: rs.allpts[i + 3]
23662 }; // ctrlpt
23663 var p2 = {
23664 x: rs.allpts[i + 4],
23665 y: rs.allpts[i + 5]
23666 };
23667 ctrlpts.push({
23668 p0: p0,
23669 p1: p1,
23670 p2: p2,
23671 startDist: 0,
23672 length: 0,
23673 segments: []
23674 });
23675 }
23676 var bpts = _p.rstyle.bezierPts;
23677 var nProjs = r.bezierProjPcts.length;
23678 function addSegment(cp, p0, p1, t0, t1) {
23679 var length = dist(p0, p1);
23680 var prevSegment = cp.segments[cp.segments.length - 1];
23681 var segment = {
23682 p0: p0,
23683 p1: p1,
23684 t0: t0,
23685 t1: t1,
23686 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23687 length: length
23688 };
23689 cp.segments.push(segment);
23690 cp.length += length;
23691 }
23692
23693 // update each ctrlpt with segment info
23694 for (var _i = 0; _i < ctrlpts.length; _i++) {
23695 var cp = ctrlpts[_i];
23696 var prevCp = ctrlpts[_i - 1];
23697 if (prevCp) {
23698 cp.startDist = prevCp.startDist + prevCp.length;
23699 }
23700 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23701
23702 for (var j = 0; j < nProjs - 1; j++) {
23703 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23704 }
23705 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23706 }
23707
23708 return createControlPointInfo.cache = ctrlpts;
23709 };
23710 var calculateEndProjection = function calculateEndProjection(prefix) {
23711 var angle;
23712 var isSrc = prefix === 'source';
23713 if (!content[prefix]) {
23714 return;
23715 }
23716 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23717 switch (rs.edgeType) {
23718 case 'self':
23719 case 'compound':
23720 case 'bezier':
23721 case 'multibezier':
23722 {
23723 var cps = createControlPointInfo();
23724 var selected;
23725 var startDist = 0;
23726 var totalDist = 0;
23727
23728 // find the segment we're on
23729 for (var i = 0; i < cps.length; i++) {
23730 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23731 for (var j = 0; j < _cp.segments.length; j++) {
23732 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23733 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23734 startDist = totalDist;
23735 totalDist += _seg.length;
23736 if (totalDist >= offset || lastSeg) {
23737 selected = {
23738 cp: _cp,
23739 segment: _seg
23740 };
23741 break;
23742 }
23743 }
23744 if (selected) {
23745 break;
23746 }
23747 }
23748 var cp = selected.cp;
23749 var seg = selected.segment;
23750 var tSegment = (offset - startDist) / seg.length;
23751 var segDt = seg.t1 - seg.t0;
23752 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23753 t = bound(0, t, 1);
23754 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23755 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23756 break;
23757 }
23758 case 'straight':
23759 case 'segments':
23760 case 'haystack':
23761 {
23762 var d = 0,
23763 di,
23764 d0;
23765 var p0, p1;
23766 var l = rs.allpts.length;
23767 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23768 if (isSrc) {
23769 p0 = {
23770 x: rs.allpts[_i2],
23771 y: rs.allpts[_i2 + 1]
23772 };
23773 p1 = {
23774 x: rs.allpts[_i2 + 2],
23775 y: rs.allpts[_i2 + 3]
23776 };
23777 } else {
23778 p0 = {
23779 x: rs.allpts[l - 2 - _i2],
23780 y: rs.allpts[l - 1 - _i2]
23781 };
23782 p1 = {
23783 x: rs.allpts[l - 4 - _i2],
23784 y: rs.allpts[l - 3 - _i2]
23785 };
23786 }
23787 di = dist(p0, p1);
23788 d0 = d;
23789 d += di;
23790 if (d >= offset) {
23791 break;
23792 }
23793 }
23794 var pD = offset - d0;
23795 var _t = pD / di;
23796 _t = bound(0, _t, 1);
23797 p = lineAt(p0, p1, _t);
23798 angle = lineAngle(p0, p1);
23799 break;
23800 }
23801 }
23802 setRs('labelX', prefix, p.x);
23803 setRs('labelY', prefix, p.y);
23804 setRs('labelAutoAngle', prefix, angle);
23805 };
23806 calculateEndProjection('source');
23807 calculateEndProjection('target');
23808 this.applyLabelDimensions(edge);
23809};
23810BRp$9.applyLabelDimensions = function (ele) {
23811 this.applyPrefixedLabelDimensions(ele);
23812 if (ele.isEdge()) {
23813 this.applyPrefixedLabelDimensions(ele, 'source');
23814 this.applyPrefixedLabelDimensions(ele, 'target');
23815 }
23816};
23817BRp$9.applyPrefixedLabelDimensions = function (ele, prefix) {
23818 var _p = ele._private;
23819 var text = this.getLabelText(ele, prefix);
23820 var labelDims = this.calculateLabelDimensions(ele, text);
23821 var lineHeight = ele.pstyle('line-height').pfValue;
23822 var textWrap = ele.pstyle('text-wrap').strValue;
23823 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23824 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23825 var normPerLineHeight = labelDims.height / numLines;
23826 var labelLineHeight = normPerLineHeight * lineHeight;
23827 var width = labelDims.width;
23828 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23829 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23830 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23831 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23832 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23833 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23834};
23835BRp$9.getLabelText = function (ele, prefix) {
23836 var _p = ele._private;
23837 var pfd = prefix ? prefix + '-' : '';
23838 var text = ele.pstyle(pfd + 'label').strValue;
23839 var textTransform = ele.pstyle('text-transform').value;
23840 var rscratch = function rscratch(propName, value) {
23841 if (value) {
23842 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23843 return value;
23844 } else {
23845 return getPrefixedProperty(_p.rscratch, propName, prefix);
23846 }
23847 };
23848
23849 // for empty text, skip all processing
23850 if (!text) {
23851 return '';
23852 }
23853 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23854 text = text.toUpperCase();
23855 } else if (textTransform == 'lowercase') {
23856 text = text.toLowerCase();
23857 }
23858 var wrapStyle = ele.pstyle('text-wrap').value;
23859 if (wrapStyle === 'wrap') {
23860 var labelKey = rscratch('labelKey');
23861
23862 // save recalc if the label is the same as before
23863 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23864 return rscratch('labelWrapCachedText');
23865 }
23866 var zwsp = "\u200B";
23867 var lines = text.split('\n');
23868 var maxW = ele.pstyle('text-max-width').pfValue;
23869 var overflow = ele.pstyle('text-overflow-wrap').value;
23870 var overflowAny = overflow === 'anywhere';
23871 var wrappedLines = [];
23872 var separatorRegex = /[\s\u200b]+|$/g; // Include end of string to add last word
23873
23874 for (var l = 0; l < lines.length; l++) {
23875 var line = lines[l];
23876 var lineDims = this.calculateLabelDimensions(ele, line);
23877 var lineW = lineDims.width;
23878 if (overflowAny) {
23879 var processedLine = line.split('').join(zwsp);
23880 line = processedLine;
23881 }
23882 if (lineW > maxW) {
23883 // line is too long
23884 var separatorMatches = line.matchAll(separatorRegex);
23885 var subline = '';
23886 var previousIndex = 0;
23887 // Add fake match
23888 var _iterator = _createForOfIteratorHelper(separatorMatches),
23889 _step;
23890 try {
23891 for (_iterator.s(); !(_step = _iterator.n()).done;) {
23892 var separatorMatch = _step.value;
23893 var wordSeparator = separatorMatch[0];
23894 var word = line.substring(previousIndex, separatorMatch.index);
23895 previousIndex = separatorMatch.index + wordSeparator.length;
23896 var testLine = subline.length === 0 ? word : subline + word + wordSeparator;
23897 var testDims = this.calculateLabelDimensions(ele, testLine);
23898 var testW = testDims.width;
23899 if (testW <= maxW) {
23900 // word fits on current line
23901 subline += word + wordSeparator;
23902 } else {
23903 // word starts new line
23904 if (subline) {
23905 wrappedLines.push(subline);
23906 }
23907 subline = word + wordSeparator;
23908 }
23909 }
23910
23911 // if there's remaining text, put it in a wrapped line
23912 } catch (err) {
23913 _iterator.e(err);
23914 } finally {
23915 _iterator.f();
23916 }
23917 if (!subline.match(/^[\s\u200b]+$/)) {
23918 wrappedLines.push(subline);
23919 }
23920 } else {
23921 // line is already short enough
23922 wrappedLines.push(line);
23923 }
23924 } // for
23925
23926 rscratch('labelWrapCachedLines', wrappedLines);
23927 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23928 rscratch('labelWrapKey', labelKey);
23929 } else if (wrapStyle === 'ellipsis') {
23930 var _maxW = ele.pstyle('text-max-width').pfValue;
23931 var ellipsized = '';
23932 var ellipsis = "\u2026";
23933 var incLastCh = false;
23934 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23935 // the label already fits
23936 return text;
23937 }
23938 for (var i = 0; i < text.length; i++) {
23939 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23940 if (widthWithNextCh > _maxW) {
23941 break;
23942 }
23943 ellipsized += text[i];
23944 if (i === text.length - 1) {
23945 incLastCh = true;
23946 }
23947 }
23948 if (!incLastCh) {
23949 ellipsized += ellipsis;
23950 }
23951 return ellipsized;
23952 } // if ellipsize
23953
23954 return text;
23955};
23956BRp$9.getLabelJustification = function (ele) {
23957 var justification = ele.pstyle('text-justification').strValue;
23958 var textHalign = ele.pstyle('text-halign').strValue;
23959 if (justification === 'auto') {
23960 if (ele.isNode()) {
23961 switch (textHalign) {
23962 case 'left':
23963 return 'right';
23964 case 'right':
23965 return 'left';
23966 default:
23967 return 'center';
23968 }
23969 } else {
23970 return 'center';
23971 }
23972 } else {
23973 return justification;
23974 }
23975};
23976BRp$9.calculateLabelDimensions = function (ele, text) {
23977 var r = this;
23978 var containerWindow = r.cy.window();
23979 var document = containerWindow.document;
23980 var cacheKey = hashString(text, ele._private.labelDimsKey);
23981 var cache = r.labelDimCache || (r.labelDimCache = []);
23982 var existingVal = cache[cacheKey];
23983 if (existingVal != null) {
23984 return existingVal;
23985 }
23986 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23987 var fStyle = ele.pstyle('font-style').strValue;
23988 var size = ele.pstyle('font-size').pfValue;
23989 var family = ele.pstyle('font-family').strValue;
23990 var weight = ele.pstyle('font-weight').strValue;
23991 var canvas = this.labelCalcCanvas;
23992 var c2d = this.labelCalcCanvasContext;
23993 if (!canvas) {
23994 canvas = this.labelCalcCanvas = document.createElement('canvas');
23995 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23996 var ds = canvas.style;
23997 ds.position = 'absolute';
23998 ds.left = '-9999px';
23999 ds.top = '-9999px';
24000 ds.zIndex = '-1';
24001 ds.visibility = 'hidden';
24002 ds.pointerEvents = 'none';
24003 }
24004 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
24005 var width = 0;
24006 var height = 0;
24007 var lines = text.split('\n');
24008 for (var i = 0; i < lines.length; i++) {
24009 var line = lines[i];
24010 var metrics = c2d.measureText(line);
24011 var w = Math.ceil(metrics.width);
24012 var h = size;
24013 width = Math.max(w, width);
24014 height += h;
24015 }
24016 width += padding;
24017 height += padding;
24018 return cache[cacheKey] = {
24019 width: width,
24020 height: height
24021 };
24022};
24023BRp$9.calculateLabelAngle = function (ele, prefix) {
24024 var _p = ele._private;
24025 var rs = _p.rscratch;
24026 var isEdge = ele.isEdge();
24027 var prefixDash = prefix ? prefix + '-' : '';
24028 var rot = ele.pstyle(prefixDash + 'text-rotation');
24029 var rotStr = rot.strValue;
24030 if (rotStr === 'none') {
24031 return 0;
24032 } else if (isEdge && rotStr === 'autorotate') {
24033 return rs.labelAutoAngle;
24034 } else if (rotStr === 'autorotate') {
24035 return 0;
24036 } else {
24037 return rot.pfValue;
24038 }
24039};
24040BRp$9.calculateLabelAngles = function (ele) {
24041 var r = this;
24042 var isEdge = ele.isEdge();
24043 var _p = ele._private;
24044 var rs = _p.rscratch;
24045 rs.labelAngle = r.calculateLabelAngle(ele);
24046 if (isEdge) {
24047 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
24048 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
24049 }
24050};
24051
24052var BRp$8 = {};
24053var TOO_SMALL_CUT_RECT = 28;
24054var warnedCutRect = false;
24055BRp$8.getNodeShape = function (node) {
24056 var r = this;
24057 var shape = node.pstyle('shape').value;
24058 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
24059 if (!warnedCutRect) {
24060 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
24061 warnedCutRect = true;
24062 }
24063 return 'rectangle';
24064 }
24065 if (node.isParent()) {
24066 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
24067 return shape;
24068 } else {
24069 return 'rectangle';
24070 }
24071 }
24072 if (shape === 'polygon') {
24073 var points = node.pstyle('shape-polygon-points').value;
24074 return r.nodeShapes.makePolygon(points).name;
24075 }
24076 return shape;
24077};
24078
24079var BRp$7 = {};
24080BRp$7.registerCalculationListeners = function () {
24081 var cy = this.cy;
24082 var elesToUpdate = cy.collection();
24083 var r = this;
24084 var enqueue = function enqueue(eles) {
24085 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
24086 elesToUpdate.merge(eles);
24087 if (dirtyStyleCaches) {
24088 for (var i = 0; i < eles.length; i++) {
24089 var ele = eles[i];
24090 var _p = ele._private;
24091 var rstyle = _p.rstyle;
24092 rstyle.clean = false;
24093 rstyle.cleanConnected = false;
24094 }
24095 }
24096 };
24097 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24098 var ele = e.target;
24099 enqueue(ele);
24100 }).on('style.* background.*', function onDirtyStyle(e) {
24101 var ele = e.target;
24102 enqueue(ele, false);
24103 });
24104 var updateEleCalcs = function updateEleCalcs(willDraw) {
24105 if (willDraw) {
24106 var fns = r.onUpdateEleCalcsFns;
24107
24108 // because we need to have up-to-date style (e.g. stylesheet mappers)
24109 // before calculating rendered style (and pstyle might not be called yet)
24110 elesToUpdate.cleanStyle();
24111 for (var i = 0; i < elesToUpdate.length; i++) {
24112 var ele = elesToUpdate[i];
24113 var rstyle = ele._private.rstyle;
24114 if (ele.isNode() && !rstyle.cleanConnected) {
24115 enqueue(ele.connectedEdges());
24116 rstyle.cleanConnected = true;
24117 }
24118 }
24119 if (fns) {
24120 for (var _i = 0; _i < fns.length; _i++) {
24121 var fn = fns[_i];
24122 fn(willDraw, elesToUpdate);
24123 }
24124 }
24125 r.recalculateRenderedStyle(elesToUpdate);
24126 elesToUpdate = cy.collection();
24127 }
24128 };
24129 r.flushRenderedStyleQueue = function () {
24130 updateEleCalcs(true);
24131 };
24132 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24133};
24134BRp$7.onUpdateEleCalcs = function (fn) {
24135 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24136 fns.push(fn);
24137};
24138BRp$7.recalculateRenderedStyle = function (eles, useCache) {
24139 var isCleanConnected = function isCleanConnected(ele) {
24140 return ele._private.rstyle.cleanConnected;
24141 };
24142 var edges = [];
24143 var nodes = [];
24144
24145 // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24146 if (this.destroyed) {
24147 return;
24148 }
24149
24150 // use cache by default for perf
24151 if (useCache === undefined) {
24152 useCache = true;
24153 }
24154 for (var i = 0; i < eles.length; i++) {
24155 var ele = eles[i];
24156 var _p = ele._private;
24157 var rstyle = _p.rstyle;
24158
24159 // an edge may be implicitly dirty b/c of one of its connected nodes
24160 // (and a request for recalc may come in between frames)
24161 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24162 rstyle.clean = false;
24163 }
24164
24165 // only update if dirty and in graph
24166 if (useCache && rstyle.clean || ele.removed()) {
24167 continue;
24168 }
24169
24170 // only update if not display: none
24171 if (ele.pstyle('display').value === 'none') {
24172 continue;
24173 }
24174 if (_p.group === 'nodes') {
24175 nodes.push(ele);
24176 } else {
24177 // edges
24178 edges.push(ele);
24179 }
24180 rstyle.clean = true;
24181 }
24182
24183 // update node data from projections
24184 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24185 var _ele = nodes[_i2];
24186 var _p2 = _ele._private;
24187 var _rstyle = _p2.rstyle;
24188 var pos = _ele.position();
24189 this.recalculateNodeLabelProjection(_ele);
24190 _rstyle.nodeX = pos.x;
24191 _rstyle.nodeY = pos.y;
24192 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24193 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24194 }
24195 this.recalculateEdgeProjections(edges);
24196
24197 // update edge data from projections
24198 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24199 var _ele2 = edges[_i3];
24200 var _p3 = _ele2._private;
24201 var _rstyle2 = _p3.rstyle;
24202 var rs = _p3.rscratch;
24203
24204 // update rstyle positions
24205 _rstyle2.srcX = rs.arrowStartX;
24206 _rstyle2.srcY = rs.arrowStartY;
24207 _rstyle2.tgtX = rs.arrowEndX;
24208 _rstyle2.tgtY = rs.arrowEndY;
24209 _rstyle2.midX = rs.midX;
24210 _rstyle2.midY = rs.midY;
24211 _rstyle2.labelAngle = rs.labelAngle;
24212 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24213 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24214 }
24215};
24216
24217var BRp$6 = {};
24218BRp$6.updateCachedGrabbedEles = function () {
24219 var eles = this.cachedZSortedEles;
24220 if (!eles) {
24221 // just let this be recalculated on the next z sort tick
24222 return;
24223 }
24224 eles.drag = [];
24225 eles.nondrag = [];
24226 var grabTargets = [];
24227 for (var i = 0; i < eles.length; i++) {
24228 var ele = eles[i];
24229 var rs = ele._private.rscratch;
24230 if (ele.grabbed() && !ele.isParent()) {
24231 grabTargets.push(ele);
24232 } else if (rs.inDragLayer) {
24233 eles.drag.push(ele);
24234 } else {
24235 eles.nondrag.push(ele);
24236 }
24237 }
24238
24239 // put the grab target nodes last so it's on top of its neighbourhood
24240 for (var i = 0; i < grabTargets.length; i++) {
24241 var ele = grabTargets[i];
24242 eles.drag.push(ele);
24243 }
24244};
24245BRp$6.invalidateCachedZSortedEles = function () {
24246 this.cachedZSortedEles = null;
24247};
24248BRp$6.getCachedZSortedEles = function (forceRecalc) {
24249 if (forceRecalc || !this.cachedZSortedEles) {
24250 var eles = this.cy.mutableElements().toArray();
24251 eles.sort(zIndexSort);
24252 eles.interactive = eles.filter(function (ele) {
24253 return ele.interactive();
24254 });
24255 this.cachedZSortedEles = eles;
24256 this.updateCachedGrabbedEles();
24257 } else {
24258 eles = this.cachedZSortedEles;
24259 }
24260 return eles;
24261};
24262
24263var BRp$5 = {};
24264[BRp$e, BRp$d, BRp$c, BRp$b, BRp$a, BRp$9, BRp$8, BRp$7, BRp$6].forEach(function (props) {
24265 extend(BRp$5, props);
24266});
24267
24268var BRp$4 = {};
24269BRp$4.getCachedImage = function (url, crossOrigin, onLoad) {
24270 var r = this;
24271 var imageCache = r.imageCache = r.imageCache || {};
24272 var cache = imageCache[url];
24273 if (cache) {
24274 if (!cache.image.complete) {
24275 cache.image.addEventListener('load', onLoad);
24276 }
24277 return cache.image;
24278 } else {
24279 cache = imageCache[url] = imageCache[url] || {};
24280 var image = cache.image = new Image(); // eslint-disable-line no-undef
24281
24282 image.addEventListener('load', onLoad);
24283 image.addEventListener('error', function () {
24284 image.error = true;
24285 });
24286
24287 // #1582 safari doesn't load data uris with crossOrigin properly
24288 // https://bugs.webkit.org/show_bug.cgi?id=123978
24289 var dataUriPrefix = 'data:';
24290 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24291 if (!isDataUri) {
24292 // if crossorigin is 'null'(stringified), then manually set it to null
24293 crossOrigin = crossOrigin === 'null' ? null : crossOrigin;
24294 image.crossOrigin = crossOrigin; // prevent tainted canvas
24295 }
24296
24297 image.src = url;
24298 return image;
24299 }
24300};
24301
24302var BRp$3 = {};
24303
24304/* global document, ResizeObserver, MutationObserver */
24305
24306BRp$3.registerBinding = function (target, event, handler, useCapture) {
24307 // eslint-disable-line no-unused-vars
24308 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24309 var b = this.binder(target);
24310 return b.on.apply(b, args);
24311};
24312BRp$3.binder = function (tgt) {
24313 var r = this;
24314 var containerWindow = r.cy.window();
24315 var tgtIsDom = tgt === containerWindow || tgt === containerWindow.document || tgt === containerWindow.document.body || domElement(tgt);
24316 if (r.supportsPassiveEvents == null) {
24317 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24318 var supportsPassive = false;
24319 try {
24320 var opts = Object.defineProperty({}, 'passive', {
24321 get: function get() {
24322 supportsPassive = true;
24323 return true;
24324 }
24325 });
24326 containerWindow.addEventListener('test', null, opts);
24327 } catch (err) {
24328 // not supported
24329 }
24330 r.supportsPassiveEvents = supportsPassive;
24331 }
24332 var on = function on(event, handler, useCapture) {
24333 var args = Array.prototype.slice.call(arguments);
24334 if (tgtIsDom && r.supportsPassiveEvents) {
24335 // replace useCapture w/ opts obj
24336 args[2] = {
24337 capture: useCapture != null ? useCapture : false,
24338 passive: false,
24339 once: false
24340 };
24341 }
24342 r.bindings.push({
24343 target: tgt,
24344 args: args
24345 });
24346 (tgt.addEventListener || tgt.on).apply(tgt, args);
24347 return this;
24348 };
24349 return {
24350 on: on,
24351 addEventListener: on,
24352 addListener: on,
24353 bind: on
24354 };
24355};
24356BRp$3.nodeIsDraggable = function (node) {
24357 return node && node.isNode() && !node.locked() && node.grabbable();
24358};
24359BRp$3.nodeIsGrabbable = function (node) {
24360 return this.nodeIsDraggable(node) && node.interactive();
24361};
24362BRp$3.load = function () {
24363 var r = this;
24364 var containerWindow = r.cy.window();
24365 var isSelected = function isSelected(ele) {
24366 return ele.selected();
24367 };
24368 var triggerEvents = function triggerEvents(target, names, e, position) {
24369 if (target == null) {
24370 target = r.cy;
24371 }
24372 for (var i = 0; i < names.length; i++) {
24373 var name = names[i];
24374 target.emit({
24375 originalEvent: e,
24376 type: name,
24377 position: position
24378 });
24379 }
24380 };
24381 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24382 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24383 };
24384
24385 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24386 var allowPassthrough = true;
24387 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24388 // a grabbable compound node below the ele => no passthrough panning
24389 for (var i = 0; downs && i < downs.length; i++) {
24390 var down = downs[i];
24391
24392 //if any parent node in event hierarchy isn't pannable, reject passthrough
24393 if (down.isNode() && down.isParent() && !down.pannable()) {
24394 allowPassthrough = false;
24395 break;
24396 }
24397 }
24398 } else {
24399 allowPassthrough = true;
24400 }
24401 return allowPassthrough;
24402 };
24403 var setGrabbed = function setGrabbed(ele) {
24404 ele[0]._private.grabbed = true;
24405 };
24406 var setFreed = function setFreed(ele) {
24407 ele[0]._private.grabbed = false;
24408 };
24409 var setInDragLayer = function setInDragLayer(ele) {
24410 ele[0]._private.rscratch.inDragLayer = true;
24411 };
24412 var setOutDragLayer = function setOutDragLayer(ele) {
24413 ele[0]._private.rscratch.inDragLayer = false;
24414 };
24415 var setGrabTarget = function setGrabTarget(ele) {
24416 ele[0]._private.rscratch.isGrabTarget = true;
24417 };
24418 var removeGrabTarget = function removeGrabTarget(ele) {
24419 ele[0]._private.rscratch.isGrabTarget = false;
24420 };
24421 var addToDragList = function addToDragList(ele, opts) {
24422 var list = opts.addToList;
24423 var listHasEle = list.has(ele);
24424 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24425 list.merge(ele);
24426 setGrabbed(ele);
24427 }
24428 };
24429
24430 // helper function to determine which child nodes and inner edges
24431 // of a compound node to be dragged as well as the grabbed and selected nodes
24432 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24433 if (!node.cy().hasCompoundNodes()) {
24434 return;
24435 }
24436 if (opts.inDragLayer == null && opts.addToList == null) {
24437 return;
24438 } // nothing to do
24439
24440 var innerNodes = node.descendants();
24441 if (opts.inDragLayer) {
24442 innerNodes.forEach(setInDragLayer);
24443 innerNodes.connectedEdges().forEach(setInDragLayer);
24444 }
24445 if (opts.addToList) {
24446 addToDragList(innerNodes, opts);
24447 }
24448 };
24449
24450 // adds the given nodes and its neighbourhood to the drag layer
24451 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24452 opts = opts || {};
24453 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24454 if (opts.inDragLayer) {
24455 nodes.forEach(setInDragLayer);
24456 nodes.neighborhood().stdFilter(function (ele) {
24457 return !hasCompoundNodes || ele.isEdge();
24458 }).forEach(setInDragLayer);
24459 }
24460 if (opts.addToList) {
24461 nodes.forEach(function (ele) {
24462 addToDragList(ele, opts);
24463 });
24464 }
24465 addDescendantsToDrag(nodes, opts); // always add to drag
24466
24467 // also add nodes and edges related to the topmost ancestor
24468 updateAncestorsInDragLayer(nodes, {
24469 inDragLayer: opts.inDragLayer
24470 });
24471 r.updateCachedGrabbedEles();
24472 };
24473 var addNodeToDrag = addNodesToDrag;
24474 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24475 if (!grabbedEles) {
24476 return;
24477 }
24478
24479 // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24480 r.getCachedZSortedEles().forEach(function (ele) {
24481 setFreed(ele);
24482 setOutDragLayer(ele);
24483 removeGrabTarget(ele);
24484 });
24485 r.updateCachedGrabbedEles();
24486 };
24487
24488 // helper function to determine which ancestor nodes and edges should go
24489 // to the drag layer (or should be removed from drag layer).
24490 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24491 if (opts.inDragLayer == null && opts.addToList == null) {
24492 return;
24493 } // nothing to do
24494
24495 if (!node.cy().hasCompoundNodes()) {
24496 return;
24497 }
24498
24499 // find top-level parent
24500 var parent = node.ancestors().orphans();
24501
24502 // no parent node: no nodes to add to the drag layer
24503 if (parent.same(node)) {
24504 return;
24505 }
24506 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24507 var edges = nodes.connectedEdges();
24508 if (opts.inDragLayer) {
24509 edges.forEach(setInDragLayer);
24510 nodes.forEach(setInDragLayer);
24511 }
24512 if (opts.addToList) {
24513 nodes.forEach(function (ele) {
24514 addToDragList(ele, opts);
24515 });
24516 }
24517 };
24518 var blurActiveDomElement = function blurActiveDomElement() {
24519 if (document.activeElement != null && document.activeElement.blur != null) {
24520 document.activeElement.blur();
24521 }
24522 };
24523 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24524 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined';
24525
24526 // watch for when the cy container is removed from the dom
24527 if (haveMutationsApi) {
24528 r.removeObserver = new MutationObserver(function (mutns) {
24529 // eslint-disable-line no-undef
24530 for (var i = 0; i < mutns.length; i++) {
24531 var mutn = mutns[i];
24532 var rNodes = mutn.removedNodes;
24533 if (rNodes) {
24534 for (var j = 0; j < rNodes.length; j++) {
24535 var rNode = rNodes[j];
24536 if (rNode === r.container) {
24537 r.destroy();
24538 break;
24539 }
24540 }
24541 }
24542 }
24543 });
24544 if (r.container.parentNode) {
24545 r.removeObserver.observe(r.container.parentNode, {
24546 childList: true
24547 });
24548 }
24549 } else {
24550 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24551 // eslint-disable-line no-unused-vars
24552 r.destroy();
24553 });
24554 }
24555 var onResize = debounce_1(function () {
24556 r.cy.resize();
24557 }, 100);
24558 if (haveMutationsApi) {
24559 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24560
24561 r.styleObserver.observe(r.container, {
24562 attributes: true
24563 });
24564 }
24565
24566 // auto resize
24567 r.registerBinding(containerWindow, 'resize', onResize); // eslint-disable-line no-undef
24568
24569 if (haveResizeObserverApi) {
24570 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24571
24572 r.resizeObserver.observe(r.container);
24573 }
24574 var forEachUp = function forEachUp(domEle, fn) {
24575 while (domEle != null) {
24576 fn(domEle);
24577 domEle = domEle.parentNode;
24578 }
24579 };
24580 var invalidateCoords = function invalidateCoords() {
24581 r.invalidateContainerClientCoordsCache();
24582 };
24583 forEachUp(r.container, function (domEle) {
24584 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24585 r.registerBinding(domEle, 'animationend', invalidateCoords);
24586 r.registerBinding(domEle, 'scroll', invalidateCoords);
24587 });
24588
24589 // stop right click menu from appearing on cy
24590 r.registerBinding(r.container, 'contextmenu', function (e) {
24591 e.preventDefault();
24592 });
24593 var inBoxSelection = function inBoxSelection() {
24594 return r.selection[4] !== 0;
24595 };
24596 var eventInContainer = function eventInContainer(e) {
24597 // save cycles if mouse events aren't to be captured
24598 var containerPageCoords = r.findContainerClientCoords();
24599 var x = containerPageCoords[0];
24600 var y = containerPageCoords[1];
24601 var width = containerPageCoords[2];
24602 var height = containerPageCoords[3];
24603 var positions = e.touches ? e.touches : [e];
24604 var atLeastOnePosInside = false;
24605 for (var i = 0; i < positions.length; i++) {
24606 var p = positions[i];
24607 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24608 atLeastOnePosInside = true;
24609 break;
24610 }
24611 }
24612 if (!atLeastOnePosInside) {
24613 return false;
24614 }
24615 var container = r.container;
24616 var target = e.target;
24617 var tParent = target.parentNode;
24618 var containerIsTarget = false;
24619 while (tParent) {
24620 if (tParent === container) {
24621 containerIsTarget = true;
24622 break;
24623 }
24624 tParent = tParent.parentNode;
24625 }
24626 if (!containerIsTarget) {
24627 return false;
24628 } // if target is outisde cy container, then this event is not for us
24629
24630 return true;
24631 };
24632
24633 // Primary key
24634 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24635 if (!eventInContainer(e)) {
24636 return;
24637 }
24638
24639 // during left mouse button gestures, ignore other buttons
24640 if (r.hoverData.which === 1 && e.which !== 1) {
24641 return;
24642 }
24643 e.preventDefault();
24644 blurActiveDomElement();
24645 r.hoverData.capture = true;
24646 r.hoverData.which = e.which;
24647 var cy = r.cy;
24648 var gpos = [e.clientX, e.clientY];
24649 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24650 var select = r.selection;
24651 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24652 var near = nears[0];
24653 var draggedElements = r.dragData.possibleDragElements;
24654 r.hoverData.mdownPos = pos;
24655 r.hoverData.mdownGPos = gpos;
24656 var checkForTaphold = function checkForTaphold() {
24657 r.hoverData.tapholdCancelled = false;
24658 clearTimeout(r.hoverData.tapholdTimeout);
24659 r.hoverData.tapholdTimeout = setTimeout(function () {
24660 if (r.hoverData.tapholdCancelled) {
24661 return;
24662 } else {
24663 var ele = r.hoverData.down;
24664 if (ele) {
24665 ele.emit({
24666 originalEvent: e,
24667 type: 'taphold',
24668 position: {
24669 x: pos[0],
24670 y: pos[1]
24671 }
24672 });
24673 } else {
24674 cy.emit({
24675 originalEvent: e,
24676 type: 'taphold',
24677 position: {
24678 x: pos[0],
24679 y: pos[1]
24680 }
24681 });
24682 }
24683 }
24684 }, r.tapholdDuration);
24685 };
24686
24687 // Right click button
24688 if (e.which == 3) {
24689 r.hoverData.cxtStarted = true;
24690 var cxtEvt = {
24691 originalEvent: e,
24692 type: 'cxttapstart',
24693 position: {
24694 x: pos[0],
24695 y: pos[1]
24696 }
24697 };
24698 if (near) {
24699 near.activate();
24700 near.emit(cxtEvt);
24701 r.hoverData.down = near;
24702 } else {
24703 cy.emit(cxtEvt);
24704 }
24705 r.hoverData.downTime = new Date().getTime();
24706 r.hoverData.cxtDragged = false;
24707
24708 // Primary button
24709 } else if (e.which == 1) {
24710 if (near) {
24711 near.activate();
24712 }
24713
24714 // Element dragging
24715 {
24716 // If something is under the cursor and it is draggable, prepare to grab it
24717 if (near != null) {
24718 if (r.nodeIsGrabbable(near)) {
24719 var makeEvent = function makeEvent(type) {
24720 return {
24721 originalEvent: e,
24722 type: type,
24723 position: {
24724 x: pos[0],
24725 y: pos[1]
24726 }
24727 };
24728 };
24729 var triggerGrab = function triggerGrab(ele) {
24730 ele.emit(makeEvent('grab'));
24731 };
24732 setGrabTarget(near);
24733 if (!near.selected()) {
24734 draggedElements = r.dragData.possibleDragElements = cy.collection();
24735 addNodeToDrag(near, {
24736 addToList: draggedElements
24737 });
24738 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24739 } else {
24740 draggedElements = r.dragData.possibleDragElements = cy.collection();
24741 var selectedNodes = cy.$(function (ele) {
24742 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24743 });
24744 addNodesToDrag(selectedNodes, {
24745 addToList: draggedElements
24746 });
24747 near.emit(makeEvent('grabon'));
24748 selectedNodes.forEach(triggerGrab);
24749 }
24750 r.redrawHint('eles', true);
24751 r.redrawHint('drag', true);
24752 }
24753 }
24754 r.hoverData.down = near;
24755 r.hoverData.downs = nears;
24756 r.hoverData.downTime = new Date().getTime();
24757 }
24758 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24759 x: pos[0],
24760 y: pos[1]
24761 });
24762 if (near == null) {
24763 select[4] = 1;
24764 r.data.bgActivePosistion = {
24765 x: pos[0],
24766 y: pos[1]
24767 };
24768 r.redrawHint('select', true);
24769 r.redraw();
24770 } else if (near.pannable()) {
24771 select[4] = 1; // for future pan
24772 }
24773
24774 checkForTaphold();
24775 }
24776
24777 // Initialize selection box coordinates
24778 select[0] = select[2] = pos[0];
24779 select[1] = select[3] = pos[1];
24780 }, false);
24781 r.registerBinding(containerWindow, 'mousemove', function mousemoveHandler(e) {
24782 // eslint-disable-line no-undef
24783 var capture = r.hoverData.capture;
24784 if (!capture && !eventInContainer(e)) {
24785 return;
24786 }
24787 var preventDefault = false;
24788 var cy = r.cy;
24789 var zoom = cy.zoom();
24790 var gpos = [e.clientX, e.clientY];
24791 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24792 var mdownPos = r.hoverData.mdownPos;
24793 var mdownGPos = r.hoverData.mdownGPos;
24794 var select = r.selection;
24795 var near = null;
24796 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24797 near = r.findNearestElement(pos[0], pos[1], true, false);
24798 }
24799 var last = r.hoverData.last;
24800 var down = r.hoverData.down;
24801 var disp = [pos[0] - select[2], pos[1] - select[3]];
24802 var draggedElements = r.dragData.possibleDragElements;
24803 var isOverThresholdDrag;
24804 if (mdownGPos) {
24805 var dx = gpos[0] - mdownGPos[0];
24806 var dx2 = dx * dx;
24807 var dy = gpos[1] - mdownGPos[1];
24808 var dy2 = dy * dy;
24809 var dist2 = dx2 + dy2;
24810 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24811 }
24812 var multSelKeyDown = isMultSelKeyDown(e);
24813 if (isOverThresholdDrag) {
24814 r.hoverData.tapholdCancelled = true;
24815 }
24816 var updateDragDelta = function updateDragDelta() {
24817 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24818 if (dragDelta.length === 0) {
24819 dragDelta.push(disp[0]);
24820 dragDelta.push(disp[1]);
24821 } else {
24822 dragDelta[0] += disp[0];
24823 dragDelta[1] += disp[1];
24824 }
24825 };
24826 preventDefault = true;
24827 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24828 x: pos[0],
24829 y: pos[1]
24830 });
24831 var goIntoBoxMode = function goIntoBoxMode() {
24832 r.data.bgActivePosistion = undefined;
24833 if (!r.hoverData.selecting) {
24834 cy.emit({
24835 originalEvent: e,
24836 type: 'boxstart',
24837 position: {
24838 x: pos[0],
24839 y: pos[1]
24840 }
24841 });
24842 }
24843 select[4] = 1;
24844 r.hoverData.selecting = true;
24845 r.redrawHint('select', true);
24846 r.redraw();
24847 };
24848
24849 // trigger context drag if rmouse down
24850 if (r.hoverData.which === 3) {
24851 // but only if over threshold
24852 if (isOverThresholdDrag) {
24853 var cxtEvt = {
24854 originalEvent: e,
24855 type: 'cxtdrag',
24856 position: {
24857 x: pos[0],
24858 y: pos[1]
24859 }
24860 };
24861 if (down) {
24862 down.emit(cxtEvt);
24863 } else {
24864 cy.emit(cxtEvt);
24865 }
24866 r.hoverData.cxtDragged = true;
24867 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24868 if (r.hoverData.cxtOver) {
24869 r.hoverData.cxtOver.emit({
24870 originalEvent: e,
24871 type: 'cxtdragout',
24872 position: {
24873 x: pos[0],
24874 y: pos[1]
24875 }
24876 });
24877 }
24878 r.hoverData.cxtOver = near;
24879 if (near) {
24880 near.emit({
24881 originalEvent: e,
24882 type: 'cxtdragover',
24883 position: {
24884 x: pos[0],
24885 y: pos[1]
24886 }
24887 });
24888 }
24889 }
24890 }
24891
24892 // Check if we are drag panning the entire graph
24893 } else if (r.hoverData.dragging) {
24894 preventDefault = true;
24895 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24896 var deltaP;
24897 if (r.hoverData.justStartedPan) {
24898 var mdPos = r.hoverData.mdownPos;
24899 deltaP = {
24900 x: (pos[0] - mdPos[0]) * zoom,
24901 y: (pos[1] - mdPos[1]) * zoom
24902 };
24903 r.hoverData.justStartedPan = false;
24904 } else {
24905 deltaP = {
24906 x: disp[0] * zoom,
24907 y: disp[1] * zoom
24908 };
24909 }
24910 cy.panBy(deltaP);
24911 cy.emit('dragpan');
24912 r.hoverData.dragged = true;
24913 }
24914
24915 // Needs reproject due to pan changing viewport
24916 pos = r.projectIntoViewport(e.clientX, e.clientY);
24917
24918 // Checks primary button down & out of time & mouse not moved much
24919 } else if (select[4] == 1 && (down == null || down.pannable())) {
24920 if (isOverThresholdDrag) {
24921 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24922 goIntoBoxMode();
24923 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24924 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24925 if (allowPassthrough) {
24926 r.hoverData.dragging = true;
24927 r.hoverData.justStartedPan = true;
24928 select[4] = 0;
24929 r.data.bgActivePosistion = array2point(mdownPos);
24930 r.redrawHint('select', true);
24931 r.redraw();
24932 }
24933 }
24934 if (down && down.pannable() && down.active()) {
24935 down.unactivate();
24936 }
24937 }
24938 } else {
24939 if (down && down.pannable() && down.active()) {
24940 down.unactivate();
24941 }
24942 if ((!down || !down.grabbed()) && near != last) {
24943 if (last) {
24944 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24945 x: pos[0],
24946 y: pos[1]
24947 });
24948 }
24949 if (near) {
24950 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24951 x: pos[0],
24952 y: pos[1]
24953 });
24954 }
24955 r.hoverData.last = near;
24956 }
24957 if (down) {
24958 if (isOverThresholdDrag) {
24959 // then we can take action
24960
24961 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24962 // then selection overrides
24963 if (down && down.grabbed()) {
24964 freeDraggedElements(draggedElements);
24965 down.emit('freeon');
24966 draggedElements.emit('free');
24967 if (r.dragData.didDrag) {
24968 down.emit('dragfreeon');
24969 draggedElements.emit('dragfree');
24970 }
24971 }
24972 goIntoBoxMode();
24973 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24974 // drag node
24975 var justStartedDrag = !r.dragData.didDrag;
24976 if (justStartedDrag) {
24977 r.redrawHint('eles', true);
24978 }
24979 r.dragData.didDrag = true; // indicate that we actually did drag the node
24980
24981 // now, add the elements to the drag layer if not done already
24982 if (!r.hoverData.draggingEles) {
24983 addNodesToDrag(draggedElements, {
24984 inDragLayer: true
24985 });
24986 }
24987 var totalShift = {
24988 x: 0,
24989 y: 0
24990 };
24991 if (number$1(disp[0]) && number$1(disp[1])) {
24992 totalShift.x += disp[0];
24993 totalShift.y += disp[1];
24994 if (justStartedDrag) {
24995 var dragDelta = r.hoverData.dragDelta;
24996 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
24997 totalShift.x += dragDelta[0];
24998 totalShift.y += dragDelta[1];
24999 }
25000 }
25001 }
25002 r.hoverData.draggingEles = true;
25003 draggedElements.silentShift(totalShift).emit('position drag');
25004 r.redrawHint('drag', true);
25005 r.redraw();
25006 }
25007 } else {
25008 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25009 updateDragDelta();
25010 }
25011 }
25012
25013 // prevent the dragging from triggering text selection on the page
25014 preventDefault = true;
25015 }
25016 select[2] = pos[0];
25017 select[3] = pos[1];
25018 if (preventDefault) {
25019 if (e.stopPropagation) e.stopPropagation();
25020 if (e.preventDefault) e.preventDefault();
25021 return false;
25022 }
25023 }, false);
25024 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25025 r.registerBinding(containerWindow, 'mouseup', function mouseupHandler(e) {
25026 // eslint-disable-line no-undef
25027 // during left mouse button gestures, ignore other buttons
25028 if (r.hoverData.which === 1 && e.which !== 1 && r.hoverData.capture) {
25029 return;
25030 }
25031 var capture = r.hoverData.capture;
25032 if (!capture) {
25033 return;
25034 }
25035 r.hoverData.capture = false;
25036 var cy = r.cy;
25037 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25038 var select = r.selection;
25039 var near = r.findNearestElement(pos[0], pos[1], true, false);
25040 var draggedElements = r.dragData.possibleDragElements;
25041 var down = r.hoverData.down;
25042 var multSelKeyDown = isMultSelKeyDown(e);
25043 if (r.data.bgActivePosistion) {
25044 r.redrawHint('select', true);
25045 r.redraw();
25046 }
25047 r.hoverData.tapholdCancelled = true;
25048 r.data.bgActivePosistion = undefined; // not active bg now
25049
25050 if (down) {
25051 down.unactivate();
25052 }
25053 if (r.hoverData.which === 3) {
25054 var cxtEvt = {
25055 originalEvent: e,
25056 type: 'cxttapend',
25057 position: {
25058 x: pos[0],
25059 y: pos[1]
25060 }
25061 };
25062 if (down) {
25063 down.emit(cxtEvt);
25064 } else {
25065 cy.emit(cxtEvt);
25066 }
25067 if (!r.hoverData.cxtDragged) {
25068 var cxtTap = {
25069 originalEvent: e,
25070 type: 'cxttap',
25071 position: {
25072 x: pos[0],
25073 y: pos[1]
25074 }
25075 };
25076 if (down) {
25077 down.emit(cxtTap);
25078 } else {
25079 cy.emit(cxtTap);
25080 }
25081 }
25082 r.hoverData.cxtDragged = false;
25083 r.hoverData.which = null;
25084 } else if (r.hoverData.which === 1) {
25085 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25086 x: pos[0],
25087 y: pos[1]
25088 });
25089 if (!r.dragData.didDrag &&
25090 // didn't move a node around
25091 !r.hoverData.dragged &&
25092 // didn't pan
25093 !r.hoverData.selecting &&
25094 // not box selection
25095 !r.hoverData.isOverThresholdDrag // didn't move too much
25096 ) {
25097 triggerEvents(down, ["click", "tap", "vclick"], e, {
25098 x: pos[0],
25099 y: pos[1]
25100 });
25101 didDoubleClick = false;
25102 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25103 clickTimeout && clearTimeout(clickTimeout);
25104 didDoubleClick = true;
25105 prevClickTimeStamp = null;
25106 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25107 x: pos[0],
25108 y: pos[1]
25109 });
25110 } else {
25111 clickTimeout = setTimeout(function () {
25112 if (didDoubleClick) return;
25113 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25114 x: pos[0],
25115 y: pos[1]
25116 });
25117 }, cy.multiClickDebounceTime());
25118 prevClickTimeStamp = e.timeStamp;
25119 }
25120 }
25121
25122 // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25123 if (down == null // not mousedown on node
25124 && !r.dragData.didDrag // didn't move the node around
25125 && !r.hoverData.selecting // not box selection
25126 && !r.hoverData.dragged // didn't pan
25127 && !isMultSelKeyDown(e)) {
25128 cy.$(isSelected).unselect(['tapunselect']);
25129 if (draggedElements.length > 0) {
25130 r.redrawHint('eles', true);
25131 }
25132 r.dragData.possibleDragElements = draggedElements = cy.collection();
25133 }
25134
25135 // Single selection
25136 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25137 if (near != null && near._private.selectable) {
25138 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25139 if (near.selected()) {
25140 near.unselect(['tapunselect']);
25141 } else {
25142 near.select(['tapselect']);
25143 }
25144 } else {
25145 if (!multSelKeyDown) {
25146 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25147 near.select(['tapselect']);
25148 }
25149 }
25150 r.redrawHint('eles', true);
25151 }
25152 }
25153 if (r.hoverData.selecting) {
25154 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25155 r.redrawHint('select', true);
25156 if (box.length > 0) {
25157 r.redrawHint('eles', true);
25158 }
25159 cy.emit({
25160 type: 'boxend',
25161 originalEvent: e,
25162 position: {
25163 x: pos[0],
25164 y: pos[1]
25165 }
25166 });
25167 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25168 return ele.selectable() && !ele.selected();
25169 };
25170 if (cy.selectionType() === 'additive') {
25171 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25172 } else {
25173 if (!multSelKeyDown) {
25174 cy.$(isSelected).unmerge(box).unselect();
25175 }
25176 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25177 }
25178
25179 // always need redraw in case eles unselectable
25180 r.redraw();
25181 }
25182
25183 // Cancel drag pan
25184 if (r.hoverData.dragging) {
25185 r.hoverData.dragging = false;
25186 r.redrawHint('select', true);
25187 r.redrawHint('eles', true);
25188 r.redraw();
25189 }
25190 if (!select[4]) {
25191 r.redrawHint('drag', true);
25192 r.redrawHint('eles', true);
25193 var downWasGrabbed = down && down.grabbed();
25194 freeDraggedElements(draggedElements);
25195 if (downWasGrabbed) {
25196 down.emit('freeon');
25197 draggedElements.emit('free');
25198 if (r.dragData.didDrag) {
25199 down.emit('dragfreeon');
25200 draggedElements.emit('dragfree');
25201 }
25202 }
25203 }
25204 } // else not right mouse
25205
25206 select[4] = 0;
25207 r.hoverData.down = null;
25208 r.hoverData.cxtStarted = false;
25209 r.hoverData.draggingEles = false;
25210 r.hoverData.selecting = false;
25211 r.hoverData.isOverThresholdDrag = false;
25212 r.dragData.didDrag = false;
25213 r.hoverData.dragged = false;
25214 r.hoverData.dragDelta = [];
25215 r.hoverData.mdownPos = null;
25216 r.hoverData.mdownGPos = null;
25217 r.hoverData.which = null;
25218 }, false);
25219 var wheelHandler = function wheelHandler(e) {
25220 if (r.scrollingPage) {
25221 return;
25222 } // while scrolling, ignore wheel-to-zoom
25223
25224 var cy = r.cy;
25225 var zoom = cy.zoom();
25226 var pan = cy.pan();
25227 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25228 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25229 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25230 // if pan dragging or cxt dragging, wheel movements make no zoom
25231 e.preventDefault();
25232 return;
25233 }
25234 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25235 e.preventDefault();
25236 r.data.wheelZooming = true;
25237 clearTimeout(r.data.wheelTimeout);
25238 r.data.wheelTimeout = setTimeout(function () {
25239 r.data.wheelZooming = false;
25240 r.redrawHint('eles', true);
25241 r.redraw();
25242 }, 150);
25243 var diff;
25244 if (e.deltaY != null) {
25245 diff = e.deltaY / -250;
25246 } else if (e.wheelDeltaY != null) {
25247 diff = e.wheelDeltaY / 1000;
25248 } else {
25249 diff = e.wheelDelta / 1000;
25250 }
25251 diff = diff * r.wheelSensitivity;
25252 var needsWheelFix = e.deltaMode === 1;
25253 if (needsWheelFix) {
25254 // fixes slow wheel events on ff/linux and ff/windows
25255 diff *= 33;
25256 }
25257 var newZoom = cy.zoom() * Math.pow(10, diff);
25258 if (e.type === 'gesturechange') {
25259 newZoom = r.gestureStartZoom * e.scale;
25260 }
25261 cy.zoom({
25262 level: newZoom,
25263 renderedPosition: {
25264 x: rpos[0],
25265 y: rpos[1]
25266 }
25267 });
25268 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25269 }
25270 };
25271
25272 // Functions to help with whether mouse wheel should trigger zooming
25273 // --
25274 r.registerBinding(r.container, 'wheel', wheelHandler, true);
25275
25276 // disable nonstandard wheel events
25277 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25278 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25279 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25280
25281 r.registerBinding(containerWindow, 'scroll', function scrollHandler(e) {
25282 // eslint-disable-line no-unused-vars
25283 r.scrollingPage = true;
25284 clearTimeout(r.scrollingPageTimeout);
25285 r.scrollingPageTimeout = setTimeout(function () {
25286 r.scrollingPage = false;
25287 }, 250);
25288 }, true);
25289
25290 // desktop safari pinch to zoom start
25291 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25292 r.gestureStartZoom = r.cy.zoom();
25293 if (!r.hasTouchStarted) {
25294 // don't affect touch devices like iphone
25295 e.preventDefault();
25296 }
25297 }, true);
25298 r.registerBinding(r.container, 'gesturechange', function (e) {
25299 if (!r.hasTouchStarted) {
25300 // don't affect touch devices like iphone
25301 wheelHandler(e);
25302 }
25303 }, true);
25304
25305 // Functions to help with handling mouseout/mouseover on the Cytoscape container
25306 // Handle mouseout on Cytoscape container
25307 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25308 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25309 r.cy.emit({
25310 originalEvent: e,
25311 type: 'mouseout',
25312 position: {
25313 x: pos[0],
25314 y: pos[1]
25315 }
25316 });
25317 }, false);
25318 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25319 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25320 r.cy.emit({
25321 originalEvent: e,
25322 type: 'mouseover',
25323 position: {
25324 x: pos[0],
25325 y: pos[1]
25326 }
25327 });
25328 }, false);
25329 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25330 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25331 var center1, modelCenter1; // center point on start pinch to zoom
25332 var offsetLeft, offsetTop;
25333 var containerWidth, containerHeight;
25334 var twoFingersStartInside;
25335 var distance = function distance(x1, y1, x2, y2) {
25336 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25337 };
25338 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25339 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25340 };
25341 var touchstartHandler;
25342 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25343 r.hasTouchStarted = true;
25344 if (!eventInContainer(e)) {
25345 return;
25346 }
25347 blurActiveDomElement();
25348 r.touchData.capture = true;
25349 r.data.bgActivePosistion = undefined;
25350 var cy = r.cy;
25351 var now = r.touchData.now;
25352 var earlier = r.touchData.earlier;
25353 if (e.touches[0]) {
25354 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25355 now[0] = pos[0];
25356 now[1] = pos[1];
25357 }
25358 if (e.touches[1]) {
25359 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25360 now[2] = pos[0];
25361 now[3] = pos[1];
25362 }
25363 if (e.touches[2]) {
25364 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25365 now[4] = pos[0];
25366 now[5] = pos[1];
25367 }
25368
25369 // record starting points for pinch-to-zoom
25370 if (e.touches[1]) {
25371 r.touchData.singleTouchMoved = true;
25372 freeDraggedElements(r.dragData.touchDragEles);
25373 var offsets = r.findContainerClientCoords();
25374 offsetLeft = offsets[0];
25375 offsetTop = offsets[1];
25376 containerWidth = offsets[2];
25377 containerHeight = offsets[3];
25378 f1x1 = e.touches[0].clientX - offsetLeft;
25379 f1y1 = e.touches[0].clientY - offsetTop;
25380 f2x1 = e.touches[1].clientX - offsetLeft;
25381 f2y1 = e.touches[1].clientY - offsetTop;
25382 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25383 var pan = cy.pan();
25384 var zoom = cy.zoom();
25385 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25386 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25387 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25388 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom];
25389
25390 // consider context tap
25391 var cxtDistThreshold = 200;
25392 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25393 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25394 var near1 = r.findNearestElement(now[0], now[1], true, true);
25395 var near2 = r.findNearestElement(now[2], now[3], true, true);
25396 if (near1 && near1.isNode()) {
25397 near1.activate().emit({
25398 originalEvent: e,
25399 type: 'cxttapstart',
25400 position: {
25401 x: now[0],
25402 y: now[1]
25403 }
25404 });
25405 r.touchData.start = near1;
25406 } else if (near2 && near2.isNode()) {
25407 near2.activate().emit({
25408 originalEvent: e,
25409 type: 'cxttapstart',
25410 position: {
25411 x: now[0],
25412 y: now[1]
25413 }
25414 });
25415 r.touchData.start = near2;
25416 } else {
25417 cy.emit({
25418 originalEvent: e,
25419 type: 'cxttapstart',
25420 position: {
25421 x: now[0],
25422 y: now[1]
25423 }
25424 });
25425 }
25426 if (r.touchData.start) {
25427 r.touchData.start._private.grabbed = false;
25428 }
25429 r.touchData.cxt = true;
25430 r.touchData.cxtDragged = false;
25431 r.data.bgActivePosistion = undefined;
25432 r.redraw();
25433 return;
25434 }
25435 }
25436 if (e.touches[2]) {
25437 // ignore
25438
25439 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25440 if (cy.boxSelectionEnabled()) {
25441 e.preventDefault();
25442 }
25443 } else if (e.touches[1]) ; else if (e.touches[0]) {
25444 var nears = r.findNearestElements(now[0], now[1], true, true);
25445 var near = nears[0];
25446 if (near != null) {
25447 near.activate();
25448 r.touchData.start = near;
25449 r.touchData.starts = nears;
25450 if (r.nodeIsGrabbable(near)) {
25451 var draggedEles = r.dragData.touchDragEles = cy.collection();
25452 var selectedNodes = null;
25453 r.redrawHint('eles', true);
25454 r.redrawHint('drag', true);
25455 if (near.selected()) {
25456 // reset drag elements, since near will be added again
25457
25458 selectedNodes = cy.$(function (ele) {
25459 return ele.selected() && r.nodeIsGrabbable(ele);
25460 });
25461 addNodesToDrag(selectedNodes, {
25462 addToList: draggedEles
25463 });
25464 } else {
25465 addNodeToDrag(near, {
25466 addToList: draggedEles
25467 });
25468 }
25469 setGrabTarget(near);
25470 var makeEvent = function makeEvent(type) {
25471 return {
25472 originalEvent: e,
25473 type: type,
25474 position: {
25475 x: now[0],
25476 y: now[1]
25477 }
25478 };
25479 };
25480 near.emit(makeEvent('grabon'));
25481 if (selectedNodes) {
25482 selectedNodes.forEach(function (n) {
25483 n.emit(makeEvent('grab'));
25484 });
25485 } else {
25486 near.emit(makeEvent('grab'));
25487 }
25488 }
25489 }
25490 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25491 x: now[0],
25492 y: now[1]
25493 });
25494 if (near == null) {
25495 r.data.bgActivePosistion = {
25496 x: pos[0],
25497 y: pos[1]
25498 };
25499 r.redrawHint('select', true);
25500 r.redraw();
25501 }
25502
25503 // Tap, taphold
25504 // -----
25505
25506 r.touchData.singleTouchMoved = false;
25507 r.touchData.singleTouchStartTime = +new Date();
25508 clearTimeout(r.touchData.tapholdTimeout);
25509 r.touchData.tapholdTimeout = setTimeout(function () {
25510 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25511 && !r.touchData.selecting // box selection shouldn't allow taphold through
25512 ) {
25513 triggerEvents(r.touchData.start, ['taphold'], e, {
25514 x: now[0],
25515 y: now[1]
25516 });
25517 }
25518 }, r.tapholdDuration);
25519 }
25520 if (e.touches.length >= 1) {
25521 var sPos = r.touchData.startPosition = [null, null, null, null, null, null];
25522 for (var i = 0; i < now.length; i++) {
25523 sPos[i] = earlier[i] = now[i];
25524 }
25525 var touch0 = e.touches[0];
25526 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25527 }
25528 }, false);
25529 var touchmoveHandler;
25530 r.registerBinding(containerWindow, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25531 // eslint-disable-line no-undef
25532 var capture = r.touchData.capture;
25533 if (!capture && !eventInContainer(e)) {
25534 return;
25535 }
25536 var select = r.selection;
25537 var cy = r.cy;
25538 var now = r.touchData.now;
25539 var earlier = r.touchData.earlier;
25540 var zoom = cy.zoom();
25541 if (e.touches[0]) {
25542 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25543 now[0] = pos[0];
25544 now[1] = pos[1];
25545 }
25546 if (e.touches[1]) {
25547 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25548 now[2] = pos[0];
25549 now[3] = pos[1];
25550 }
25551 if (e.touches[2]) {
25552 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25553 now[4] = pos[0];
25554 now[5] = pos[1];
25555 }
25556 var startGPos = r.touchData.startGPosition;
25557 var isOverThresholdDrag;
25558 if (capture && e.touches[0] && startGPos) {
25559 var disp = [];
25560 for (var j = 0; j < now.length; j++) {
25561 disp[j] = now[j] - earlier[j];
25562 }
25563 var dx = e.touches[0].clientX - startGPos[0];
25564 var dx2 = dx * dx;
25565 var dy = e.touches[0].clientY - startGPos[1];
25566 var dy2 = dy * dy;
25567 var dist2 = dx2 + dy2;
25568 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25569 }
25570
25571 // context swipe cancelling
25572 if (capture && r.touchData.cxt) {
25573 e.preventDefault();
25574 var f1x2 = e.touches[0].clientX - offsetLeft,
25575 f1y2 = e.touches[0].clientY - offsetTop;
25576 var f2x2 = e.touches[1].clientX - offsetLeft,
25577 f2y2 = e.touches[1].clientY - offsetTop;
25578 // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25579 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25580 var factorSq = distance2Sq / distance1Sq;
25581 var distThreshold = 150;
25582 var distThresholdSq = distThreshold * distThreshold;
25583 var factorThreshold = 1.5;
25584 var factorThresholdSq = factorThreshold * factorThreshold;
25585
25586 // cancel ctx gestures if the distance b/t the fingers increases
25587 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25588 r.touchData.cxt = false;
25589 r.data.bgActivePosistion = undefined;
25590 r.redrawHint('select', true);
25591 var cxtEvt = {
25592 originalEvent: e,
25593 type: 'cxttapend',
25594 position: {
25595 x: now[0],
25596 y: now[1]
25597 }
25598 };
25599 if (r.touchData.start) {
25600 r.touchData.start.unactivate().emit(cxtEvt);
25601 r.touchData.start = null;
25602 } else {
25603 cy.emit(cxtEvt);
25604 }
25605 }
25606 }
25607
25608 // context swipe
25609 if (capture && r.touchData.cxt) {
25610 var cxtEvt = {
25611 originalEvent: e,
25612 type: 'cxtdrag',
25613 position: {
25614 x: now[0],
25615 y: now[1]
25616 }
25617 };
25618 r.data.bgActivePosistion = undefined;
25619 r.redrawHint('select', true);
25620 if (r.touchData.start) {
25621 r.touchData.start.emit(cxtEvt);
25622 } else {
25623 cy.emit(cxtEvt);
25624 }
25625 if (r.touchData.start) {
25626 r.touchData.start._private.grabbed = false;
25627 }
25628 r.touchData.cxtDragged = true;
25629 var near = r.findNearestElement(now[0], now[1], true, true);
25630 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25631 if (r.touchData.cxtOver) {
25632 r.touchData.cxtOver.emit({
25633 originalEvent: e,
25634 type: 'cxtdragout',
25635 position: {
25636 x: now[0],
25637 y: now[1]
25638 }
25639 });
25640 }
25641 r.touchData.cxtOver = near;
25642 if (near) {
25643 near.emit({
25644 originalEvent: e,
25645 type: 'cxtdragover',
25646 position: {
25647 x: now[0],
25648 y: now[1]
25649 }
25650 });
25651 }
25652 }
25653
25654 // box selection
25655 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25656 e.preventDefault();
25657 r.data.bgActivePosistion = undefined;
25658 this.lastThreeTouch = +new Date();
25659 if (!r.touchData.selecting) {
25660 cy.emit({
25661 originalEvent: e,
25662 type: 'boxstart',
25663 position: {
25664 x: now[0],
25665 y: now[1]
25666 }
25667 });
25668 }
25669 r.touchData.selecting = true;
25670 r.touchData.didSelect = true;
25671 select[4] = 1;
25672 if (!select || select.length === 0 || select[0] === undefined) {
25673 select[0] = (now[0] + now[2] + now[4]) / 3;
25674 select[1] = (now[1] + now[3] + now[5]) / 3;
25675 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25676 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25677 } else {
25678 select[2] = (now[0] + now[2] + now[4]) / 3;
25679 select[3] = (now[1] + now[3] + now[5]) / 3;
25680 }
25681 r.redrawHint('select', true);
25682 r.redraw();
25683
25684 // pinch to zoom
25685 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25686 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25687 // two fingers => pinch to zoom
25688 e.preventDefault();
25689 r.data.bgActivePosistion = undefined;
25690 r.redrawHint('select', true);
25691 var draggedEles = r.dragData.touchDragEles;
25692 if (draggedEles) {
25693 r.redrawHint('drag', true);
25694 for (var i = 0; i < draggedEles.length; i++) {
25695 var de_p = draggedEles[i]._private;
25696 de_p.grabbed = false;
25697 de_p.rscratch.inDragLayer = false;
25698 }
25699 }
25700 var _start = r.touchData.start;
25701
25702 // (x2, y2) for fingers 1 and 2
25703 var f1x2 = e.touches[0].clientX - offsetLeft,
25704 f1y2 = e.touches[0].clientY - offsetTop;
25705 var f2x2 = e.touches[1].clientX - offsetLeft,
25706 f2y2 = e.touches[1].clientY - offsetTop;
25707 var distance2 = distance(f1x2, f1y2, f2x2, f2y2);
25708 // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25709 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25710 var factor = distance2 / distance1;
25711 if (twoFingersStartInside) {
25712 // delta finger1
25713 var df1x = f1x2 - f1x1;
25714 var df1y = f1y2 - f1y1;
25715
25716 // delta finger 2
25717 var df2x = f2x2 - f2x1;
25718 var df2y = f2y2 - f2y1;
25719
25720 // translation is the normalised vector of the two fingers movement
25721 // i.e. so pinching cancels out and moving together pans
25722 var tx = (df1x + df2x) / 2;
25723 var ty = (df1y + df2y) / 2;
25724
25725 // now calculate the zoom
25726 var zoom1 = cy.zoom();
25727 var zoom2 = zoom1 * factor;
25728 var pan1 = cy.pan();
25729
25730 // the model center point converted to the current rendered pos
25731 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25732 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25733 var pan2 = {
25734 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25735 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25736 };
25737
25738 // remove dragged eles
25739 if (_start && _start.active()) {
25740 var draggedEles = r.dragData.touchDragEles;
25741 freeDraggedElements(draggedEles);
25742 r.redrawHint('drag', true);
25743 r.redrawHint('eles', true);
25744 _start.unactivate().emit('freeon');
25745 draggedEles.emit('free');
25746 if (r.dragData.didDrag) {
25747 _start.emit('dragfreeon');
25748 draggedEles.emit('dragfree');
25749 }
25750 }
25751 cy.viewport({
25752 zoom: zoom2,
25753 pan: pan2,
25754 cancelOnFailedZoom: true
25755 });
25756 cy.emit('pinchzoom');
25757 distance1 = distance2;
25758 f1x1 = f1x2;
25759 f1y1 = f1y2;
25760 f2x1 = f2x2;
25761 f2y1 = f2y2;
25762 r.pinching = true;
25763 }
25764
25765 // Re-project
25766 if (e.touches[0]) {
25767 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25768 now[0] = pos[0];
25769 now[1] = pos[1];
25770 }
25771 if (e.touches[1]) {
25772 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25773 now[2] = pos[0];
25774 now[3] = pos[1];
25775 }
25776 if (e.touches[2]) {
25777 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25778 now[4] = pos[0];
25779 now[5] = pos[1];
25780 }
25781 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25782 ) {
25783 var start = r.touchData.start;
25784 var last = r.touchData.last;
25785 var near;
25786 if (!r.hoverData.draggingEles && !r.swipePanning) {
25787 near = r.findNearestElement(now[0], now[1], true, true);
25788 }
25789 if (capture && start != null) {
25790 e.preventDefault();
25791 }
25792
25793 // dragging nodes
25794 if (capture && start != null && r.nodeIsDraggable(start)) {
25795 if (isOverThresholdDrag) {
25796 // then dragging can happen
25797 var draggedEles = r.dragData.touchDragEles;
25798 var justStartedDrag = !r.dragData.didDrag;
25799 if (justStartedDrag) {
25800 addNodesToDrag(draggedEles, {
25801 inDragLayer: true
25802 });
25803 }
25804 r.dragData.didDrag = true;
25805 var totalShift = {
25806 x: 0,
25807 y: 0
25808 };
25809 if (number$1(disp[0]) && number$1(disp[1])) {
25810 totalShift.x += disp[0];
25811 totalShift.y += disp[1];
25812 if (justStartedDrag) {
25813 r.redrawHint('eles', true);
25814 var dragDelta = r.touchData.dragDelta;
25815 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25816 totalShift.x += dragDelta[0];
25817 totalShift.y += dragDelta[1];
25818 }
25819 }
25820 }
25821 r.hoverData.draggingEles = true;
25822 draggedEles.silentShift(totalShift).emit('position drag');
25823 r.redrawHint('drag', true);
25824 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25825 r.redrawHint('eles', true);
25826 }
25827 r.redraw();
25828 } else {
25829 // otherwise keep track of drag delta for later
25830 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25831 if (dragDelta.length === 0) {
25832 dragDelta.push(disp[0]);
25833 dragDelta.push(disp[1]);
25834 } else {
25835 dragDelta[0] += disp[0];
25836 dragDelta[1] += disp[1];
25837 }
25838 }
25839 }
25840
25841 // touchmove
25842 {
25843 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25844 x: now[0],
25845 y: now[1]
25846 });
25847 if ((!start || !start.grabbed()) && near != last) {
25848 if (last) {
25849 last.emit({
25850 originalEvent: e,
25851 type: 'tapdragout',
25852 position: {
25853 x: now[0],
25854 y: now[1]
25855 }
25856 });
25857 }
25858 if (near) {
25859 near.emit({
25860 originalEvent: e,
25861 type: 'tapdragover',
25862 position: {
25863 x: now[0],
25864 y: now[1]
25865 }
25866 });
25867 }
25868 }
25869 r.touchData.last = near;
25870 }
25871
25872 // check to cancel taphold
25873 if (capture) {
25874 for (var i = 0; i < now.length; i++) {
25875 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25876 r.touchData.singleTouchMoved = true;
25877 }
25878 }
25879 }
25880
25881 // panning
25882 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25883 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25884 if (allowPassthrough) {
25885 e.preventDefault();
25886 if (!r.data.bgActivePosistion) {
25887 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25888 }
25889 if (r.swipePanning) {
25890 cy.panBy({
25891 x: disp[0] * zoom,
25892 y: disp[1] * zoom
25893 });
25894 cy.emit('dragpan');
25895 } else if (isOverThresholdDrag) {
25896 r.swipePanning = true;
25897 cy.panBy({
25898 x: dx * zoom,
25899 y: dy * zoom
25900 });
25901 cy.emit('dragpan');
25902 if (start) {
25903 start.unactivate();
25904 r.redrawHint('select', true);
25905 r.touchData.start = null;
25906 }
25907 }
25908 }
25909
25910 // Re-project
25911 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25912 now[0] = pos[0];
25913 now[1] = pos[1];
25914 }
25915 }
25916 for (var j = 0; j < now.length; j++) {
25917 earlier[j] = now[j];
25918 }
25919
25920 // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25921 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25922 r.data.bgActivePosistion = undefined;
25923 r.redrawHint('select', true);
25924 r.redraw();
25925 }
25926 }, false);
25927 var touchcancelHandler;
25928 r.registerBinding(containerWindow, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25929 // eslint-disable-line no-unused-vars
25930 var start = r.touchData.start;
25931 r.touchData.capture = false;
25932 if (start) {
25933 start.unactivate();
25934 }
25935 });
25936 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
25937 r.registerBinding(containerWindow, 'touchend', touchendHandler = function touchendHandler(e) {
25938 // eslint-disable-line no-unused-vars
25939 var start = r.touchData.start;
25940 var capture = r.touchData.capture;
25941 if (capture) {
25942 if (e.touches.length === 0) {
25943 r.touchData.capture = false;
25944 }
25945 e.preventDefault();
25946 } else {
25947 return;
25948 }
25949 var select = r.selection;
25950 r.swipePanning = false;
25951 r.hoverData.draggingEles = false;
25952 var cy = r.cy;
25953 var zoom = cy.zoom();
25954 var now = r.touchData.now;
25955 var earlier = r.touchData.earlier;
25956 if (e.touches[0]) {
25957 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25958 now[0] = pos[0];
25959 now[1] = pos[1];
25960 }
25961 if (e.touches[1]) {
25962 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25963 now[2] = pos[0];
25964 now[3] = pos[1];
25965 }
25966 if (e.touches[2]) {
25967 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25968 now[4] = pos[0];
25969 now[5] = pos[1];
25970 }
25971 if (start) {
25972 start.unactivate();
25973 }
25974 var ctxTapend;
25975 if (r.touchData.cxt) {
25976 ctxTapend = {
25977 originalEvent: e,
25978 type: 'cxttapend',
25979 position: {
25980 x: now[0],
25981 y: now[1]
25982 }
25983 };
25984 if (start) {
25985 start.emit(ctxTapend);
25986 } else {
25987 cy.emit(ctxTapend);
25988 }
25989 if (!r.touchData.cxtDragged) {
25990 var ctxTap = {
25991 originalEvent: e,
25992 type: 'cxttap',
25993 position: {
25994 x: now[0],
25995 y: now[1]
25996 }
25997 };
25998 if (start) {
25999 start.emit(ctxTap);
26000 } else {
26001 cy.emit(ctxTap);
26002 }
26003 }
26004 if (r.touchData.start) {
26005 r.touchData.start._private.grabbed = false;
26006 }
26007 r.touchData.cxt = false;
26008 r.touchData.start = null;
26009 r.redraw();
26010 return;
26011 }
26012
26013 // no more box selection if we don't have three fingers
26014 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26015 r.touchData.selecting = false;
26016 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26017 select[0] = undefined;
26018 select[1] = undefined;
26019 select[2] = undefined;
26020 select[3] = undefined;
26021 select[4] = 0;
26022 r.redrawHint('select', true);
26023 cy.emit({
26024 type: 'boxend',
26025 originalEvent: e,
26026 position: {
26027 x: now[0],
26028 y: now[1]
26029 }
26030 });
26031 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26032 return ele.selectable() && !ele.selected();
26033 };
26034 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26035 if (box.nonempty()) {
26036 r.redrawHint('eles', true);
26037 }
26038 r.redraw();
26039 }
26040 if (start != null) {
26041 start.unactivate();
26042 }
26043 if (e.touches[2]) {
26044 r.data.bgActivePosistion = undefined;
26045 r.redrawHint('select', true);
26046 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26047 r.data.bgActivePosistion = undefined;
26048 r.redrawHint('select', true);
26049 var draggedEles = r.dragData.touchDragEles;
26050 if (start != null) {
26051 var startWasGrabbed = start._private.grabbed;
26052 freeDraggedElements(draggedEles);
26053 r.redrawHint('drag', true);
26054 r.redrawHint('eles', true);
26055 if (startWasGrabbed) {
26056 start.emit('freeon');
26057 draggedEles.emit('free');
26058 if (r.dragData.didDrag) {
26059 start.emit('dragfreeon');
26060 draggedEles.emit('dragfree');
26061 }
26062 }
26063 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26064 x: now[0],
26065 y: now[1]
26066 });
26067 start.unactivate();
26068 r.touchData.start = null;
26069 } else {
26070 var near = r.findNearestElement(now[0], now[1], true, true);
26071 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26072 x: now[0],
26073 y: now[1]
26074 });
26075 }
26076 var dx = r.touchData.startPosition[0] - now[0];
26077 var dx2 = dx * dx;
26078 var dy = r.touchData.startPosition[1] - now[1];
26079 var dy2 = dy * dy;
26080 var dist2 = dx2 + dy2;
26081 var rdist2 = dist2 * zoom * zoom;
26082
26083 // Tap event, roughly same as mouse click event for touch
26084 if (!r.touchData.singleTouchMoved) {
26085 if (!start) {
26086 cy.$(':selected').unselect(['tapunselect']);
26087 }
26088 triggerEvents(start, ['tap', 'vclick'], e, {
26089 x: now[0],
26090 y: now[1]
26091 });
26092 didDoubleTouch = false;
26093 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26094 touchTimeout && clearTimeout(touchTimeout);
26095 didDoubleTouch = true;
26096 prevTouchTimeStamp = null;
26097 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26098 x: now[0],
26099 y: now[1]
26100 });
26101 } else {
26102 touchTimeout = setTimeout(function () {
26103 if (didDoubleTouch) return;
26104 triggerEvents(start, ['onetap', 'voneclick'], e, {
26105 x: now[0],
26106 y: now[1]
26107 });
26108 }, cy.multiClickDebounceTime());
26109 prevTouchTimeStamp = e.timeStamp;
26110 }
26111 }
26112
26113 // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26114 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26115 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26116 ) {
26117 if (cy.selectionType() === 'single') {
26118 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26119 start.select(['tapselect']);
26120 } else {
26121 if (start.selected()) {
26122 start.unselect(['tapunselect']);
26123 } else {
26124 start.select(['tapselect']);
26125 }
26126 }
26127 r.redrawHint('eles', true);
26128 }
26129 r.touchData.singleTouchMoved = true;
26130 }
26131 for (var j = 0; j < now.length; j++) {
26132 earlier[j] = now[j];
26133 }
26134 r.dragData.didDrag = false; // reset for next touchstart
26135
26136 if (e.touches.length === 0) {
26137 r.touchData.dragDelta = [];
26138 r.touchData.startPosition = [null, null, null, null, null, null];
26139 r.touchData.startGPosition = null;
26140 r.touchData.didSelect = false;
26141 }
26142 if (e.touches.length < 2) {
26143 if (e.touches.length === 1) {
26144 // the old start global pos'n may not be the same finger that remains
26145 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26146 }
26147 r.pinching = false;
26148 r.redrawHint('eles', true);
26149 r.redraw();
26150 }
26151
26152 //r.redraw();
26153 }, false);
26154
26155 // fallback compatibility layer for ms pointer events
26156 if (typeof TouchEvent === 'undefined') {
26157 var pointers = [];
26158 var makeTouch = function makeTouch(e) {
26159 return {
26160 clientX: e.clientX,
26161 clientY: e.clientY,
26162 force: 1,
26163 identifier: e.pointerId,
26164 pageX: e.pageX,
26165 pageY: e.pageY,
26166 radiusX: e.width / 2,
26167 radiusY: e.height / 2,
26168 screenX: e.screenX,
26169 screenY: e.screenY,
26170 target: e.target
26171 };
26172 };
26173 var makePointer = function makePointer(e) {
26174 return {
26175 event: e,
26176 touch: makeTouch(e)
26177 };
26178 };
26179 var addPointer = function addPointer(e) {
26180 pointers.push(makePointer(e));
26181 };
26182 var removePointer = function removePointer(e) {
26183 for (var i = 0; i < pointers.length; i++) {
26184 var p = pointers[i];
26185 if (p.event.pointerId === e.pointerId) {
26186 pointers.splice(i, 1);
26187 return;
26188 }
26189 }
26190 };
26191 var updatePointer = function updatePointer(e) {
26192 var p = pointers.filter(function (p) {
26193 return p.event.pointerId === e.pointerId;
26194 })[0];
26195 p.event = e;
26196 p.touch = makeTouch(e);
26197 };
26198 var addTouchesToEvent = function addTouchesToEvent(e) {
26199 e.touches = pointers.map(function (p) {
26200 return p.touch;
26201 });
26202 };
26203 var pointerIsMouse = function pointerIsMouse(e) {
26204 return e.pointerType === 'mouse' || e.pointerType === 4;
26205 };
26206 r.registerBinding(r.container, 'pointerdown', function (e) {
26207 if (pointerIsMouse(e)) {
26208 return;
26209 } // mouse already handled
26210
26211 e.preventDefault();
26212 addPointer(e);
26213 addTouchesToEvent(e);
26214 touchstartHandler(e);
26215 });
26216 r.registerBinding(r.container, 'pointerup', function (e) {
26217 if (pointerIsMouse(e)) {
26218 return;
26219 } // mouse already handled
26220
26221 removePointer(e);
26222 addTouchesToEvent(e);
26223 touchendHandler(e);
26224 });
26225 r.registerBinding(r.container, 'pointercancel', function (e) {
26226 if (pointerIsMouse(e)) {
26227 return;
26228 } // mouse already handled
26229
26230 removePointer(e);
26231 addTouchesToEvent(e);
26232 touchcancelHandler(e);
26233 });
26234 r.registerBinding(r.container, 'pointermove', function (e) {
26235 if (pointerIsMouse(e)) {
26236 return;
26237 } // mouse already handled
26238
26239 e.preventDefault();
26240 updatePointer(e);
26241 addTouchesToEvent(e);
26242 touchmoveHandler(e);
26243 });
26244 }
26245};
26246
26247var BRp$2 = {};
26248BRp$2.generatePolygon = function (name, points) {
26249 return this.nodeShapes[name] = {
26250 renderer: this,
26251 name: name,
26252 points: points,
26253 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26254 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26255 },
26256 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26257 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26258 },
26259 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26260 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26261 }
26262 };
26263};
26264BRp$2.generateEllipse = function () {
26265 return this.nodeShapes['ellipse'] = {
26266 renderer: this,
26267 name: 'ellipse',
26268 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26269 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26270 },
26271 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26272 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26273 },
26274 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26275 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26276 }
26277 };
26278};
26279BRp$2.generateRoundPolygon = function (name, points) {
26280 return this.nodeShapes[name] = {
26281 renderer: this,
26282 name: name,
26283 points: points,
26284 getOrCreateCorners: function getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, field) {
26285 if (rs[field] !== undefined && rs[field + '-cx'] === centerX && rs[field + '-cy'] === centerY) {
26286 return rs[field];
26287 }
26288 rs[field] = new Array(points.length / 2);
26289 rs[field + '-cx'] = centerX;
26290 rs[field + '-cy'] = centerY;
26291 var halfW = width / 2;
26292 var halfH = height / 2;
26293 cornerRadius = cornerRadius === 'auto' ? getRoundPolygonRadius(width, height) : cornerRadius;
26294 var p = new Array(points.length / 2);
26295 for (var _i = 0; _i < points.length / 2; _i++) {
26296 p[_i] = {
26297 x: centerX + halfW * points[_i * 2],
26298 y: centerY + halfH * points[_i * 2 + 1]
26299 };
26300 }
26301 var i,
26302 p1,
26303 p2,
26304 p3,
26305 len = p.length;
26306 p1 = p[len - 1];
26307 // for each point
26308 for (i = 0; i < len; i++) {
26309 p2 = p[i % len];
26310 p3 = p[(i + 1) % len];
26311 rs[field][i] = getRoundCorner(p1, p2, p3, cornerRadius);
26312 p1 = p2;
26313 p2 = p3;
26314 }
26315 return rs[field];
26316 },
26317 draw: function draw(context, centerX, centerY, width, height, cornerRadius, rs) {
26318 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points, this.getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, 'drawCorners'));
26319 },
26320 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius, rs) {
26321 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height, padding, this.getOrCreateCorners(nodeX, nodeY, width, height, cornerRadius, rs, 'corners'));
26322 },
26323 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius, rs) {
26324 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height, this.getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, 'corners'));
26325 }
26326 };
26327};
26328BRp$2.generateRoundRectangle = function () {
26329 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26330 renderer: this,
26331 name: 'round-rectangle',
26332 points: generateUnitNgonPointsFitToSquare(4, 0),
26333 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26334 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, this.points, cornerRadius);
26335 },
26336 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26337 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding, cornerRadius);
26338 },
26339 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26340 var halfWidth = width / 2;
26341 var halfHeight = height / 2;
26342 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(width, height) : cornerRadius;
26343 cornerRadius = Math.min(halfWidth, halfHeight, cornerRadius);
26344 var diam = cornerRadius * 2;
26345
26346 // Check hBox
26347 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26348 return true;
26349 }
26350
26351 // Check vBox
26352 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26353 return true;
26354 }
26355
26356 // Check top left quarter circle
26357 if (checkInEllipse(x, y, diam, diam, centerX - halfWidth + cornerRadius, centerY - halfHeight + cornerRadius, padding)) {
26358 return true;
26359 }
26360
26361 // Check top right quarter circle
26362 if (checkInEllipse(x, y, diam, diam, centerX + halfWidth - cornerRadius, centerY - halfHeight + cornerRadius, padding)) {
26363 return true;
26364 }
26365
26366 // Check bottom right quarter circle
26367 if (checkInEllipse(x, y, diam, diam, centerX + halfWidth - cornerRadius, centerY + halfHeight - cornerRadius, padding)) {
26368 return true;
26369 }
26370
26371 // Check bottom left quarter circle
26372 if (checkInEllipse(x, y, diam, diam, centerX - halfWidth + cornerRadius, centerY + halfHeight - cornerRadius, padding)) {
26373 return true;
26374 }
26375 return false;
26376 }
26377 };
26378};
26379BRp$2.generateCutRectangle = function () {
26380 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26381 renderer: this,
26382 name: 'cut-rectangle',
26383 cornerLength: getCutRectangleCornerLength(),
26384 points: generateUnitNgonPointsFitToSquare(4, 0),
26385 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26386 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, null, cornerRadius);
26387 },
26388 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY, cornerRadius) {
26389 var cl = cornerRadius === 'auto' ? this.cornerLength : cornerRadius;
26390 var hh = height / 2;
26391 var hw = width / 2;
26392 var xBegin = centerX - hw;
26393 var xEnd = centerX + hw;
26394 var yBegin = centerY - hh;
26395 var yEnd = centerY + hh;
26396
26397 // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26398 return {
26399 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26400 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26401 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26402 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26403 };
26404 },
26405 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26406 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY, cornerRadius);
26407 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26408 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26409 },
26410 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26411 var cl = cornerRadius === 'auto' ? this.cornerLength : cornerRadius;
26412 // Check hBox
26413 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * cl, [0, -1], padding)) {
26414 return true;
26415 }
26416
26417 // Check vBox
26418 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * cl, height, [0, -1], padding)) {
26419 return true;
26420 }
26421 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26422 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26423 }
26424 };
26425};
26426BRp$2.generateBarrel = function () {
26427 return this.nodeShapes['barrel'] = {
26428 renderer: this,
26429 name: 'barrel',
26430 points: generateUnitNgonPointsFitToSquare(4, 0),
26431 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26432 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26433 },
26434 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26435 // use two fixed t values for the bezier curve approximation
26436
26437 var t0 = 0.15;
26438 var t1 = 0.5;
26439 var t2 = 0.85;
26440 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26441 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26442 // approximate curve pts based on the two t values
26443 var m0 = qbezierPtAt({
26444 x: pts[0],
26445 y: pts[1]
26446 }, {
26447 x: pts[2],
26448 y: pts[3]
26449 }, {
26450 x: pts[4],
26451 y: pts[5]
26452 }, t0);
26453 var m1 = qbezierPtAt({
26454 x: pts[0],
26455 y: pts[1]
26456 }, {
26457 x: pts[2],
26458 y: pts[3]
26459 }, {
26460 x: pts[4],
26461 y: pts[5]
26462 }, t1);
26463 var m2 = qbezierPtAt({
26464 x: pts[0],
26465 y: pts[1]
26466 }, {
26467 x: pts[2],
26468 y: pts[3]
26469 }, {
26470 x: pts[4],
26471 y: pts[5]
26472 }, t2);
26473 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26474 };
26475 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26476 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26477 },
26478 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26479 var hh = height / 2;
26480 var hw = width / 2;
26481 var xBegin = centerX - hw;
26482 var xEnd = centerX + hw;
26483 var yBegin = centerY - hh;
26484 var yEnd = centerY + hh;
26485 var curveConstants = getBarrelCurveConstants(width, height);
26486 var hOffset = curveConstants.heightOffset;
26487 var wOffset = curveConstants.widthOffset;
26488 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width;
26489
26490 // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26491 var pts = {
26492 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26493 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26494 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26495 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26496 };
26497 pts.topLeft.isTop = true;
26498 pts.topRight.isTop = true;
26499 pts.bottomLeft.isBottom = true;
26500 pts.bottomRight.isBottom = true;
26501 return pts;
26502 },
26503 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26504 var curveConstants = getBarrelCurveConstants(width, height);
26505 var hOffset = curveConstants.heightOffset;
26506 var wOffset = curveConstants.widthOffset;
26507
26508 // Check hBox
26509 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26510 return true;
26511 }
26512
26513 // Check vBox
26514 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26515 return true;
26516 }
26517 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26518 var getCurveT = function getCurveT(x, y, curvePts) {
26519 var x0 = curvePts[4];
26520 var x1 = curvePts[2];
26521 var x2 = curvePts[0];
26522 var y0 = curvePts[5];
26523 // var y1 = curvePts[ 3 ];
26524 var y2 = curvePts[1];
26525 var xMin = Math.min(x0, x2);
26526 var xMax = Math.max(x0, x2);
26527 var yMin = Math.min(y0, y2);
26528 var yMax = Math.max(y0, y2);
26529 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26530 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26531 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26532 var validRoots = roots.filter(function (r) {
26533 return 0 <= r && r <= 1;
26534 });
26535 if (validRoots.length > 0) {
26536 return validRoots[0];
26537 }
26538 }
26539 return null;
26540 };
26541 var curveRegions = Object.keys(barrelCurvePts);
26542 for (var i = 0; i < curveRegions.length; i++) {
26543 var corner = curveRegions[i];
26544 var cornerPts = barrelCurvePts[corner];
26545 var t = getCurveT(x, y, cornerPts);
26546 if (t == null) {
26547 continue;
26548 }
26549 var y0 = cornerPts[5];
26550 var y1 = cornerPts[3];
26551 var y2 = cornerPts[1];
26552 var bezY = qbezierAt(y0, y1, y2, t);
26553 if (cornerPts.isTop && bezY <= y) {
26554 return true;
26555 }
26556 if (cornerPts.isBottom && y <= bezY) {
26557 return true;
26558 }
26559 }
26560 return false;
26561 }
26562 };
26563};
26564BRp$2.generateBottomRoundrectangle = function () {
26565 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26566 renderer: this,
26567 name: 'bottom-round-rectangle',
26568 points: generateUnitNgonPointsFitToSquare(4, 0),
26569 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26570 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, this.points, cornerRadius);
26571 },
26572 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26573 var topStartX = nodeX - (width / 2 + padding);
26574 var topStartY = nodeY - (height / 2 + padding);
26575 var topEndY = topStartY;
26576 var topEndX = nodeX + (width / 2 + padding);
26577 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26578 if (topIntersections.length > 0) {
26579 return topIntersections;
26580 }
26581 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding, cornerRadius);
26582 },
26583 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26584 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(width, height) : cornerRadius;
26585 var diam = 2 * cornerRadius;
26586
26587 // Check hBox
26588 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26589 return true;
26590 }
26591
26592 // Check vBox
26593 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26594 return true;
26595 }
26596
26597 // check non-rounded top side
26598 var outerWidth = width / 2 + 2 * padding;
26599 var outerHeight = height / 2 + 2 * padding;
26600 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26601 if (pointInsidePolygonPoints(x, y, points)) {
26602 return true;
26603 }
26604
26605 // Check bottom right quarter circle
26606 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26607 return true;
26608 }
26609
26610 // Check bottom left quarter circle
26611 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26612 return true;
26613 }
26614 return false;
26615 }
26616 };
26617};
26618BRp$2.registerNodeShapes = function () {
26619 var nodeShapes = this.nodeShapes = {};
26620 var renderer = this;
26621 this.generateEllipse();
26622 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26623 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26624 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26625 nodeShapes['square'] = nodeShapes['rectangle'];
26626 this.generateRoundRectangle();
26627 this.generateCutRectangle();
26628 this.generateBarrel();
26629 this.generateBottomRoundrectangle();
26630 {
26631 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26632 this.generatePolygon('diamond', diamondPoints);
26633 this.generateRoundPolygon('round-diamond', diamondPoints);
26634 }
26635 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26636 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26637 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26638 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26639 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26640 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26641 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26642 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26643 var star5Points = new Array(20);
26644 {
26645 var outerPoints = generateUnitNgonPoints(5, 0);
26646 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5);
26647
26648 // Outer radius is 1; inner radius of star is smaller
26649 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26650 innerRadius *= 1.57;
26651 for (var i = 0; i < innerPoints.length / 2; i++) {
26652 innerPoints[i * 2] *= innerRadius;
26653 innerPoints[i * 2 + 1] *= innerRadius;
26654 }
26655 for (var i = 0; i < 20 / 4; i++) {
26656 star5Points[i * 4] = outerPoints[i * 2];
26657 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26658 star5Points[i * 4 + 2] = innerPoints[i * 2];
26659 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26660 }
26661 }
26662 star5Points = fitPolygonToSquare(star5Points);
26663 this.generatePolygon('star', star5Points);
26664 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26665 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26666 this.generatePolygon('right-rhomboid', [-0.333, -1, 1, -1, 0.333, 1, -1, 1]);
26667 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]);
26668 {
26669 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26670 this.generatePolygon('tag', tagPoints);
26671 this.generateRoundPolygon('round-tag', tagPoints);
26672 }
26673 nodeShapes.makePolygon = function (points) {
26674 // use caching on user-specified polygons so they are as fast as native shapes
26675
26676 var key = points.join('$');
26677 var name = 'polygon-' + key;
26678 var shape;
26679 if (shape = this[name]) {
26680 // got cached shape
26681 return shape;
26682 }
26683
26684 // create and cache new shape
26685 return renderer.generatePolygon(name, points);
26686 };
26687};
26688
26689var BRp$1 = {};
26690BRp$1.timeToRender = function () {
26691 return this.redrawTotalTime / this.redrawCount;
26692};
26693BRp$1.redraw = function (options) {
26694 options = options || staticEmptyObject();
26695 var r = this;
26696 if (r.averageRedrawTime === undefined) {
26697 r.averageRedrawTime = 0;
26698 }
26699 if (r.lastRedrawTime === undefined) {
26700 r.lastRedrawTime = 0;
26701 }
26702 if (r.lastDrawTime === undefined) {
26703 r.lastDrawTime = 0;
26704 }
26705 r.requestedFrame = true;
26706 r.renderOptions = options;
26707};
26708BRp$1.beforeRender = function (fn, priority) {
26709 // the renderer can't add tick callbacks when destroyed
26710 if (this.destroyed) {
26711 return;
26712 }
26713 if (priority == null) {
26714 error('Priority is not optional for beforeRender');
26715 }
26716 var cbs = this.beforeRenderCallbacks;
26717 cbs.push({
26718 fn: fn,
26719 priority: priority
26720 });
26721
26722 // higher priority callbacks executed first
26723 cbs.sort(function (a, b) {
26724 return b.priority - a.priority;
26725 });
26726};
26727var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26728 var cbs = r.beforeRenderCallbacks;
26729 for (var i = 0; i < cbs.length; i++) {
26730 cbs[i].fn(willDraw, startTime);
26731 }
26732};
26733BRp$1.startRenderLoop = function () {
26734 var r = this;
26735 var cy = r.cy;
26736 if (r.renderLoopStarted) {
26737 return;
26738 } else {
26739 r.renderLoopStarted = true;
26740 }
26741 var renderFn = function renderFn(requestTime) {
26742 if (r.destroyed) {
26743 return;
26744 }
26745 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26746 beforeRenderCallbacks(r, true, requestTime);
26747 var startTime = performanceNow();
26748 r.render(r.renderOptions);
26749 var endTime = r.lastDrawTime = performanceNow();
26750 if (r.averageRedrawTime === undefined) {
26751 r.averageRedrawTime = endTime - startTime;
26752 }
26753 if (r.redrawCount === undefined) {
26754 r.redrawCount = 0;
26755 }
26756 r.redrawCount++;
26757 if (r.redrawTotalTime === undefined) {
26758 r.redrawTotalTime = 0;
26759 }
26760 var duration = endTime - startTime;
26761 r.redrawTotalTime += duration;
26762 r.lastRedrawTime = duration;
26763
26764 // use a weighted average with a bias from the previous average so we don't spike so easily
26765 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26766 r.requestedFrame = false;
26767 } else {
26768 beforeRenderCallbacks(r, false, requestTime);
26769 }
26770 r.skipFrame = false;
26771 requestAnimationFrame(renderFn);
26772 };
26773 requestAnimationFrame(renderFn);
26774};
26775
26776var BaseRenderer = function BaseRenderer(options) {
26777 this.init(options);
26778};
26779var BR = BaseRenderer;
26780var BRp = BR.prototype;
26781BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26782BRp.init = function (options) {
26783 var r = this;
26784 r.options = options;
26785 r.cy = options.cy;
26786 var ctr = r.container = options.cy.container();
26787 var containerWindow = r.cy.window();
26788
26789 // prepend a stylesheet in the head such that
26790 if (containerWindow) {
26791 var document = containerWindow.document;
26792 var head = document.head;
26793 var stylesheetId = '__________cytoscape_stylesheet';
26794 var className = '__________cytoscape_container';
26795 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26796 if (ctr.className.indexOf(className) < 0) {
26797 ctr.className = (ctr.className || '') + ' ' + className;
26798 }
26799 if (!stylesheetAlreadyExists) {
26800 var stylesheet = document.createElement('style');
26801 stylesheet.id = stylesheetId;
26802 stylesheet.textContent = '.' + className + ' { position: relative; }';
26803 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26804 }
26805
26806 var computedStyle = containerWindow.getComputedStyle(ctr);
26807 var position = computedStyle.getPropertyValue('position');
26808 if (position === 'static') {
26809 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26810 }
26811 }
26812 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26813
26814 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95];
26815
26816 //--Pointer-related data
26817 r.hoverData = {
26818 down: null,
26819 last: null,
26820 downTime: null,
26821 triggerMode: null,
26822 dragging: false,
26823 initialPan: [null, null],
26824 capture: false
26825 };
26826 r.dragData = {
26827 possibleDragElements: []
26828 };
26829 r.touchData = {
26830 start: null,
26831 capture: false,
26832 // These 3 fields related to tap, taphold events
26833 startPosition: [null, null, null, null, null, null],
26834 singleTouchStartTime: null,
26835 singleTouchMoved: true,
26836 now: [null, null, null, null, null, null],
26837 earlier: [null, null, null, null, null, null]
26838 };
26839 r.redraws = 0;
26840 r.showFps = options.showFps;
26841 r.debug = options.debug;
26842 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26843 r.textureOnViewport = options.textureOnViewport;
26844 r.wheelSensitivity = options.wheelSensitivity;
26845 r.motionBlurEnabled = options.motionBlur; // on by default
26846 r.forcedPixelRatio = number$1(options.pixelRatio) ? options.pixelRatio : null;
26847 r.motionBlur = options.motionBlur; // for initial kick off
26848 r.motionBlurOpacity = options.motionBlurOpacity;
26849 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26850 r.motionBlurPxRatio = 1;
26851 r.mbPxRBlurry = 1; //0.8;
26852 r.minMbLowQualFrames = 4;
26853 r.fullQualityMb = false;
26854 r.clearedForMotionBlur = [];
26855 r.desktopTapThreshold = options.desktopTapThreshold;
26856 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26857 r.touchTapThreshold = options.touchTapThreshold;
26858 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26859 r.tapholdDuration = 500;
26860 r.bindings = [];
26861 r.beforeRenderCallbacks = [];
26862 r.beforeRenderPriorities = {
26863 // higher priority execs before lower one
26864 animations: 400,
26865 eleCalcs: 300,
26866 eleTxrDeq: 200,
26867 lyrTxrDeq: 150,
26868 lyrTxrSkip: 100
26869 };
26870 r.registerNodeShapes();
26871 r.registerArrowShapes();
26872 r.registerCalculationListeners();
26873};
26874BRp.notify = function (eventName, eles) {
26875 var r = this;
26876 var cy = r.cy;
26877
26878 // the renderer can't be notified after it's destroyed
26879 if (this.destroyed) {
26880 return;
26881 }
26882 if (eventName === 'init') {
26883 r.load();
26884 return;
26885 }
26886 if (eventName === 'destroy') {
26887 r.destroy();
26888 return;
26889 }
26890 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26891 r.invalidateCachedZSortedEles();
26892 }
26893 if (eventName === 'viewport') {
26894 r.redrawHint('select', true);
26895 }
26896 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26897 r.invalidateContainerClientCoordsCache();
26898 r.matchCanvasSize(r.container);
26899 }
26900 r.redrawHint('eles', true);
26901 r.redrawHint('drag', true);
26902 this.startRenderLoop();
26903 this.redraw();
26904};
26905BRp.destroy = function () {
26906 var r = this;
26907 r.destroyed = true;
26908 r.cy.stopAnimationLoop();
26909 for (var i = 0; i < r.bindings.length; i++) {
26910 var binding = r.bindings[i];
26911 var b = binding;
26912 var tgt = b.target;
26913 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26914 }
26915 r.bindings = [];
26916 r.beforeRenderCallbacks = [];
26917 r.onUpdateEleCalcsFns = [];
26918 if (r.removeObserver) {
26919 r.removeObserver.disconnect();
26920 }
26921 if (r.styleObserver) {
26922 r.styleObserver.disconnect();
26923 }
26924 if (r.resizeObserver) {
26925 r.resizeObserver.disconnect();
26926 }
26927 if (r.labelCalcDiv) {
26928 try {
26929 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26930 } catch (e) {
26931 // ie10 issue #1014
26932 }
26933 }
26934};
26935BRp.isHeadless = function () {
26936 return false;
26937};
26938[BRp$f, BRp$5, BRp$4, BRp$3, BRp$2, BRp$1].forEach(function (props) {
26939 extend(BRp, props);
26940});
26941
26942var fullFpsTime = 1000 / 60; // assume 60 frames per second
26943
26944var defs = {
26945 setupDequeueing: function setupDequeueing(opts) {
26946 return function setupDequeueingImpl() {
26947 var self = this;
26948 var r = this.renderer;
26949 if (self.dequeueingSetup) {
26950 return;
26951 } else {
26952 self.dequeueingSetup = true;
26953 }
26954 var queueRedraw = debounce_1(function () {
26955 r.redrawHint('eles', true);
26956 r.redrawHint('drag', true);
26957 r.redraw();
26958 }, opts.deqRedrawThreshold);
26959 var dequeue = function dequeue(willDraw, frameStartTime) {
26960 var startTime = performanceNow();
26961 var avgRenderTime = r.averageRedrawTime;
26962 var renderTime = r.lastRedrawTime;
26963 var deqd = [];
26964 var extent = r.cy.extent();
26965 var pixelRatio = r.getPixelRatio();
26966
26967 // if we aren't in a tick that causes a draw, then the rendered style
26968 // queue won't automatically be flushed before dequeueing starts
26969 if (!willDraw) {
26970 r.flushRenderedStyleQueue();
26971 }
26972 while (true) {
26973 // eslint-disable-line no-constant-condition
26974 var now = performanceNow();
26975 var duration = now - startTime;
26976 var frameDuration = now - frameStartTime;
26977 if (renderTime < fullFpsTime) {
26978 // if we're rendering faster than the ideal fps, then do dequeueing
26979 // during all of the remaining frame time
26980
26981 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26982 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26983 break;
26984 }
26985 } else {
26986 if (willDraw) {
26987 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26988 break;
26989 }
26990 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26991 break;
26992 }
26993 }
26994 var thisDeqd = opts.deq(self, pixelRatio, extent);
26995 if (thisDeqd.length > 0) {
26996 for (var i = 0; i < thisDeqd.length; i++) {
26997 deqd.push(thisDeqd[i]);
26998 }
26999 } else {
27000 break;
27001 }
27002 }
27003
27004 // callbacks on dequeue
27005 if (deqd.length > 0) {
27006 opts.onDeqd(self, deqd);
27007 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27008 queueRedraw();
27009 }
27010 }
27011 };
27012 var priority = opts.priority || noop$1;
27013 r.beforeRender(dequeue, priority(self));
27014 };
27015 }
27016};
27017
27018// Allows lookups for (ele, lvl) => cache.
27019// Uses keys so elements may share the same cache.
27020var ElementTextureCacheLookup = /*#__PURE__*/function () {
27021 function ElementTextureCacheLookup(getKey) {
27022 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27023 _classCallCheck(this, ElementTextureCacheLookup);
27024 this.idsByKey = new Map$2();
27025 this.keyForId = new Map$2();
27026 this.cachesByLvl = new Map$2();
27027 this.lvls = [];
27028 this.getKey = getKey;
27029 this.doesEleInvalidateKey = doesEleInvalidateKey;
27030 }
27031 _createClass(ElementTextureCacheLookup, [{
27032 key: "getIdsFor",
27033 value: function getIdsFor(key) {
27034 if (key == null) {
27035 error("Can not get id list for null key");
27036 }
27037 var idsByKey = this.idsByKey;
27038 var ids = this.idsByKey.get(key);
27039 if (!ids) {
27040 ids = new Set$1();
27041 idsByKey.set(key, ids);
27042 }
27043 return ids;
27044 }
27045 }, {
27046 key: "addIdForKey",
27047 value: function addIdForKey(key, id) {
27048 if (key != null) {
27049 this.getIdsFor(key).add(id);
27050 }
27051 }
27052 }, {
27053 key: "deleteIdForKey",
27054 value: function deleteIdForKey(key, id) {
27055 if (key != null) {
27056 this.getIdsFor(key)["delete"](id);
27057 }
27058 }
27059 }, {
27060 key: "getNumberOfIdsForKey",
27061 value: function getNumberOfIdsForKey(key) {
27062 if (key == null) {
27063 return 0;
27064 } else {
27065 return this.getIdsFor(key).size;
27066 }
27067 }
27068 }, {
27069 key: "updateKeyMappingFor",
27070 value: function updateKeyMappingFor(ele) {
27071 var id = ele.id();
27072 var prevKey = this.keyForId.get(id);
27073 var currKey = this.getKey(ele);
27074 this.deleteIdForKey(prevKey, id);
27075 this.addIdForKey(currKey, id);
27076 this.keyForId.set(id, currKey);
27077 }
27078 }, {
27079 key: "deleteKeyMappingFor",
27080 value: function deleteKeyMappingFor(ele) {
27081 var id = ele.id();
27082 var prevKey = this.keyForId.get(id);
27083 this.deleteIdForKey(prevKey, id);
27084 this.keyForId["delete"](id);
27085 }
27086 }, {
27087 key: "keyHasChangedFor",
27088 value: function keyHasChangedFor(ele) {
27089 var id = ele.id();
27090 var prevKey = this.keyForId.get(id);
27091 var newKey = this.getKey(ele);
27092 return prevKey !== newKey;
27093 }
27094 }, {
27095 key: "isInvalid",
27096 value: function isInvalid(ele) {
27097 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27098 }
27099 }, {
27100 key: "getCachesAt",
27101 value: function getCachesAt(lvl) {
27102 var cachesByLvl = this.cachesByLvl,
27103 lvls = this.lvls;
27104 var caches = cachesByLvl.get(lvl);
27105 if (!caches) {
27106 caches = new Map$2();
27107 cachesByLvl.set(lvl, caches);
27108 lvls.push(lvl);
27109 }
27110 return caches;
27111 }
27112 }, {
27113 key: "getCache",
27114 value: function getCache(key, lvl) {
27115 return this.getCachesAt(lvl).get(key);
27116 }
27117 }, {
27118 key: "get",
27119 value: function get(ele, lvl) {
27120 var key = this.getKey(ele);
27121 var cache = this.getCache(key, lvl);
27122
27123 // getting for an element may need to add to the id list b/c eles can share keys
27124 if (cache != null) {
27125 this.updateKeyMappingFor(ele);
27126 }
27127 return cache;
27128 }
27129 }, {
27130 key: "getForCachedKey",
27131 value: function getForCachedKey(ele, lvl) {
27132 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27133 var cache = this.getCache(key, lvl);
27134 return cache;
27135 }
27136 }, {
27137 key: "hasCache",
27138 value: function hasCache(key, lvl) {
27139 return this.getCachesAt(lvl).has(key);
27140 }
27141 }, {
27142 key: "has",
27143 value: function has(ele, lvl) {
27144 var key = this.getKey(ele);
27145 return this.hasCache(key, lvl);
27146 }
27147 }, {
27148 key: "setCache",
27149 value: function setCache(key, lvl, cache) {
27150 cache.key = key;
27151 this.getCachesAt(lvl).set(key, cache);
27152 }
27153 }, {
27154 key: "set",
27155 value: function set(ele, lvl, cache) {
27156 var key = this.getKey(ele);
27157 this.setCache(key, lvl, cache);
27158 this.updateKeyMappingFor(ele);
27159 }
27160 }, {
27161 key: "deleteCache",
27162 value: function deleteCache(key, lvl) {
27163 this.getCachesAt(lvl)["delete"](key);
27164 }
27165 }, {
27166 key: "delete",
27167 value: function _delete(ele, lvl) {
27168 var key = this.getKey(ele);
27169 this.deleteCache(key, lvl);
27170 }
27171 }, {
27172 key: "invalidateKey",
27173 value: function invalidateKey(key) {
27174 var _this = this;
27175 this.lvls.forEach(function (lvl) {
27176 return _this.deleteCache(key, lvl);
27177 });
27178 }
27179
27180 // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27181 }, {
27182 key: "invalidate",
27183 value: function invalidate(ele) {
27184 var id = ele.id();
27185 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27186
27187 this.deleteKeyMappingFor(ele);
27188 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27189 if (entireKeyInvalidated) {
27190 // clear mapping for current key
27191 this.invalidateKey(key);
27192 }
27193 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27194 }
27195 }]);
27196 return ElementTextureCacheLookup;
27197}();
27198
27199var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27200var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27201var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27202var maxLvl$1 = 3; // when larger than this scale just render directly (caching is not helpful)
27203var maxZoom$1 = 7.99; // beyond this zoom level, layered textures are not used
27204var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27205var defTxrWidth = 1024; // default/minimum texture width
27206var maxTxrW = 1024; // the maximum width of a texture
27207var maxTxrH = 1024; // the maximum height of a texture
27208var minUtility = 0.2; // if usage of texture is less than this, it is retired
27209var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27210var maxFullnessChecks = 10; // dequeued after this many checks
27211var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27212var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27213var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27214var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27215var deqRedrawThreshold$1 = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27216var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27217
27218var getTxrReasons = {
27219 dequeue: 'dequeue',
27220 downscale: 'downscale',
27221 highQuality: 'highQuality'
27222};
27223var initDefaults = defaults$g({
27224 getKey: null,
27225 doesEleInvalidateKey: falsify,
27226 drawElement: null,
27227 getBoundingBox: null,
27228 getRotationPoint: null,
27229 getRotationOffset: null,
27230 isVisible: trueify,
27231 allowEdgeTxrCaching: true,
27232 allowParentTxrCaching: true
27233});
27234var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27235 var self = this;
27236 self.renderer = renderer;
27237 self.onDequeues = [];
27238 var opts = initDefaults(initOptions);
27239 extend(self, opts);
27240 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27241 self.setupDequeueing();
27242};
27243var ETCp = ElementTextureCache.prototype;
27244ETCp.reasons = getTxrReasons;
27245
27246// the list of textures in which new subtextures for elements can be placed
27247ETCp.getTextureQueue = function (txrH) {
27248 var self = this;
27249 self.eleImgCaches = self.eleImgCaches || {};
27250 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27251};
27252
27253// the list of usused textures which can be recycled (in use in texture queue)
27254ETCp.getRetiredTextureQueue = function (txrH) {
27255 var self = this;
27256 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27257 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27258 return rtxtrQ;
27259};
27260
27261// queue of element draw requests at different scale levels
27262ETCp.getElementQueue = function () {
27263 var self = this;
27264 var q = self.eleCacheQueue = self.eleCacheQueue || new heap(function (a, b) {
27265 return b.reqs - a.reqs;
27266 });
27267 return q;
27268};
27269
27270// queue of element draw requests at different scale levels (element id lookup)
27271ETCp.getElementKeyToQueue = function () {
27272 var self = this;
27273 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27274 return k2q;
27275};
27276ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27277 var self = this;
27278 var r = this.renderer;
27279 var zoom = r.cy.zoom();
27280 var lookup = this.lookup;
27281 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27282 return null;
27283 }
27284 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27285 return null;
27286 }
27287 if (lvl == null) {
27288 lvl = Math.ceil(log2(zoom * pxRatio));
27289 }
27290 if (lvl < minLvl$1) {
27291 lvl = minLvl$1;
27292 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27293 return null;
27294 }
27295 var scale = Math.pow(2, lvl);
27296 var eleScaledH = bb.h * scale;
27297 var eleScaledW = bb.w * scale;
27298 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27299 if (!this.isVisible(ele, scaledLabelShown)) {
27300 return null;
27301 }
27302 var eleCache = lookup.get(ele, lvl);
27303
27304 // if this get was on an unused/invalidated cache, then restore the texture usage metric
27305 if (eleCache && eleCache.invalidated) {
27306 eleCache.invalidated = false;
27307 eleCache.texture.invalidatedWidth -= eleCache.width;
27308 }
27309 if (eleCache) {
27310 return eleCache;
27311 }
27312 var txrH; // which texture height this ele belongs to
27313
27314 if (eleScaledH <= minTxrH) {
27315 txrH = minTxrH;
27316 } else if (eleScaledH <= txrStepH) {
27317 txrH = txrStepH;
27318 } else {
27319 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27320 }
27321 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27322 return null; // caching large elements is not efficient
27323 }
27324
27325 var txrQ = self.getTextureQueue(txrH);
27326
27327 // first try the second last one in case it has space at the end
27328 var txr = txrQ[txrQ.length - 2];
27329 var addNewTxr = function addNewTxr() {
27330 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27331 };
27332
27333 // try the last one if there is no second last one
27334 if (!txr) {
27335 txr = txrQ[txrQ.length - 1];
27336 }
27337
27338 // if the last one doesn't exist, we need a first one
27339 if (!txr) {
27340 txr = addNewTxr();
27341 }
27342
27343 // if there's no room in the current texture, we need a new one
27344 if (txr.width - txr.usedWidth < eleScaledW) {
27345 txr = addNewTxr();
27346 }
27347 var scalableFrom = function scalableFrom(otherCache) {
27348 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27349 };
27350 var deqing = reason && reason === getTxrReasons.dequeue;
27351 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27352 var downscaleReq = reason && reason === getTxrReasons.downscale;
27353 var higherCache; // the nearest cache with a higher level
27354 for (var l = lvl + 1; l <= maxLvl$1; l++) {
27355 var c = lookup.get(ele, l);
27356 if (c) {
27357 higherCache = c;
27358 break;
27359 }
27360 }
27361 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27362 var downscale = function downscale() {
27363 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27364 };
27365
27366 // reset ele area in texture
27367 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27368 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27369 if (scalableFrom(oneUpCache)) {
27370 // then we can relatively cheaply rescale the existing image w/o rerendering
27371 downscale();
27372 } else if (scalableFrom(higherCache)) {
27373 // then use the higher cache for now and queue the next level down
27374 // to cheaply scale towards the smaller level
27375
27376 if (highQualityReq) {
27377 for (var _l = higherCache.level; _l > lvl; _l--) {
27378 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27379 }
27380 downscale();
27381 } else {
27382 self.queueElement(ele, higherCache.level - 1);
27383 return higherCache;
27384 }
27385 } else {
27386 var lowerCache; // the nearest cache with a lower level
27387 if (!deqing && !highQualityReq && !downscaleReq) {
27388 for (var _l2 = lvl - 1; _l2 >= minLvl$1; _l2--) {
27389 var _c = lookup.get(ele, _l2);
27390 if (_c) {
27391 lowerCache = _c;
27392 break;
27393 }
27394 }
27395 }
27396 if (scalableFrom(lowerCache)) {
27397 // then use the lower quality cache for now and queue the better one for later
27398
27399 self.queueElement(ele, lvl);
27400 return lowerCache;
27401 }
27402 txr.context.translate(txr.usedWidth, 0);
27403 txr.context.scale(scale, scale);
27404 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27405 txr.context.scale(1 / scale, 1 / scale);
27406 txr.context.translate(-txr.usedWidth, 0);
27407 }
27408 eleCache = {
27409 x: txr.usedWidth,
27410 texture: txr,
27411 level: lvl,
27412 scale: scale,
27413 width: eleScaledW,
27414 height: eleScaledH,
27415 scaledLabelShown: scaledLabelShown
27416 };
27417 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27418 txr.eleCaches.push(eleCache);
27419 lookup.set(ele, lvl, eleCache);
27420 self.checkTextureFullness(txr);
27421 return eleCache;
27422};
27423ETCp.invalidateElements = function (eles) {
27424 for (var i = 0; i < eles.length; i++) {
27425 this.invalidateElement(eles[i]);
27426 }
27427};
27428ETCp.invalidateElement = function (ele) {
27429 var self = this;
27430 var lookup = self.lookup;
27431 var caches = [];
27432 var invalid = lookup.isInvalid(ele);
27433 if (!invalid) {
27434 return; // override the invalidation request if the element key has not changed
27435 }
27436
27437 for (var lvl = minLvl$1; lvl <= maxLvl$1; lvl++) {
27438 var cache = lookup.getForCachedKey(ele, lvl);
27439 if (cache) {
27440 caches.push(cache);
27441 }
27442 }
27443 var noOtherElesUseCache = lookup.invalidate(ele);
27444 if (noOtherElesUseCache) {
27445 for (var i = 0; i < caches.length; i++) {
27446 var _cache = caches[i];
27447 var txr = _cache.texture;
27448
27449 // remove space from the texture it belongs to
27450 txr.invalidatedWidth += _cache.width;
27451
27452 // mark the cache as invalidated
27453 _cache.invalidated = true;
27454
27455 // retire the texture if its utility is low
27456 self.checkTextureUtility(txr);
27457 }
27458 }
27459
27460 // remove from queue since the old req was for the old state
27461 self.removeFromQueue(ele);
27462};
27463ETCp.checkTextureUtility = function (txr) {
27464 // invalidate all entries in the cache if the cache size is small
27465 if (txr.invalidatedWidth >= minUtility * txr.width) {
27466 this.retireTexture(txr);
27467 }
27468};
27469ETCp.checkTextureFullness = function (txr) {
27470 // if texture has been mostly filled and passed over several times, remove
27471 // it from the queue so we don't need to waste time looking at it to put new things
27472
27473 var self = this;
27474 var txrQ = self.getTextureQueue(txr.height);
27475 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27476 removeFromArray(txrQ, txr);
27477 } else {
27478 txr.fullnessChecks++;
27479 }
27480};
27481ETCp.retireTexture = function (txr) {
27482 var self = this;
27483 var txrH = txr.height;
27484 var txrQ = self.getTextureQueue(txrH);
27485 var lookup = this.lookup;
27486
27487 // retire the texture from the active / searchable queue:
27488
27489 removeFromArray(txrQ, txr);
27490 txr.retired = true;
27491
27492 // remove the refs from the eles to the caches:
27493
27494 var eleCaches = txr.eleCaches;
27495 for (var i = 0; i < eleCaches.length; i++) {
27496 var eleCache = eleCaches[i];
27497 lookup.deleteCache(eleCache.key, eleCache.level);
27498 }
27499 clearArray(eleCaches);
27500
27501 // add the texture to a retired queue so it can be recycled in future:
27502
27503 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27504 rtxtrQ.push(txr);
27505};
27506ETCp.addTexture = function (txrH, minW) {
27507 var self = this;
27508 var txrQ = self.getTextureQueue(txrH);
27509 var txr = {};
27510 txrQ.push(txr);
27511 txr.eleCaches = [];
27512 txr.height = txrH;
27513 txr.width = Math.max(defTxrWidth, minW);
27514 txr.usedWidth = 0;
27515 txr.invalidatedWidth = 0;
27516 txr.fullnessChecks = 0;
27517 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27518 txr.context = txr.canvas.getContext('2d');
27519 return txr;
27520};
27521ETCp.recycleTexture = function (txrH, minW) {
27522 var self = this;
27523 var txrQ = self.getTextureQueue(txrH);
27524 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27525 for (var i = 0; i < rtxtrQ.length; i++) {
27526 var txr = rtxtrQ[i];
27527 if (txr.width >= minW) {
27528 txr.retired = false;
27529 txr.usedWidth = 0;
27530 txr.invalidatedWidth = 0;
27531 txr.fullnessChecks = 0;
27532 clearArray(txr.eleCaches);
27533 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27534 txr.context.clearRect(0, 0, txr.width, txr.height);
27535 removeFromArray(rtxtrQ, txr);
27536 txrQ.push(txr);
27537 return txr;
27538 }
27539 }
27540};
27541ETCp.queueElement = function (ele, lvl) {
27542 var self = this;
27543 var q = self.getElementQueue();
27544 var k2q = self.getElementKeyToQueue();
27545 var key = this.getKey(ele);
27546 var existingReq = k2q[key];
27547 if (existingReq) {
27548 // use the max lvl b/c in between lvls are cheap to make
27549 existingReq.level = Math.max(existingReq.level, lvl);
27550 existingReq.eles.merge(ele);
27551 existingReq.reqs++;
27552 q.updateItem(existingReq);
27553 } else {
27554 var req = {
27555 eles: ele.spawn().merge(ele),
27556 level: lvl,
27557 reqs: 1,
27558 key: key
27559 };
27560 q.push(req);
27561 k2q[key] = req;
27562 }
27563};
27564ETCp.dequeue = function (pxRatio /*, extent*/) {
27565 var self = this;
27566 var q = self.getElementQueue();
27567 var k2q = self.getElementKeyToQueue();
27568 var dequeued = [];
27569 var lookup = self.lookup;
27570 for (var i = 0; i < maxDeqSize$1; i++) {
27571 if (q.size() > 0) {
27572 var req = q.pop();
27573 var key = req.key;
27574 var ele = req.eles[0]; // all eles have the same key
27575 var cacheExists = lookup.hasCache(ele, req.level);
27576
27577 // clear out the key to req lookup
27578 k2q[key] = null;
27579
27580 // dequeueing isn't necessary with an existing cache
27581 if (cacheExists) {
27582 continue;
27583 }
27584 dequeued.push(req);
27585 var bb = self.getBoundingBox(ele);
27586 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27587 } else {
27588 break;
27589 }
27590 }
27591 return dequeued;
27592};
27593ETCp.removeFromQueue = function (ele) {
27594 var self = this;
27595 var q = self.getElementQueue();
27596 var k2q = self.getElementKeyToQueue();
27597 var key = this.getKey(ele);
27598 var req = k2q[key];
27599 if (req != null) {
27600 if (req.eles.length === 1) {
27601 // remove if last ele in the req
27602 // bring to front of queue
27603 req.reqs = MAX_INT$1;
27604 q.updateItem(req);
27605 q.pop(); // remove from queue
27606
27607 k2q[key] = null; // remove from lookup map
27608 } else {
27609 // otherwise just remove ele from req
27610 req.eles.unmerge(ele);
27611 }
27612 }
27613};
27614ETCp.onDequeue = function (fn) {
27615 this.onDequeues.push(fn);
27616};
27617ETCp.offDequeue = function (fn) {
27618 removeFromArray(this.onDequeues, fn);
27619};
27620ETCp.setupDequeueing = defs.setupDequeueing({
27621 deqRedrawThreshold: deqRedrawThreshold$1,
27622 deqCost: deqCost$1,
27623 deqAvgCost: deqAvgCost$1,
27624 deqNoDrawCost: deqNoDrawCost$1,
27625 deqFastCost: deqFastCost$1,
27626 deq: function deq(self, pxRatio, extent) {
27627 return self.dequeue(pxRatio, extent);
27628 },
27629 onDeqd: function onDeqd(self, deqd) {
27630 for (var i = 0; i < self.onDequeues.length; i++) {
27631 var fn = self.onDequeues[i];
27632 fn(deqd);
27633 }
27634 },
27635 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27636 for (var i = 0; i < deqd.length; i++) {
27637 var eles = deqd[i].eles;
27638 for (var j = 0; j < eles.length; j++) {
27639 var bb = eles[j].boundingBox();
27640 if (boundingBoxesIntersect(bb, extent)) {
27641 return true;
27642 }
27643 }
27644 }
27645 return false;
27646 },
27647 priority: function priority(self) {
27648 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27649 }
27650});
27651
27652var defNumLayers = 1; // default number of layers to use
27653var minLvl = -4; // when scaling smaller than that we don't need to re-render
27654var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
27655var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
27656var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27657var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27658var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27659var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27660var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27661var deqFastCost = 0.9; // % of frame time to be used when >60fps
27662var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27663var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27664var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27665var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27666
27667// var log = function(){ console.log.apply( console, arguments ); };
27668
27669var LayeredTextureCache = function LayeredTextureCache(renderer) {
27670 var self = this;
27671 var r = self.renderer = renderer;
27672 var cy = r.cy;
27673 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27674
27675 self.firstGet = true;
27676 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27677 self.skipping = false;
27678 self.eleTxrDeqs = cy.collection();
27679 self.scheduleElementRefinement = debounce_1(function () {
27680 self.refineElementTextures(self.eleTxrDeqs);
27681 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27682 }, refineEleDebounceTime);
27683 r.beforeRender(function (willDraw, now) {
27684 if (now - self.lastInvalidationTime <= invalidThreshold) {
27685 self.skipping = true;
27686 } else {
27687 self.skipping = false;
27688 }
27689 }, r.beforeRenderPriorities.lyrTxrSkip);
27690 var qSort = function qSort(a, b) {
27691 return b.reqs - a.reqs;
27692 };
27693 self.layersQueue = new heap(qSort);
27694 self.setupDequeueing();
27695};
27696var LTCp = LayeredTextureCache.prototype;
27697var layerIdPool = 0;
27698var MAX_INT = Math.pow(2, 53) - 1;
27699LTCp.makeLayer = function (bb, lvl) {
27700 var scale = Math.pow(2, lvl);
27701 var w = Math.ceil(bb.w * scale);
27702 var h = Math.ceil(bb.h * scale);
27703 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27704 var layer = {
27705 id: layerIdPool = ++layerIdPool % MAX_INT,
27706 bb: bb,
27707 level: lvl,
27708 width: w,
27709 height: h,
27710 canvas: canvas,
27711 context: canvas.getContext('2d'),
27712 eles: [],
27713 elesQueue: [],
27714 reqs: 0
27715 };
27716
27717 // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27718
27719 var cxt = layer.context;
27720 var dx = -layer.bb.x1;
27721 var dy = -layer.bb.y1;
27722
27723 // do the transform on creation to save cycles (it's the same for all eles)
27724 cxt.scale(scale, scale);
27725 cxt.translate(dx, dy);
27726 return layer;
27727};
27728LTCp.getLayers = function (eles, pxRatio, lvl) {
27729 var self = this;
27730 var r = self.renderer;
27731 var cy = r.cy;
27732 var zoom = cy.zoom();
27733 var firstGet = self.firstGet;
27734 self.firstGet = false;
27735
27736 // log('--\nget layers with %s eles', eles.length);
27737 //log eles.map(function(ele){ return ele.id() }) );
27738
27739 if (lvl == null) {
27740 lvl = Math.ceil(log2(zoom * pxRatio));
27741 if (lvl < minLvl) {
27742 lvl = minLvl;
27743 } else if (zoom >= maxZoom || lvl > maxLvl) {
27744 return null;
27745 }
27746 }
27747 self.validateLayersElesOrdering(lvl, eles);
27748 var layersByLvl = self.layersByLevel;
27749 var scale = Math.pow(2, lvl);
27750 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27751 var bb;
27752 var lvlComplete = self.levelIsComplete(lvl, eles);
27753 var tmpLayers;
27754 var checkTempLevels = function checkTempLevels() {
27755 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27756 self.validateLayersElesOrdering(l, eles);
27757 if (self.levelIsComplete(l, eles)) {
27758 tmpLayers = layersByLvl[l];
27759 return true;
27760 }
27761 };
27762 var checkLvls = function checkLvls(dir) {
27763 if (tmpLayers) {
27764 return;
27765 }
27766 for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) {
27767 if (canUseAsTmpLvl(l)) {
27768 break;
27769 }
27770 }
27771 };
27772 checkLvls(+1);
27773 checkLvls(-1);
27774
27775 // remove the invalid layers; they will be replaced as needed later in this function
27776 for (var i = layers.length - 1; i >= 0; i--) {
27777 var layer = layers[i];
27778 if (layer.invalid) {
27779 removeFromArray(layers, layer);
27780 }
27781 }
27782 };
27783 if (!lvlComplete) {
27784 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27785 // and later queue the current layerset so we can get the proper quality level soon
27786
27787 checkTempLevels();
27788 } else {
27789 // log('level complete, using existing layers\n--');
27790 return layers;
27791 }
27792 var getBb = function getBb() {
27793 if (!bb) {
27794 bb = makeBoundingBox();
27795 for (var i = 0; i < eles.length; i++) {
27796 updateBoundingBox(bb, eles[i].boundingBox());
27797 }
27798 }
27799 return bb;
27800 };
27801 var makeLayer = function makeLayer(opts) {
27802 opts = opts || {};
27803 var after = opts.after;
27804 getBb();
27805 var area = bb.w * scale * (bb.h * scale);
27806 if (area > maxLayerArea) {
27807 return null;
27808 }
27809 var layer = self.makeLayer(bb, lvl);
27810 if (after != null) {
27811 var index = layers.indexOf(after) + 1;
27812 layers.splice(index, 0, layer);
27813 } else if (opts.insert === undefined || opts.insert) {
27814 // no after specified => first layer made so put at start
27815 layers.unshift(layer);
27816 }
27817
27818 // if( tmpLayers ){
27819 //self.queueLayer( layer );
27820 // }
27821
27822 return layer;
27823 };
27824 if (self.skipping && !firstGet) {
27825 // log('skip layers');
27826 return null;
27827 }
27828
27829 // log('do layers');
27830
27831 var layer = null;
27832 var maxElesPerLayer = eles.length / defNumLayers;
27833 var allowLazyQueueing = !firstGet;
27834 for (var i = 0; i < eles.length; i++) {
27835 var ele = eles[i];
27836 var rs = ele._private.rscratch;
27837 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27838
27839 // log('look at ele', ele.id());
27840
27841 var existingLayer = caches[lvl];
27842 if (existingLayer) {
27843 // reuse layer for later eles
27844 // log('reuse layer for', ele.id());
27845 layer = existingLayer;
27846 continue;
27847 }
27848 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27849 // log('make new layer for ele %s', ele.id());
27850
27851 layer = makeLayer({
27852 insert: true,
27853 after: layer
27854 });
27855
27856 // if now layer can be built then we can't use layers at this level
27857 if (!layer) {
27858 return null;
27859 }
27860
27861 // log('new layer with id %s', layer.id);
27862 }
27863
27864 if (tmpLayers || allowLazyQueueing) {
27865 // log('queue ele %s in layer %s', ele.id(), layer.id);
27866 self.queueLayer(layer, ele);
27867 } else {
27868 // log('draw ele %s in layer %s', ele.id(), layer.id);
27869 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27870 }
27871 layer.eles.push(ele);
27872 caches[lvl] = layer;
27873 }
27874
27875 // log('--');
27876
27877 if (tmpLayers) {
27878 // then we only queued the current layerset and can't draw it yet
27879 return tmpLayers;
27880 }
27881 if (allowLazyQueueing) {
27882 // log('lazy queue level', lvl);
27883 return null;
27884 }
27885 return layers;
27886};
27887
27888// a layer may want to use an ele cache of a higher level to avoid blurriness
27889// so the layer level might not equal the ele level
27890LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27891 return lvl;
27892};
27893LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27894 var self = this;
27895 var r = this.renderer;
27896 var context = layer.context;
27897 var bb = ele.boundingBox();
27898 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27899 return;
27900 }
27901 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27902 {
27903 r.setImgSmoothing(context, false);
27904 }
27905 {
27906 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27907 }
27908 {
27909 r.setImgSmoothing(context, true);
27910 }
27911};
27912LTCp.levelIsComplete = function (lvl, eles) {
27913 var self = this;
27914 var layers = self.layersByLevel[lvl];
27915 if (!layers || layers.length === 0) {
27916 return false;
27917 }
27918 var numElesInLayers = 0;
27919 for (var i = 0; i < layers.length; i++) {
27920 var layer = layers[i];
27921
27922 // if there are any eles needed to be drawn yet, the level is not complete
27923 if (layer.reqs > 0) {
27924 return false;
27925 }
27926
27927 // if the layer is invalid, the level is not complete
27928 if (layer.invalid) {
27929 return false;
27930 }
27931 numElesInLayers += layer.eles.length;
27932 }
27933
27934 // we should have exactly the number of eles passed in to be complete
27935 if (numElesInLayers !== eles.length) {
27936 return false;
27937 }
27938 return true;
27939};
27940LTCp.validateLayersElesOrdering = function (lvl, eles) {
27941 var layers = this.layersByLevel[lvl];
27942 if (!layers) {
27943 return;
27944 }
27945
27946 // if in a layer the eles are not in the same order, then the layer is invalid
27947 // (i.e. there is an ele in between the eles in the layer)
27948
27949 for (var i = 0; i < layers.length; i++) {
27950 var layer = layers[i];
27951 var offset = -1;
27952
27953 // find the offset
27954 for (var j = 0; j < eles.length; j++) {
27955 if (layer.eles[0] === eles[j]) {
27956 offset = j;
27957 break;
27958 }
27959 }
27960 if (offset < 0) {
27961 // then the layer has nonexistent elements and is invalid
27962 this.invalidateLayer(layer);
27963 continue;
27964 }
27965
27966 // the eles in the layer must be in the same continuous order, else the layer is invalid
27967
27968 var o = offset;
27969 for (var j = 0; j < layer.eles.length; j++) {
27970 if (layer.eles[j] !== eles[o + j]) {
27971 // log('invalidate based on ordering', layer.id);
27972
27973 this.invalidateLayer(layer);
27974 break;
27975 }
27976 }
27977 }
27978};
27979LTCp.updateElementsInLayers = function (eles, update) {
27980 var self = this;
27981 var isEles = element(eles[0]);
27982
27983 // collect udpated elements (cascaded from the layers) and update each
27984 // layer itself along the way
27985 for (var i = 0; i < eles.length; i++) {
27986 var req = isEles ? null : eles[i];
27987 var ele = isEles ? eles[i] : eles[i].ele;
27988 var rs = ele._private.rscratch;
27989 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27990 for (var l = minLvl; l <= maxLvl; l++) {
27991 var layer = caches[l];
27992 if (!layer) {
27993 continue;
27994 }
27995
27996 // if update is a request from the ele cache, then it affects only
27997 // the matching level
27998 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
27999 continue;
28000 }
28001 update(layer, ele, req);
28002 }
28003 }
28004};
28005LTCp.haveLayers = function () {
28006 var self = this;
28007 var haveLayers = false;
28008 for (var l = minLvl; l <= maxLvl; l++) {
28009 var layers = self.layersByLevel[l];
28010 if (layers && layers.length > 0) {
28011 haveLayers = true;
28012 break;
28013 }
28014 }
28015 return haveLayers;
28016};
28017LTCp.invalidateElements = function (eles) {
28018 var self = this;
28019 if (eles.length === 0) {
28020 return;
28021 }
28022 self.lastInvalidationTime = performanceNow();
28023
28024 // log('update invalidate layer time from eles');
28025
28026 if (eles.length === 0 || !self.haveLayers()) {
28027 return;
28028 }
28029 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28030 self.invalidateLayer(layer);
28031 });
28032};
28033LTCp.invalidateLayer = function (layer) {
28034 // log('update invalidate layer time');
28035
28036 this.lastInvalidationTime = performanceNow();
28037 if (layer.invalid) {
28038 return;
28039 } // save cycles
28040
28041 var lvl = layer.level;
28042 var eles = layer.eles;
28043 var layers = this.layersByLevel[lvl];
28044
28045 // log('invalidate layer', layer.id );
28046
28047 removeFromArray(layers, layer);
28048 // layer.eles = [];
28049
28050 layer.elesQueue = [];
28051 layer.invalid = true;
28052 if (layer.replacement) {
28053 layer.replacement.invalid = true;
28054 }
28055 for (var i = 0; i < eles.length; i++) {
28056 var caches = eles[i]._private.rscratch.imgLayerCaches;
28057 if (caches) {
28058 caches[lvl] = null;
28059 }
28060 }
28061};
28062LTCp.refineElementTextures = function (eles) {
28063 var self = this;
28064
28065 // log('refine', eles.length);
28066
28067 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28068 var rLyr = layer.replacement;
28069 if (!rLyr) {
28070 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28071 rLyr.replaces = layer;
28072 rLyr.eles = layer.eles;
28073
28074 // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28075 }
28076
28077 if (!rLyr.reqs) {
28078 for (var i = 0; i < rLyr.eles.length; i++) {
28079 self.queueLayer(rLyr, rLyr.eles[i]);
28080 }
28081
28082 // log('queue replacement layer refinement', rLyr.id);
28083 }
28084 });
28085};
28086
28087LTCp.enqueueElementRefinement = function (ele) {
28088 this.eleTxrDeqs.merge(ele);
28089 this.scheduleElementRefinement();
28090};
28091LTCp.queueLayer = function (layer, ele) {
28092 var self = this;
28093 var q = self.layersQueue;
28094 var elesQ = layer.elesQueue;
28095 var hasId = elesQ.hasId = elesQ.hasId || {};
28096
28097 // if a layer is going to be replaced, queuing is a waste of time
28098 if (layer.replacement) {
28099 return;
28100 }
28101 if (ele) {
28102 if (hasId[ele.id()]) {
28103 return;
28104 }
28105 elesQ.push(ele);
28106 hasId[ele.id()] = true;
28107 }
28108 if (layer.reqs) {
28109 layer.reqs++;
28110 q.updateItem(layer);
28111 } else {
28112 layer.reqs = 1;
28113 q.push(layer);
28114 }
28115};
28116LTCp.dequeue = function (pxRatio) {
28117 var self = this;
28118 var q = self.layersQueue;
28119 var deqd = [];
28120 var eleDeqs = 0;
28121 while (eleDeqs < maxDeqSize) {
28122 if (q.size() === 0) {
28123 break;
28124 }
28125 var layer = q.peek();
28126
28127 // if a layer has been or will be replaced, then don't waste time with it
28128 if (layer.replacement) {
28129 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28130 q.pop();
28131 continue;
28132 }
28133
28134 // if this is a replacement layer that has been superceded, then forget it
28135 if (layer.replaces && layer !== layer.replaces.replacement) {
28136 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28137 q.pop();
28138 continue;
28139 }
28140 if (layer.invalid) {
28141 // log('replacement layer %s is invalid; dequeued', layer.id);
28142 q.pop();
28143 continue;
28144 }
28145 var ele = layer.elesQueue.shift();
28146 if (ele) {
28147 // log('dequeue layer %s', layer.id);
28148
28149 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28150 eleDeqs++;
28151 }
28152 if (deqd.length === 0) {
28153 // we need only one entry in deqd to queue redrawing etc
28154 deqd.push(true);
28155 }
28156
28157 // if the layer has all its eles done, then remove from the queue
28158 if (layer.elesQueue.length === 0) {
28159 q.pop();
28160 layer.reqs = 0;
28161
28162 // log('dequeue of layer %s complete', layer.id);
28163
28164 // when a replacement layer is dequeued, it replaces the old layer in the level
28165 if (layer.replaces) {
28166 self.applyLayerReplacement(layer);
28167 }
28168 self.requestRedraw();
28169 }
28170 }
28171 return deqd;
28172};
28173LTCp.applyLayerReplacement = function (layer) {
28174 var self = this;
28175 var layersInLevel = self.layersByLevel[layer.level];
28176 var replaced = layer.replaces;
28177 var index = layersInLevel.indexOf(replaced);
28178
28179 // if the replaced layer is not in the active list for the level, then replacing
28180 // refs would be a mistake (i.e. overwriting the true active layer)
28181 if (index < 0 || replaced.invalid) {
28182 // log('replacement layer would have no effect', layer.id);
28183 return;
28184 }
28185 layersInLevel[index] = layer; // replace level ref
28186
28187 // replace refs in eles
28188 for (var i = 0; i < layer.eles.length; i++) {
28189 var _p = layer.eles[i]._private;
28190 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28191 if (cache) {
28192 cache[layer.level] = layer;
28193 }
28194 }
28195
28196 // log('apply replacement layer %s over %s', layer.id, replaced.id);
28197
28198 self.requestRedraw();
28199};
28200LTCp.requestRedraw = debounce_1(function () {
28201 var r = this.renderer;
28202 r.redrawHint('eles', true);
28203 r.redrawHint('drag', true);
28204 r.redraw();
28205}, 100);
28206LTCp.setupDequeueing = defs.setupDequeueing({
28207 deqRedrawThreshold: deqRedrawThreshold,
28208 deqCost: deqCost,
28209 deqAvgCost: deqAvgCost,
28210 deqNoDrawCost: deqNoDrawCost,
28211 deqFastCost: deqFastCost,
28212 deq: function deq(self, pxRatio) {
28213 return self.dequeue(pxRatio);
28214 },
28215 onDeqd: noop$1,
28216 shouldRedraw: trueify,
28217 priority: function priority(self) {
28218 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28219 }
28220});
28221
28222var CRp$a = {};
28223var impl;
28224function polygon(context, points) {
28225 for (var i = 0; i < points.length; i++) {
28226 var pt = points[i];
28227 context.lineTo(pt.x, pt.y);
28228 }
28229}
28230function triangleBackcurve(context, points, controlPoint) {
28231 var firstPt;
28232 for (var i = 0; i < points.length; i++) {
28233 var pt = points[i];
28234 if (i === 0) {
28235 firstPt = pt;
28236 }
28237 context.lineTo(pt.x, pt.y);
28238 }
28239 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28240}
28241function triangleTee(context, trianglePoints, teePoints) {
28242 if (context.beginPath) {
28243 context.beginPath();
28244 }
28245 var triPts = trianglePoints;
28246 for (var i = 0; i < triPts.length; i++) {
28247 var pt = triPts[i];
28248 context.lineTo(pt.x, pt.y);
28249 }
28250 var teePts = teePoints;
28251 var firstTeePt = teePoints[0];
28252 context.moveTo(firstTeePt.x, firstTeePt.y);
28253 for (var i = 1; i < teePts.length; i++) {
28254 var pt = teePts[i];
28255 context.lineTo(pt.x, pt.y);
28256 }
28257 if (context.closePath) {
28258 context.closePath();
28259 }
28260}
28261function circleTriangle(context, trianglePoints, rx, ry, r) {
28262 if (context.beginPath) {
28263 context.beginPath();
28264 }
28265 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28266 var triPts = trianglePoints;
28267 var firstTrPt = triPts[0];
28268 context.moveTo(firstTrPt.x, firstTrPt.y);
28269 for (var i = 0; i < triPts.length; i++) {
28270 var pt = triPts[i];
28271 context.lineTo(pt.x, pt.y);
28272 }
28273 if (context.closePath) {
28274 context.closePath();
28275 }
28276}
28277function circle(context, rx, ry, r) {
28278 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28279}
28280CRp$a.arrowShapeImpl = function (name) {
28281 return (impl || (impl = {
28282 'polygon': polygon,
28283 'triangle-backcurve': triangleBackcurve,
28284 'triangle-tee': triangleTee,
28285 'circle-triangle': circleTriangle,
28286 'triangle-cross': triangleTee,
28287 'circle': circle
28288 }))[name];
28289};
28290
28291var CRp$9 = {};
28292CRp$9.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28293 var r = this;
28294 if (ele.isNode()) {
28295 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28296 } else {
28297 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28298 }
28299};
28300CRp$9.drawElementOverlay = function (context, ele) {
28301 var r = this;
28302 if (ele.isNode()) {
28303 r.drawNodeOverlay(context, ele);
28304 } else {
28305 r.drawEdgeOverlay(context, ele);
28306 }
28307};
28308CRp$9.drawElementUnderlay = function (context, ele) {
28309 var r = this;
28310 if (ele.isNode()) {
28311 r.drawNodeUnderlay(context, ele);
28312 } else {
28313 r.drawEdgeUnderlay(context, ele);
28314 }
28315};
28316CRp$9.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28317 var r = this;
28318 var bb = eleTxrCache.getBoundingBox(ele);
28319 if (bb.w === 0 || bb.h === 0) {
28320 return;
28321 } // ignore zero size case
28322
28323 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28324 if (eleCache != null) {
28325 var opacity = getOpacity(r, ele);
28326 if (opacity === 0) {
28327 return;
28328 }
28329 var theta = getRotation(r, ele);
28330 var x1 = bb.x1,
28331 y1 = bb.y1,
28332 w = bb.w,
28333 h = bb.h;
28334 var x, y, sx, sy, smooth;
28335 if (theta !== 0) {
28336 var rotPt = eleTxrCache.getRotationPoint(ele);
28337 sx = rotPt.x;
28338 sy = rotPt.y;
28339 context.translate(sx, sy);
28340 context.rotate(theta);
28341 smooth = r.getImgSmoothing(context);
28342 if (!smooth) {
28343 r.setImgSmoothing(context, true);
28344 }
28345 var off = eleTxrCache.getRotationOffset(ele);
28346 x = off.x;
28347 y = off.y;
28348 } else {
28349 x = x1;
28350 y = y1;
28351 }
28352 var oldGlobalAlpha;
28353 if (opacity !== 1) {
28354 oldGlobalAlpha = context.globalAlpha;
28355 context.globalAlpha = oldGlobalAlpha * opacity;
28356 }
28357 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28358 if (opacity !== 1) {
28359 context.globalAlpha = oldGlobalAlpha;
28360 }
28361 if (theta !== 0) {
28362 context.rotate(-theta);
28363 context.translate(-sx, -sy);
28364 if (!smooth) {
28365 r.setImgSmoothing(context, false);
28366 }
28367 }
28368 } else {
28369 eleTxrCache.drawElement(context, ele); // direct draw fallback
28370 }
28371};
28372
28373var getZeroRotation = function getZeroRotation() {
28374 return 0;
28375};
28376var getLabelRotation = function getLabelRotation(r, ele) {
28377 return r.getTextAngle(ele, null);
28378};
28379var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28380 return r.getTextAngle(ele, 'source');
28381};
28382var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28383 return r.getTextAngle(ele, 'target');
28384};
28385var getOpacity = function getOpacity(r, ele) {
28386 return ele.effectiveOpacity();
28387};
28388var getTextOpacity = function getTextOpacity(e, ele) {
28389 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28390};
28391CRp$9.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28392 var r = this;
28393 var _r$data = r.data,
28394 eleTxrCache = _r$data.eleTxrCache,
28395 lblTxrCache = _r$data.lblTxrCache,
28396 slbTxrCache = _r$data.slbTxrCache,
28397 tlbTxrCache = _r$data.tlbTxrCache;
28398 var bb = ele.boundingBox();
28399 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28400 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28401 return;
28402 }
28403 if (!extent || boundingBoxesIntersect(bb, extent)) {
28404 var isEdge = ele.isEdge();
28405 var badLine = ele.element()._private.rscratch.badLine;
28406 r.drawElementUnderlay(context, ele);
28407 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28408 if (!isEdge || !badLine) {
28409 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28410 }
28411 if (isEdge && !badLine) {
28412 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28413 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28414 }
28415 r.drawElementOverlay(context, ele);
28416 }
28417};
28418CRp$9.drawElements = function (context, eles) {
28419 var r = this;
28420 for (var i = 0; i < eles.length; i++) {
28421 var ele = eles[i];
28422 r.drawElement(context, ele);
28423 }
28424};
28425CRp$9.drawCachedElements = function (context, eles, pxRatio, extent) {
28426 var r = this;
28427 for (var i = 0; i < eles.length; i++) {
28428 var ele = eles[i];
28429 r.drawCachedElement(context, ele, pxRatio, extent);
28430 }
28431};
28432CRp$9.drawCachedNodes = function (context, eles, pxRatio, extent) {
28433 var r = this;
28434 for (var i = 0; i < eles.length; i++) {
28435 var ele = eles[i];
28436 if (!ele.isNode()) {
28437 continue;
28438 }
28439 r.drawCachedElement(context, ele, pxRatio, extent);
28440 }
28441};
28442CRp$9.drawLayeredElements = function (context, eles, pxRatio, extent) {
28443 var r = this;
28444 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28445 if (layers) {
28446 for (var i = 0; i < layers.length; i++) {
28447 var layer = layers[i];
28448 var bb = layer.bb;
28449 if (bb.w === 0 || bb.h === 0) {
28450 continue;
28451 }
28452 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28453 }
28454 } else {
28455 // fall back on plain caching if no layers
28456 r.drawCachedElements(context, eles, pxRatio, extent);
28457 }
28458};
28459
28460var CRp$8 = {};
28461CRp$8.drawEdge = function (context, edge, shiftToOriginWithBb) {
28462 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28463 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28464 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28465 var r = this;
28466 var rs = edge._private.rscratch;
28467 if (shouldDrawOpacity && !edge.visible()) {
28468 return;
28469 }
28470
28471 // if bezier ctrl pts can not be calculated, then die
28472 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28473 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28474 return;
28475 }
28476 var bb;
28477 if (shiftToOriginWithBb) {
28478 bb = shiftToOriginWithBb;
28479 context.translate(-bb.x1, -bb.y1);
28480 }
28481 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28482 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28483 var curveStyle = edge.pstyle('curve-style').value;
28484 var lineStyle = edge.pstyle('line-style').value;
28485 var edgeWidth = edge.pstyle('width').pfValue;
28486 var lineCap = edge.pstyle('line-cap').value;
28487 var lineOutlineWidth = edge.pstyle('line-outline-width').value;
28488 var lineOutlineColor = edge.pstyle('line-outline-color').value;
28489 var effectiveLineOpacity = opacity * lineOpacity;
28490 // separate arrow opacity would require arrow-opacity property
28491 var effectiveArrowOpacity = opacity * lineOpacity;
28492 var drawLine = function drawLine() {
28493 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28494 if (curveStyle === 'straight-triangle') {
28495 r.eleStrokeStyle(context, edge, strokeOpacity);
28496 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28497 } else {
28498 context.lineWidth = edgeWidth;
28499 context.lineCap = lineCap;
28500 r.eleStrokeStyle(context, edge, strokeOpacity);
28501 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28502 context.lineCap = 'butt'; // reset for other drawing functions
28503 }
28504 };
28505
28506 var drawLineOutline = function drawLineOutline() {
28507 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28508 context.lineWidth = edgeWidth + lineOutlineWidth;
28509 context.lineCap = lineCap;
28510 if (lineOutlineWidth > 0) {
28511 r.colorStrokeStyle(context, lineOutlineColor[0], lineOutlineColor[1], lineOutlineColor[2], strokeOpacity);
28512 } else {
28513 // do not draw any lineOutline
28514 context.lineCap = 'butt'; // reset for other drawing functions
28515 return;
28516 }
28517 if (curveStyle === 'straight-triangle') {
28518 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28519 } else {
28520 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28521 context.lineCap = 'butt'; // reset for other drawing functions
28522 }
28523 };
28524
28525 var drawOverlay = function drawOverlay() {
28526 if (!shouldDrawOverlay) {
28527 return;
28528 }
28529 r.drawEdgeOverlay(context, edge);
28530 };
28531 var drawUnderlay = function drawUnderlay() {
28532 if (!shouldDrawOverlay) {
28533 return;
28534 }
28535 r.drawEdgeUnderlay(context, edge);
28536 };
28537 var drawArrows = function drawArrows() {
28538 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28539 r.drawArrowheads(context, edge, arrowOpacity);
28540 };
28541 var drawText = function drawText() {
28542 r.drawElementText(context, edge, null, drawLabel);
28543 };
28544 context.lineJoin = 'round';
28545 var ghost = edge.pstyle('ghost').value === 'yes';
28546 if (ghost) {
28547 var gx = edge.pstyle('ghost-offset-x').pfValue;
28548 var gy = edge.pstyle('ghost-offset-y').pfValue;
28549 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28550 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28551 context.translate(gx, gy);
28552 drawLine(effectiveGhostOpacity);
28553 drawArrows(effectiveGhostOpacity);
28554 context.translate(-gx, -gy);
28555 } else {
28556 drawLineOutline();
28557 }
28558 drawUnderlay();
28559 drawLine();
28560 drawArrows();
28561 drawOverlay();
28562 drawText();
28563 if (shiftToOriginWithBb) {
28564 context.translate(bb.x1, bb.y1);
28565 }
28566};
28567var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28568 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28569 throw new Error('Invalid state');
28570 }
28571 return function (context, edge) {
28572 if (!edge.visible()) {
28573 return;
28574 }
28575 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28576 if (opacity === 0) {
28577 return;
28578 }
28579 var r = this;
28580 var usePaths = r.usePaths();
28581 var rs = edge._private.rscratch;
28582 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28583 var width = 2 * padding;
28584 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28585 context.lineWidth = width;
28586 if (rs.edgeType === 'self' && !usePaths) {
28587 context.lineCap = 'butt';
28588 } else {
28589 context.lineCap = 'round';
28590 }
28591 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28592 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28593 };
28594};
28595CRp$8.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
28596CRp$8.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
28597CRp$8.drawEdgePath = function (edge, context, pts, type) {
28598 var rs = edge._private.rscratch;
28599 var canvasCxt = context;
28600 var path;
28601 var pathCacheHit = false;
28602 var usePaths = this.usePaths();
28603 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28604 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28605 if (usePaths) {
28606 var pathCacheKey = pts.join('$');
28607 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28608 if (keyMatches) {
28609 path = context = rs.pathCache;
28610 pathCacheHit = true;
28611 } else {
28612 path = context = new Path2D();
28613 rs.pathCacheKey = pathCacheKey;
28614 rs.pathCache = path;
28615 }
28616 }
28617 if (canvasCxt.setLineDash) {
28618 // for very outofdate browsers
28619 switch (type) {
28620 case 'dotted':
28621 canvasCxt.setLineDash([1, 1]);
28622 break;
28623 case 'dashed':
28624 canvasCxt.setLineDash(lineDashPattern);
28625 canvasCxt.lineDashOffset = lineDashOffset;
28626 break;
28627 case 'solid':
28628 canvasCxt.setLineDash([]);
28629 break;
28630 }
28631 }
28632 if (!pathCacheHit && !rs.badLine) {
28633 if (context.beginPath) {
28634 context.beginPath();
28635 }
28636 context.moveTo(pts[0], pts[1]);
28637 switch (rs.edgeType) {
28638 case 'bezier':
28639 case 'self':
28640 case 'compound':
28641 case 'multibezier':
28642 for (var i = 2; i + 3 < pts.length; i += 4) {
28643 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28644 }
28645 break;
28646 case 'straight':
28647 case 'haystack':
28648 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28649 context.lineTo(pts[_i], pts[_i + 1]);
28650 }
28651 break;
28652 case 'segments':
28653 if (rs.isRound) {
28654 var _iterator = _createForOfIteratorHelper(rs.roundCorners),
28655 _step;
28656 try {
28657 for (_iterator.s(); !(_step = _iterator.n()).done;) {
28658 var corner = _step.value;
28659 drawPreparedRoundCorner(context, corner);
28660 }
28661 } catch (err) {
28662 _iterator.e(err);
28663 } finally {
28664 _iterator.f();
28665 }
28666 context.lineTo(pts[pts.length - 2], pts[pts.length - 1]);
28667 } else {
28668 for (var _i2 = 2; _i2 + 1 < pts.length; _i2 += 2) {
28669 context.lineTo(pts[_i2], pts[_i2 + 1]);
28670 }
28671 }
28672 break;
28673 }
28674 }
28675 context = canvasCxt;
28676 if (usePaths) {
28677 context.stroke(path);
28678 } else {
28679 context.stroke();
28680 }
28681
28682 // reset any line dashes
28683 if (context.setLineDash) {
28684 // for very outofdate browsers
28685 context.setLineDash([]);
28686 }
28687};
28688CRp$8.drawEdgeTrianglePath = function (edge, context, pts) {
28689 // use line stroke style for triangle fill style
28690 context.fillStyle = context.strokeStyle;
28691 var edgeWidth = edge.pstyle('width').pfValue;
28692 for (var i = 0; i + 1 < pts.length; i += 2) {
28693 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
28694 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
28695 var normal = [vector[1] / length, -vector[0] / length];
28696 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
28697 context.beginPath();
28698 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
28699 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
28700 context.lineTo(pts[i + 2], pts[i + 3]);
28701 context.closePath();
28702 context.fill();
28703 }
28704};
28705CRp$8.drawArrowheads = function (context, edge, opacity) {
28706 var rs = edge._private.rscratch;
28707 var isHaystack = rs.edgeType === 'haystack';
28708 if (!isHaystack) {
28709 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28710 }
28711 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28712 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28713 if (!isHaystack) {
28714 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28715 }
28716};
28717CRp$8.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28718 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28719 return;
28720 }
28721 var self = this;
28722 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28723 if (arrowShape === 'none') {
28724 return;
28725 }
28726 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28727 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28728 var edgeWidth = edge.pstyle('width').pfValue;
28729 var pArrowWidth = edge.pstyle(prefix + '-arrow-width');
28730 var arrowWidth = pArrowWidth.value === 'match-line' ? edgeWidth : pArrowWidth.pfValue;
28731 if (pArrowWidth.units === '%') arrowWidth *= edgeWidth;
28732 var edgeOpacity = edge.pstyle('opacity').value;
28733 if (opacity === undefined) {
28734 opacity = edgeOpacity;
28735 }
28736 var gco = context.globalCompositeOperation;
28737 if (opacity !== 1 || arrowFill === 'hollow') {
28738 // then extra clear is needed
28739 context.globalCompositeOperation = 'destination-out';
28740 self.colorFillStyle(context, 255, 255, 255, 1);
28741 self.colorStrokeStyle(context, 255, 255, 255, 1);
28742 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, arrowWidth, x, y, angle);
28743 context.globalCompositeOperation = gco;
28744 } // otherwise, the opaque arrow clears it for free :)
28745
28746 var color = edge.pstyle(prefix + '-arrow-color').value;
28747 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28748 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28749 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, arrowWidth, x, y, angle);
28750};
28751CRp$8.drawArrowShape = function (edge, context, fill, edgeWidth, shape, shapeWidth, x, y, angle) {
28752 var r = this;
28753 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28754 var pathCacheHit = false;
28755 var path;
28756 var canvasContext = context;
28757 var translation = {
28758 x: x,
28759 y: y
28760 };
28761 var scale = edge.pstyle('arrow-scale').value;
28762 var size = this.getArrowWidth(edgeWidth, scale);
28763 var shapeImpl = r.arrowShapes[shape];
28764 if (usePaths) {
28765 var cache = r.arrowPathCache = r.arrowPathCache || [];
28766 var key = hashString(shape);
28767 var cachedPath = cache[key];
28768 if (cachedPath != null) {
28769 path = context = cachedPath;
28770 pathCacheHit = true;
28771 } else {
28772 path = context = new Path2D();
28773 cache[key] = path;
28774 }
28775 }
28776 if (!pathCacheHit) {
28777 if (context.beginPath) {
28778 context.beginPath();
28779 }
28780 if (usePaths) {
28781 // store in the path cache with values easily manipulated later
28782 shapeImpl.draw(context, 1, 0, {
28783 x: 0,
28784 y: 0
28785 }, 1);
28786 } else {
28787 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28788 }
28789 if (context.closePath) {
28790 context.closePath();
28791 }
28792 }
28793 context = canvasContext;
28794 if (usePaths) {
28795 // set transform to arrow position/orientation
28796 context.translate(x, y);
28797 context.rotate(angle);
28798 context.scale(size, size);
28799 }
28800 if (fill === 'filled' || fill === 'both') {
28801 if (usePaths) {
28802 context.fill(path);
28803 } else {
28804 context.fill();
28805 }
28806 }
28807 if (fill === 'hollow' || fill === 'both') {
28808 context.lineWidth = shapeWidth / (usePaths ? size : 1);
28809 context.lineJoin = 'miter';
28810 if (usePaths) {
28811 context.stroke(path);
28812 } else {
28813 context.stroke();
28814 }
28815 }
28816 if (usePaths) {
28817 // reset transform by applying inverse
28818 context.scale(1 / size, 1 / size);
28819 context.rotate(-angle);
28820 context.translate(-x, -y);
28821 }
28822};
28823
28824var CRp$7 = {};
28825CRp$7.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28826 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28827 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28828 return;
28829 }
28830 try {
28831 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28832 } catch (e) {
28833 warn(e);
28834 }
28835};
28836CRp$7.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28837 var r = this;
28838 var pos = node.position();
28839 var nodeX = pos.x;
28840 var nodeY = pos.y;
28841 var styleObj = node.cy().style();
28842 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28843 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28844 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28845 var nodeW = node.width();
28846 var nodeH = node.height();
28847 var paddingX2 = node.padding() * 2;
28848 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28849 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28850 var rs = node._private.rscratch;
28851 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28852 var shouldClip = clip === 'node';
28853 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28854 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
28855 var cornerRadius = node.pstyle('corner-radius').value;
28856 if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
28857 var imgW = img.width || img.cachedW;
28858 var imgH = img.height || img.cachedH;
28859
28860 // workaround for broken browsers like ie
28861 if (null == imgW || null == imgH) {
28862 document.body.appendChild(img); // eslint-disable-line no-undef
28863
28864 imgW = img.cachedW = img.width || img.offsetWidth;
28865 imgH = img.cachedH = img.height || img.offsetHeight;
28866 document.body.removeChild(img); // eslint-disable-line no-undef
28867 }
28868
28869 var w = imgW;
28870 var h = imgH;
28871 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
28872 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
28873 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
28874 } else {
28875 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
28876 }
28877 }
28878 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
28879 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
28880 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
28881 } else {
28882 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
28883 }
28884 }
28885 if (w === 0 || h === 0) {
28886 return; // no point in drawing empty image (and chrome is broken in this case)
28887 }
28888
28889 if (fit === 'contain') {
28890 var scale = Math.min(nodeTW / w, nodeTH / h);
28891 w *= scale;
28892 h *= scale;
28893 } else if (fit === 'cover') {
28894 var scale = Math.max(nodeTW / w, nodeTH / h);
28895 w *= scale;
28896 h *= scale;
28897 }
28898 var x = nodeX - nodeTW / 2; // left
28899 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
28900 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
28901 if (posXUnits === '%') {
28902 x += (nodeTW - w) * posXPfVal;
28903 } else {
28904 x += posXPfVal;
28905 }
28906 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
28907 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
28908 if (offXUnits === '%') {
28909 x += (nodeTW - w) * offXPfVal;
28910 } else {
28911 x += offXPfVal;
28912 }
28913 var y = nodeY - nodeTH / 2; // top
28914 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
28915 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
28916 if (posYUnits === '%') {
28917 y += (nodeTH - h) * posYPfVal;
28918 } else {
28919 y += posYPfVal;
28920 }
28921 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
28922 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
28923 if (offYUnits === '%') {
28924 y += (nodeTH - h) * offYPfVal;
28925 } else {
28926 y += offYPfVal;
28927 }
28928 if (rs.pathCache) {
28929 x -= nodeX;
28930 y -= nodeY;
28931 nodeX = 0;
28932 nodeY = 0;
28933 }
28934 var gAlpha = context.globalAlpha;
28935 context.globalAlpha = imgOpacity;
28936 var smoothingEnabled = r.getImgSmoothing(context);
28937 var isSmoothingSwitched = false;
28938 if (smooth === 'no' && smoothingEnabled) {
28939 r.setImgSmoothing(context, false);
28940 isSmoothingSwitched = true;
28941 } else if (smooth === 'yes' && !smoothingEnabled) {
28942 r.setImgSmoothing(context, true);
28943 isSmoothingSwitched = true;
28944 }
28945 if (repeat === 'no-repeat') {
28946 if (shouldClip) {
28947 context.save();
28948 if (rs.pathCache) {
28949 context.clip(rs.pathCache);
28950 } else {
28951 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH, cornerRadius, rs);
28952 context.clip();
28953 }
28954 }
28955 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
28956 if (shouldClip) {
28957 context.restore();
28958 }
28959 } else {
28960 var pattern = context.createPattern(img, repeat);
28961 context.fillStyle = pattern;
28962 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH, cornerRadius, rs);
28963 context.translate(x, y);
28964 context.fill();
28965 context.translate(-x, -y);
28966 }
28967 context.globalAlpha = gAlpha;
28968 if (isSmoothingSwitched) {
28969 r.setImgSmoothing(context, smoothingEnabled);
28970 }
28971};
28972
28973var CRp$6 = {};
28974CRp$6.eleTextBiggerThanMin = function (ele, scale) {
28975 if (!scale) {
28976 var zoom = ele.cy().zoom();
28977 var pxRatio = this.getPixelRatio();
28978 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
28979
28980 scale = Math.pow(2, lvl);
28981 }
28982 var computedSize = ele.pstyle('font-size').pfValue * scale;
28983 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
28984 if (computedSize < minSize) {
28985 return false;
28986 }
28987 return true;
28988};
28989CRp$6.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
28990 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28991 var r = this;
28992 if (force == null) {
28993 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
28994 return;
28995 }
28996 } else if (force === false) {
28997 return;
28998 }
28999 if (ele.isNode()) {
29000 var label = ele.pstyle('label');
29001 if (!label || !label.value) {
29002 return;
29003 }
29004 var justification = r.getLabelJustification(ele);
29005 context.textAlign = justification;
29006 context.textBaseline = 'bottom';
29007 } else {
29008 var badLine = ele.element()._private.rscratch.badLine;
29009 var _label = ele.pstyle('label');
29010 var srcLabel = ele.pstyle('source-label');
29011 var tgtLabel = ele.pstyle('target-label');
29012 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29013 return;
29014 }
29015 context.textAlign = 'center';
29016 context.textBaseline = 'bottom';
29017 }
29018 var applyRotation = !shiftToOriginWithBb;
29019 var bb;
29020 if (shiftToOriginWithBb) {
29021 bb = shiftToOriginWithBb;
29022 context.translate(-bb.x1, -bb.y1);
29023 }
29024 if (prefix == null) {
29025 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29026 if (ele.isEdge()) {
29027 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29028 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29029 }
29030 } else {
29031 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29032 }
29033 if (shiftToOriginWithBb) {
29034 context.translate(bb.x1, bb.y1);
29035 }
29036};
29037CRp$6.getFontCache = function (context) {
29038 var cache;
29039 this.fontCaches = this.fontCaches || [];
29040 for (var i = 0; i < this.fontCaches.length; i++) {
29041 cache = this.fontCaches[i];
29042 if (cache.context === context) {
29043 return cache;
29044 }
29045 }
29046 cache = {
29047 context: context
29048 };
29049 this.fontCaches.push(cache);
29050 return cache;
29051};
29052
29053// set up canvas context with font
29054// returns transformed text string
29055CRp$6.setupTextStyle = function (context, ele) {
29056 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29057 // Font style
29058 var labelStyle = ele.pstyle('font-style').strValue;
29059 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29060 var labelFamily = ele.pstyle('font-family').strValue;
29061 var labelWeight = ele.pstyle('font-weight').strValue;
29062 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29063 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29064 var color = ele.pstyle('color').value;
29065 var outlineColor = ele.pstyle('text-outline-color').value;
29066 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29067 context.lineJoin = 'round'; // so text outlines aren't jagged
29068
29069 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29070 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29071};
29072
29073// TODO ensure re-used
29074function roundRect(ctx, x, y, width, height) {
29075 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29076 var stroke = arguments.length > 6 ? arguments[6] : undefined;
29077 ctx.beginPath();
29078 ctx.moveTo(x + radius, y);
29079 ctx.lineTo(x + width - radius, y);
29080 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29081 ctx.lineTo(x + width, y + height - radius);
29082 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29083 ctx.lineTo(x + radius, y + height);
29084 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29085 ctx.lineTo(x, y + radius);
29086 ctx.quadraticCurveTo(x, y, x + radius, y);
29087 ctx.closePath();
29088 if (stroke) ctx.stroke();else ctx.fill();
29089}
29090CRp$6.getTextAngle = function (ele, prefix) {
29091 var theta;
29092 var _p = ele._private;
29093 var rscratch = _p.rscratch;
29094 var pdash = prefix ? prefix + '-' : '';
29095 var rotation = ele.pstyle(pdash + 'text-rotation');
29096 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29097 if (rotation.strValue === 'autorotate') {
29098 theta = ele.isEdge() ? textAngle : 0;
29099 } else if (rotation.strValue === 'none') {
29100 theta = 0;
29101 } else {
29102 theta = rotation.pfValue;
29103 }
29104 return theta;
29105};
29106CRp$6.drawText = function (context, ele, prefix) {
29107 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29108 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29109 var _p = ele._private;
29110 var rscratch = _p.rscratch;
29111 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29112 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29113 return;
29114 }
29115
29116 // use 'main' as an alias for the main label (i.e. null prefix)
29117 if (prefix === 'main') {
29118 prefix = null;
29119 }
29120 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29121 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29122 var orgTextX, orgTextY; // used for rotation
29123 var text = this.getLabelText(ele, prefix);
29124 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29125 this.setupTextStyle(context, ele, useEleOpacity);
29126 var pdash = prefix ? prefix + '-' : '';
29127 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29128 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29129 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29130 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29131 var isEdge = ele.isEdge();
29132 var halign = ele.pstyle('text-halign').value;
29133 var valign = ele.pstyle('text-valign').value;
29134 if (isEdge) {
29135 halign = 'center';
29136 valign = 'center';
29137 }
29138 textX += marginX;
29139 textY += marginY;
29140 var theta;
29141 if (!applyRotation) {
29142 theta = 0;
29143 } else {
29144 theta = this.getTextAngle(ele, prefix);
29145 }
29146 if (theta !== 0) {
29147 orgTextX = textX;
29148 orgTextY = textY;
29149 context.translate(orgTextX, orgTextY);
29150 context.rotate(theta);
29151 textX = 0;
29152 textY = 0;
29153 }
29154 switch (valign) {
29155 case 'top':
29156 break;
29157 case 'center':
29158 textY += textH / 2;
29159 break;
29160 case 'bottom':
29161 textY += textH;
29162 break;
29163 }
29164 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29165 var borderOpacity = ele.pstyle('text-border-opacity').value;
29166 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29167 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29168 var styleShape = ele.pstyle('text-background-shape').strValue;
29169 var rounded = styleShape.indexOf('round') === 0;
29170 var roundRadius = 2;
29171 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29172 var bgX = textX - backgroundPadding;
29173 switch (halign) {
29174 case 'left':
29175 bgX -= textW;
29176 break;
29177 case 'center':
29178 bgX -= textW / 2;
29179 break;
29180 }
29181 var bgY = textY - textH - backgroundPadding;
29182 var bgW = textW + 2 * backgroundPadding;
29183 var bgH = textH + 2 * backgroundPadding;
29184 if (backgroundOpacity > 0) {
29185 var textFill = context.fillStyle;
29186 var textBackgroundColor = ele.pstyle('text-background-color').value;
29187 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29188 if (rounded) {
29189 roundRect(context, bgX, bgY, bgW, bgH, roundRadius);
29190 } else {
29191 context.fillRect(bgX, bgY, bgW, bgH);
29192 }
29193 context.fillStyle = textFill;
29194 }
29195 if (textBorderWidth > 0 && borderOpacity > 0) {
29196 var textStroke = context.strokeStyle;
29197 var textLineWidth = context.lineWidth;
29198 var textBorderColor = ele.pstyle('text-border-color').value;
29199 var textBorderStyle = ele.pstyle('text-border-style').value;
29200 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29201 context.lineWidth = textBorderWidth;
29202 if (context.setLineDash) {
29203 // for very outofdate browsers
29204 switch (textBorderStyle) {
29205 case 'dotted':
29206 context.setLineDash([1, 1]);
29207 break;
29208 case 'dashed':
29209 context.setLineDash([4, 2]);
29210 break;
29211 case 'double':
29212 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29213 context.setLineDash([]);
29214 break;
29215 case 'solid':
29216 context.setLineDash([]);
29217 break;
29218 }
29219 }
29220 if (rounded) {
29221 roundRect(context, bgX, bgY, bgW, bgH, roundRadius, 'stroke');
29222 } else {
29223 context.strokeRect(bgX, bgY, bgW, bgH);
29224 }
29225 if (textBorderStyle === 'double') {
29226 var whiteWidth = textBorderWidth / 2;
29227 if (rounded) {
29228 roundRect(context, bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2, roundRadius, 'stroke');
29229 } else {
29230 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29231 }
29232 }
29233 if (context.setLineDash) {
29234 // for very outofdate browsers
29235 context.setLineDash([]);
29236 }
29237 context.lineWidth = textLineWidth;
29238 context.strokeStyle = textStroke;
29239 }
29240 }
29241 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29242
29243 if (lineWidth > 0) {
29244 context.lineWidth = lineWidth;
29245 }
29246 if (ele.pstyle('text-wrap').value === 'wrap') {
29247 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29248 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29249 var halfTextW = textW / 2;
29250 var justification = this.getLabelJustification(ele);
29251 if (justification === 'auto') ; else if (halign === 'left') {
29252 // auto justification : right
29253 if (justification === 'left') {
29254 textX += -textW;
29255 } else if (justification === 'center') {
29256 textX += -halfTextW;
29257 } // else same as auto
29258 } else if (halign === 'center') {
29259 // auto justfication : center
29260 if (justification === 'left') {
29261 textX += -halfTextW;
29262 } else if (justification === 'right') {
29263 textX += halfTextW;
29264 } // else same as auto
29265 } else if (halign === 'right') {
29266 // auto justification : left
29267 if (justification === 'center') {
29268 textX += halfTextW;
29269 } else if (justification === 'right') {
29270 textX += textW;
29271 } // else same as auto
29272 }
29273
29274 switch (valign) {
29275 case 'top':
29276 textY -= (lines.length - 1) * lineHeight;
29277 break;
29278 case 'center':
29279 case 'bottom':
29280 textY -= (lines.length - 1) * lineHeight;
29281 break;
29282 }
29283 for (var l = 0; l < lines.length; l++) {
29284 if (lineWidth > 0) {
29285 context.strokeText(lines[l], textX, textY);
29286 }
29287 context.fillText(lines[l], textX, textY);
29288 textY += lineHeight;
29289 }
29290 } else {
29291 if (lineWidth > 0) {
29292 context.strokeText(text, textX, textY);
29293 }
29294 context.fillText(text, textX, textY);
29295 }
29296 if (theta !== 0) {
29297 context.rotate(-theta);
29298 context.translate(-orgTextX, -orgTextY);
29299 }
29300 }
29301};
29302
29303/* global Path2D */
29304var CRp$5 = {};
29305CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29306 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29307 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29308 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29309 var r = this;
29310 var nodeWidth, nodeHeight;
29311 var _p = node._private;
29312 var rs = _p.rscratch;
29313 var pos = node.position();
29314 if (!number$1(pos.x) || !number$1(pos.y)) {
29315 return; // can't draw node with undefined position
29316 }
29317
29318 if (shouldDrawOpacity && !node.visible()) {
29319 return;
29320 }
29321 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29322 var usePaths = r.usePaths();
29323 var path;
29324 var pathCacheHit = false;
29325 var padding = node.padding();
29326 nodeWidth = node.width() + 2 * padding;
29327 nodeHeight = node.height() + 2 * padding;
29328
29329 //
29330 // setup shift
29331
29332 var bb;
29333 if (shiftToOriginWithBb) {
29334 bb = shiftToOriginWithBb;
29335 context.translate(-bb.x1, -bb.y1);
29336 }
29337
29338 //
29339 // load bg image
29340
29341 var bgImgProp = node.pstyle('background-image');
29342 var urls = bgImgProp.value;
29343 var urlDefined = new Array(urls.length);
29344 var image = new Array(urls.length);
29345 var numImages = 0;
29346 for (var i = 0; i < urls.length; i++) {
29347 var url = urls[i];
29348 var defd = urlDefined[i] = url != null && url !== 'none';
29349 if (defd) {
29350 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29351 numImages++;
29352
29353 // get image, and if not loaded then ask to redraw when later loaded
29354 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29355 _p.backgroundTimestamp = Date.now();
29356 node.emitAndNotify('background');
29357 });
29358 }
29359 }
29360
29361 //
29362 // setup styles
29363
29364 var darkness = node.pstyle('background-blacken').value;
29365 var borderWidth = node.pstyle('border-width').pfValue;
29366 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29367 var borderColor = node.pstyle('border-color').value;
29368 var borderStyle = node.pstyle('border-style').value;
29369 var borderJoin = node.pstyle('border-join').value;
29370 var borderCap = node.pstyle('border-cap').value;
29371 var borderPosition = node.pstyle('border-position').value;
29372 var borderPattern = node.pstyle('border-dash-pattern').pfValue;
29373 var borderOffset = node.pstyle('border-dash-offset').pfValue;
29374 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29375 var outlineWidth = node.pstyle('outline-width').pfValue;
29376 var outlineColor = node.pstyle('outline-color').value;
29377 var outlineStyle = node.pstyle('outline-style').value;
29378 var outlineOpacity = node.pstyle('outline-opacity').value * eleOpacity;
29379 var outlineOffset = node.pstyle('outline-offset').value;
29380 var cornerRadius = node.pstyle('corner-radius').value;
29381 if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
29382 var setupShapeColor = function setupShapeColor() {
29383 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29384 r.eleFillStyle(context, node, bgOpy);
29385 };
29386 var setupBorderColor = function setupBorderColor() {
29387 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29388 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29389 };
29390 var setupOutlineColor = function setupOutlineColor() {
29391 var otlnOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : outlineOpacity;
29392 r.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], otlnOpy);
29393 };
29394
29395 //
29396 // setup shape
29397
29398 var getPath = function getPath(width, height, shape, points) {
29399 var pathCache = r.nodePathCache = r.nodePathCache || [];
29400 var key = hashStrings(shape === 'polygon' ? shape + ',' + points.join(',') : shape, '' + height, '' + width, '' + cornerRadius);
29401 var cachedPath = pathCache[key];
29402 var path;
29403 var cacheHit = false;
29404 if (cachedPath != null) {
29405 path = cachedPath;
29406 cacheHit = true;
29407 rs.pathCache = path;
29408 } else {
29409 path = new Path2D();
29410 pathCache[key] = rs.pathCache = path;
29411 }
29412 return {
29413 path: path,
29414 cacheHit: cacheHit
29415 };
29416 };
29417 var styleShape = node.pstyle('shape').strValue;
29418 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29419 if (usePaths) {
29420 context.translate(pos.x, pos.y);
29421 var shapePath = getPath(nodeWidth, nodeHeight, styleShape, shapePts);
29422 path = shapePath.path;
29423 pathCacheHit = shapePath.cacheHit;
29424 }
29425 var drawShape = function drawShape() {
29426 if (!pathCacheHit) {
29427 var npos = pos;
29428 if (usePaths) {
29429 npos = {
29430 x: 0,
29431 y: 0
29432 };
29433 }
29434 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight, cornerRadius, rs);
29435 }
29436 if (usePaths) {
29437 context.fill(path);
29438 } else {
29439 context.fill();
29440 }
29441 };
29442 var drawImages = function drawImages() {
29443 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29444 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29445 var prevBging = _p.backgrounding;
29446 var totalCompleted = 0;
29447 for (var _i = 0; _i < image.length; _i++) {
29448 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29449 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29450 totalCompleted++;
29451 continue;
29452 }
29453 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29454 totalCompleted++;
29455 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29456 }
29457 }
29458 _p.backgrounding = !(totalCompleted === numImages);
29459 if (prevBging !== _p.backgrounding) {
29460 // update style b/c :backgrounding state changed
29461 node.updateStyle(false);
29462 }
29463 };
29464 var drawPie = function drawPie() {
29465 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29466 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29467 if (r.hasPie(node)) {
29468 r.drawPie(context, node, pieOpacity);
29469
29470 // redraw/restore path if steps after pie need it
29471 if (redrawShape) {
29472 if (!usePaths) {
29473 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight, cornerRadius, rs);
29474 }
29475 }
29476 }
29477 };
29478 var darken = function darken() {
29479 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29480 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29481 var c = darkness > 0 ? 0 : 255;
29482 if (darkness !== 0) {
29483 r.colorFillStyle(context, c, c, c, opacity);
29484 if (usePaths) {
29485 context.fill(path);
29486 } else {
29487 context.fill();
29488 }
29489 }
29490 };
29491 var drawBorder = function drawBorder() {
29492 if (borderWidth > 0) {
29493 context.lineWidth = borderWidth;
29494 context.lineCap = borderCap;
29495 context.lineJoin = borderJoin;
29496 if (context.setLineDash) {
29497 // for very outofdate browsers
29498 switch (borderStyle) {
29499 case 'dotted':
29500 context.setLineDash([1, 1]);
29501 break;
29502 case 'dashed':
29503 context.setLineDash(borderPattern);
29504 context.lineDashOffset = borderOffset;
29505 break;
29506 case 'solid':
29507 case 'double':
29508 context.setLineDash([]);
29509 break;
29510 }
29511 }
29512 if (borderPosition !== 'center') {
29513 context.save();
29514 context.lineWidth *= 2;
29515 if (borderPosition === 'inside') {
29516 usePaths ? context.clip(path) : context.clip();
29517 } else {
29518 var region = new Path2D();
29519 region.rect(-nodeWidth / 2 - borderWidth, -nodeHeight / 2 - borderWidth, nodeWidth + 2 * borderWidth, nodeHeight + 2 * borderWidth);
29520 region.addPath(path);
29521 context.clip(region, 'evenodd');
29522 }
29523 usePaths ? context.stroke(path) : context.stroke();
29524 context.restore();
29525 } else {
29526 usePaths ? context.stroke(path) : context.stroke();
29527 }
29528 if (borderStyle === 'double') {
29529 context.lineWidth = borderWidth / 3;
29530 var gco = context.globalCompositeOperation;
29531 context.globalCompositeOperation = 'destination-out';
29532 if (usePaths) {
29533 context.stroke(path);
29534 } else {
29535 context.stroke();
29536 }
29537 context.globalCompositeOperation = gco;
29538 }
29539
29540 // reset in case we changed the border style
29541 if (context.setLineDash) {
29542 // for very outofdate browsers
29543 context.setLineDash([]);
29544 }
29545 }
29546 };
29547 var drawOutline = function drawOutline() {
29548 if (outlineWidth > 0) {
29549 context.lineWidth = outlineWidth;
29550 context.lineCap = 'butt';
29551 if (context.setLineDash) {
29552 // for very outofdate browsers
29553 switch (outlineStyle) {
29554 case 'dotted':
29555 context.setLineDash([1, 1]);
29556 break;
29557 case 'dashed':
29558 context.setLineDash([4, 2]);
29559 break;
29560 case 'solid':
29561 case 'double':
29562 context.setLineDash([]);
29563 break;
29564 }
29565 }
29566 var npos = pos;
29567 if (usePaths) {
29568 npos = {
29569 x: 0,
29570 y: 0
29571 };
29572 }
29573 var shape = r.getNodeShape(node);
29574 var bWidth = borderWidth;
29575 if (borderPosition === 'inside') bWidth = 0;
29576 if (borderPosition === 'outside') bWidth *= 2;
29577 var scaleX = (nodeWidth + bWidth + (outlineWidth + outlineOffset)) / nodeWidth;
29578 var scaleY = (nodeHeight + bWidth + (outlineWidth + outlineOffset)) / nodeHeight;
29579 var sWidth = nodeWidth * scaleX;
29580 var sHeight = nodeHeight * scaleY;
29581 var points = r.nodeShapes[shape].points;
29582 var _path;
29583 if (usePaths) {
29584 var outlinePath = getPath(sWidth, sHeight, shape, points);
29585 _path = outlinePath.path;
29586 }
29587
29588 // draw the outline path, either by using expanded points or by scaling
29589 // the dimensions, depending on shape
29590 if (shape === "ellipse") {
29591 r.drawEllipsePath(_path || context, npos.x, npos.y, sWidth, sHeight);
29592 } else if (['round-diamond', 'round-heptagon', 'round-hexagon', 'round-octagon', 'round-pentagon', 'round-polygon', 'round-triangle', 'round-tag'].includes(shape)) {
29593 var sMult = 0;
29594 var offsetX = 0;
29595 var offsetY = 0;
29596 if (shape === 'round-diamond') {
29597 sMult = (bWidth + outlineOffset + outlineWidth) * 1.4;
29598 } else if (shape === 'round-heptagon') {
29599 sMult = (bWidth + outlineOffset + outlineWidth) * 1.075;
29600 offsetY = -(bWidth / 2 + outlineOffset + outlineWidth) / 35;
29601 } else if (shape === 'round-hexagon') {
29602 sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
29603 } else if (shape === 'round-pentagon') {
29604 sMult = (bWidth + outlineOffset + outlineWidth) * 1.13;
29605 offsetY = -(bWidth / 2 + outlineOffset + outlineWidth) / 15;
29606 } else if (shape === 'round-tag') {
29607 sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
29608 offsetX = (bWidth / 2 + outlineWidth + outlineOffset) * .07;
29609 } else if (shape === 'round-triangle') {
29610 sMult = (bWidth + outlineOffset + outlineWidth) * (Math.PI / 2);
29611 offsetY = -(bWidth + outlineOffset / 2 + outlineWidth) / Math.PI;
29612 }
29613 if (sMult !== 0) {
29614 scaleX = (nodeWidth + sMult) / nodeWidth;
29615 sWidth = nodeWidth * scaleX;
29616 if (!['round-hexagon', 'round-tag'].includes(shape)) {
29617 scaleY = (nodeHeight + sMult) / nodeHeight;
29618 sHeight = nodeHeight * scaleY;
29619 }
29620 }
29621 cornerRadius = cornerRadius === 'auto' ? getRoundPolygonRadius(sWidth, sHeight) : cornerRadius;
29622 var halfW = sWidth / 2;
29623 var halfH = sHeight / 2;
29624 var radius = cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2;
29625 var p = new Array(points.length / 2);
29626 var corners = new Array(points.length / 2);
29627 for (var _i3 = 0; _i3 < points.length / 2; _i3++) {
29628 p[_i3] = {
29629 x: npos.x + offsetX + halfW * points[_i3 * 2],
29630 y: npos.y + offsetY + halfH * points[_i3 * 2 + 1]
29631 };
29632 }
29633 var _i2,
29634 p1,
29635 p2,
29636 p3,
29637 len = p.length;
29638 p1 = p[len - 1];
29639 // for each point
29640 for (_i2 = 0; _i2 < len; _i2++) {
29641 p2 = p[_i2 % len];
29642 p3 = p[(_i2 + 1) % len];
29643 corners[_i2] = getRoundCorner(p1, p2, p3, radius);
29644 p1 = p2;
29645 p2 = p3;
29646 }
29647 r.drawRoundPolygonPath(_path || context, npos.x + offsetX, npos.y + offsetY, nodeWidth * scaleX, nodeHeight * scaleY, points, corners);
29648 } else if (['roundrectangle', 'round-rectangle'].includes(shape)) {
29649 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(sWidth, sHeight) : cornerRadius;
29650 r.drawRoundRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2);
29651 } else if (['cutrectangle', 'cut-rectangle'].includes(shape)) {
29652 cornerRadius = cornerRadius === 'auto' ? getCutRectangleCornerLength() : cornerRadius;
29653 r.drawCutRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, null, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 4);
29654 } else if (['bottomroundrectangle', 'bottom-round-rectangle'].includes(shape)) {
29655 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(sWidth, sHeight) : cornerRadius;
29656 r.drawBottomRoundRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2);
29657 } else if (shape === "barrel") {
29658 r.drawBarrelPath(_path || context, npos.x, npos.y, sWidth, sHeight);
29659 } else if (shape.startsWith("polygon") || ['rhomboid', 'right-rhomboid', 'round-tag', 'tag', 'vee'].includes(shape)) {
29660 var pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
29661 points = joinLines(expandPolygon(points, pad));
29662 r.drawPolygonPath(_path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
29663 } else {
29664 var _pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
29665 points = joinLines(expandPolygon(points, -_pad));
29666 r.drawPolygonPath(_path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
29667 }
29668 if (usePaths) {
29669 context.stroke(_path);
29670 } else {
29671 context.stroke();
29672 }
29673 if (outlineStyle === 'double') {
29674 context.lineWidth = bWidth / 3;
29675 var gco = context.globalCompositeOperation;
29676 context.globalCompositeOperation = 'destination-out';
29677 if (usePaths) {
29678 context.stroke(_path);
29679 } else {
29680 context.stroke();
29681 }
29682 context.globalCompositeOperation = gco;
29683 }
29684
29685 // reset in case we changed the border style
29686 if (context.setLineDash) {
29687 // for very outofdate browsers
29688 context.setLineDash([]);
29689 }
29690 }
29691 };
29692 var drawOverlay = function drawOverlay() {
29693 if (shouldDrawOverlay) {
29694 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29695 }
29696 };
29697 var drawUnderlay = function drawUnderlay() {
29698 if (shouldDrawOverlay) {
29699 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
29700 }
29701 };
29702 var drawText = function drawText() {
29703 r.drawElementText(context, node, null, drawLabel);
29704 };
29705 var ghost = node.pstyle('ghost').value === 'yes';
29706 if (ghost) {
29707 var gx = node.pstyle('ghost-offset-x').pfValue;
29708 var gy = node.pstyle('ghost-offset-y').pfValue;
29709 var ghostOpacity = node.pstyle('ghost-opacity').value;
29710 var effGhostOpacity = ghostOpacity * eleOpacity;
29711 context.translate(gx, gy);
29712 setupOutlineColor();
29713 drawOutline();
29714 setupShapeColor(ghostOpacity * bgOpacity);
29715 drawShape();
29716 drawImages(effGhostOpacity, true);
29717 setupBorderColor(ghostOpacity * borderOpacity);
29718 drawBorder();
29719 drawPie(darkness !== 0 || borderWidth !== 0);
29720 drawImages(effGhostOpacity, false);
29721 darken(effGhostOpacity);
29722 context.translate(-gx, -gy);
29723 }
29724 if (usePaths) {
29725 context.translate(-pos.x, -pos.y);
29726 }
29727 drawUnderlay();
29728 if (usePaths) {
29729 context.translate(pos.x, pos.y);
29730 }
29731 setupOutlineColor();
29732 drawOutline();
29733 setupShapeColor();
29734 drawShape();
29735 drawImages(eleOpacity, true);
29736 setupBorderColor();
29737 drawBorder();
29738 drawPie(darkness !== 0 || borderWidth !== 0);
29739 drawImages(eleOpacity, false);
29740 darken();
29741 if (usePaths) {
29742 context.translate(-pos.x, -pos.y);
29743 }
29744 drawText();
29745 drawOverlay();
29746
29747 //
29748 // clean up shift
29749
29750 if (shiftToOriginWithBb) {
29751 context.translate(bb.x1, bb.y1);
29752 }
29753};
29754var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
29755 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
29756 throw new Error('Invalid state');
29757 }
29758 return function (context, node, pos, nodeWidth, nodeHeight) {
29759 var r = this;
29760 if (!node.visible()) {
29761 return;
29762 }
29763 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
29764 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
29765 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
29766 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
29767 var radius = node.pstyle("".concat(overlayOrUnderlay, "-corner-radius")).value;
29768 if (opacity > 0) {
29769 pos = pos || node.position();
29770 if (nodeWidth == null || nodeHeight == null) {
29771 var _padding = node.padding();
29772 nodeWidth = node.width() + 2 * _padding;
29773 nodeHeight = node.height() + 2 * _padding;
29774 }
29775 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
29776 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2, radius);
29777 context.fill();
29778 }
29779 };
29780};
29781CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
29782CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay');
29783
29784// does the node have at least one pie piece?
29785CRp$5.hasPie = function (node) {
29786 node = node[0]; // ensure ele ref
29787
29788 return node._private.hasPie;
29789};
29790CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29791 node = node[0]; // ensure ele ref
29792 pos = pos || node.position();
29793 var cyStyle = node.cy().style();
29794 var pieSize = node.pstyle('pie-size');
29795 var x = pos.x;
29796 var y = pos.y;
29797 var nodeW = node.width();
29798 var nodeH = node.height();
29799 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29800 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29801 var usePaths = this.usePaths();
29802 if (usePaths) {
29803 x = 0;
29804 y = 0;
29805 }
29806 if (pieSize.units === '%') {
29807 radius = radius * pieSize.pfValue;
29808 } else if (pieSize.pfValue !== undefined) {
29809 radius = pieSize.pfValue / 2;
29810 }
29811 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29812 // 1..N
29813 var size = node.pstyle('pie-' + i + '-background-size').value;
29814 var color = node.pstyle('pie-' + i + '-background-color').value;
29815 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29816 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29817
29818 // percent can't push beyond 1
29819 if (percent + lastPercent > 1) {
29820 percent = 1 - lastPercent;
29821 }
29822 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29823 var angleDelta = 2 * Math.PI * percent;
29824 var angleEnd = angleStart + angleDelta;
29825
29826 // ignore if
29827 // - zero size
29828 // - we're already beyond the full circle
29829 // - adding the current slice would go beyond the full circle
29830 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29831 continue;
29832 }
29833 context.beginPath();
29834 context.moveTo(x, y);
29835 context.arc(x, y, radius, angleStart, angleEnd);
29836 context.closePath();
29837 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29838 context.fill();
29839 lastPercent += percent;
29840 }
29841};
29842
29843var CRp$4 = {};
29844var motionBlurDelay = 100;
29845
29846// var isFirefox = typeof InstallTrigger !== 'undefined';
29847
29848CRp$4.getPixelRatio = function () {
29849 var context = this.data.contexts[0];
29850 if (this.forcedPixelRatio != null) {
29851 return this.forcedPixelRatio;
29852 }
29853 var containerWindow = this.cy.window();
29854 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29855 return (containerWindow.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29856};
29857
29858CRp$4.paintCache = function (context) {
29859 var caches = this.paintCaches = this.paintCaches || [];
29860 var needToCreateCache = true;
29861 var cache;
29862 for (var i = 0; i < caches.length; i++) {
29863 cache = caches[i];
29864 if (cache.context === context) {
29865 needToCreateCache = false;
29866 break;
29867 }
29868 }
29869 if (needToCreateCache) {
29870 cache = {
29871 context: context
29872 };
29873 caches.push(cache);
29874 }
29875 return cache;
29876};
29877CRp$4.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29878 var gradientStyle;
29879 var usePaths = this.usePaths();
29880 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29881 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29882 if (fill === 'radial-gradient') {
29883 if (ele.isEdge()) {
29884 var start = ele.sourceEndpoint(),
29885 end = ele.targetEndpoint(),
29886 mid = ele.midpoint();
29887 var d1 = dist(start, mid);
29888 var d2 = dist(end, mid);
29889 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29890 } else {
29891 var pos = usePaths ? {
29892 x: 0,
29893 y: 0
29894 } : ele.position(),
29895 width = ele.paddedWidth(),
29896 height = ele.paddedHeight();
29897 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29898 }
29899 } else {
29900 if (ele.isEdge()) {
29901 var _start = ele.sourceEndpoint(),
29902 _end = ele.targetEndpoint();
29903 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29904 } else {
29905 var _pos = usePaths ? {
29906 x: 0,
29907 y: 0
29908 } : ele.position(),
29909 _width = ele.paddedWidth(),
29910 _height = ele.paddedHeight(),
29911 halfWidth = _width / 2,
29912 halfHeight = _height / 2;
29913 var direction = ele.pstyle('background-gradient-direction').value;
29914 switch (direction) {
29915 case 'to-bottom':
29916 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29917 break;
29918 case 'to-top':
29919 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29920 break;
29921 case 'to-left':
29922 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29923 break;
29924 case 'to-right':
29925 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29926 break;
29927 case 'to-bottom-right':
29928 case 'to-right-bottom':
29929 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29930 break;
29931 case 'to-top-right':
29932 case 'to-right-top':
29933 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29934 break;
29935 case 'to-bottom-left':
29936 case 'to-left-bottom':
29937 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29938 break;
29939 case 'to-top-left':
29940 case 'to-left-top':
29941 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29942 break;
29943 }
29944 }
29945 }
29946 if (!gradientStyle) return null; // invalid gradient style
29947
29948 var hasPositions = positions.length === colors.length;
29949 var length = colors.length;
29950 for (var i = 0; i < length; i++) {
29951 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29952 }
29953 return gradientStyle;
29954};
29955CRp$4.gradientFillStyle = function (context, ele, fill, opacity) {
29956 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29957 if (!gradientStyle) return null; // error
29958 context.fillStyle = gradientStyle;
29959};
29960CRp$4.colorFillStyle = function (context, r, g, b, a) {
29961 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29962 // turn off for now, seems context does its own caching
29963
29964 // var cache = this.paintCache(context);
29965
29966 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29967
29968 // if( cache.fillStyle !== fillStyle ){
29969 // context.fillStyle = cache.fillStyle = fillStyle;
29970 // }
29971};
29972
29973CRp$4.eleFillStyle = function (context, ele, opacity) {
29974 var backgroundFill = ele.pstyle('background-fill').value;
29975 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
29976 this.gradientFillStyle(context, ele, backgroundFill, opacity);
29977 } else {
29978 var backgroundColor = ele.pstyle('background-color').value;
29979 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
29980 }
29981};
29982CRp$4.gradientStrokeStyle = function (context, ele, fill, opacity) {
29983 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
29984 if (!gradientStyle) return null; // error
29985 context.strokeStyle = gradientStyle;
29986};
29987CRp$4.colorStrokeStyle = function (context, r, g, b, a) {
29988 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29989 // turn off for now, seems context does its own caching
29990
29991 // var cache = this.paintCache(context);
29992
29993 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29994
29995 // if( cache.strokeStyle !== strokeStyle ){
29996 // context.strokeStyle = cache.strokeStyle = strokeStyle;
29997 // }
29998};
29999
30000CRp$4.eleStrokeStyle = function (context, ele, opacity) {
30001 var lineFill = ele.pstyle('line-fill').value;
30002 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30003 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30004 } else {
30005 var lineColor = ele.pstyle('line-color').value;
30006 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30007 }
30008};
30009
30010// Resize canvas
30011CRp$4.matchCanvasSize = function (container) {
30012 var r = this;
30013 var data = r.data;
30014 var bb = r.findContainerClientCoords();
30015 var width = bb[2];
30016 var height = bb[3];
30017 var pixelRatio = r.getPixelRatio();
30018 var mbPxRatio = r.motionBlurPxRatio;
30019 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30020 pixelRatio = mbPxRatio;
30021 }
30022 var canvasWidth = width * pixelRatio;
30023 var canvasHeight = height * pixelRatio;
30024 var canvas;
30025 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30026 return; // save cycles if same
30027 }
30028
30029 r.fontCaches = null; // resizing resets the style
30030
30031 var canvasContainer = data.canvasContainer;
30032 canvasContainer.style.width = width + 'px';
30033 canvasContainer.style.height = height + 'px';
30034 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30035 canvas = data.canvases[i];
30036 canvas.width = canvasWidth;
30037 canvas.height = canvasHeight;
30038 canvas.style.width = width + 'px';
30039 canvas.style.height = height + 'px';
30040 }
30041 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30042 canvas = data.bufferCanvases[i];
30043 canvas.width = canvasWidth;
30044 canvas.height = canvasHeight;
30045 canvas.style.width = width + 'px';
30046 canvas.style.height = height + 'px';
30047 }
30048 r.textureMult = 1;
30049 if (pixelRatio <= 1) {
30050 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30051 r.textureMult = 2;
30052 canvas.width = canvasWidth * r.textureMult;
30053 canvas.height = canvasHeight * r.textureMult;
30054 }
30055 r.canvasWidth = canvasWidth;
30056 r.canvasHeight = canvasHeight;
30057};
30058CRp$4.renderTo = function (cxt, zoom, pan, pxRatio) {
30059 this.render({
30060 forcedContext: cxt,
30061 forcedZoom: zoom,
30062 forcedPan: pan,
30063 drawAllLayers: true,
30064 forcedPxRatio: pxRatio
30065 });
30066};
30067CRp$4.render = function (options) {
30068 options = options || staticEmptyObject();
30069 var forcedContext = options.forcedContext;
30070 var drawAllLayers = options.drawAllLayers;
30071 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30072 var forcedZoom = options.forcedZoom;
30073 var forcedPan = options.forcedPan;
30074 var r = this;
30075 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30076 var cy = r.cy;
30077 var data = r.data;
30078 var needDraw = data.canvasNeedsRedraw;
30079 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30080 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30081 var mbPxRatio = r.motionBlurPxRatio;
30082 var hasCompoundNodes = cy.hasCompoundNodes();
30083 var inNodeDragGesture = r.hoverData.draggingEles;
30084 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30085 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30086 var motionBlurFadeEffect = motionBlur;
30087 if (!forcedContext) {
30088 if (r.prevPxRatio !== pixelRatio) {
30089 r.invalidateContainerClientCoordsCache();
30090 r.matchCanvasSize(r.container);
30091 r.redrawHint('eles', true);
30092 r.redrawHint('drag', true);
30093 }
30094 r.prevPxRatio = pixelRatio;
30095 }
30096 if (!forcedContext && r.motionBlurTimeout) {
30097 clearTimeout(r.motionBlurTimeout);
30098 }
30099 if (motionBlur) {
30100 if (r.mbFrames == null) {
30101 r.mbFrames = 0;
30102 }
30103 r.mbFrames++;
30104 if (r.mbFrames < 3) {
30105 // need several frames before even high quality motionblur
30106 motionBlurFadeEffect = false;
30107 }
30108
30109 // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30110 if (r.mbFrames > r.minMbLowQualFrames) {
30111 //r.fullQualityMb = false;
30112 r.motionBlurPxRatio = r.mbPxRBlurry;
30113 }
30114 }
30115 if (r.clearingMotionBlur) {
30116 r.motionBlurPxRatio = 1;
30117 }
30118
30119 // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30120 // because a rogue async texture frame would clear needDraw
30121 if (r.textureDrawLastFrame && !textureDraw) {
30122 needDraw[r.NODE] = true;
30123 needDraw[r.SELECT_BOX] = true;
30124 }
30125 var style = cy.style();
30126 var zoom = cy.zoom();
30127 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30128 var pan = cy.pan();
30129 var effectivePan = {
30130 x: pan.x,
30131 y: pan.y
30132 };
30133 var vp = {
30134 zoom: zoom,
30135 pan: {
30136 x: pan.x,
30137 y: pan.y
30138 }
30139 };
30140 var prevVp = r.prevViewport;
30141 var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y;
30142
30143 // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
30144 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30145 r.motionBlurPxRatio = 1;
30146 }
30147 if (forcedPan) {
30148 effectivePan = forcedPan;
30149 }
30150
30151 // apply pixel ratio
30152
30153 effectiveZoom *= pixelRatio;
30154 effectivePan.x *= pixelRatio;
30155 effectivePan.y *= pixelRatio;
30156 var eles = r.getCachedZSortedEles();
30157 function mbclear(context, x, y, w, h) {
30158 var gco = context.globalCompositeOperation;
30159 context.globalCompositeOperation = 'destination-out';
30160 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30161 context.fillRect(x, y, w, h);
30162 context.globalCompositeOperation = gco;
30163 }
30164 function setContextTransform(context, clear) {
30165 var ePan, eZoom, w, h;
30166 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30167 ePan = {
30168 x: pan.x * mbPxRatio,
30169 y: pan.y * mbPxRatio
30170 };
30171 eZoom = zoom * mbPxRatio;
30172 w = r.canvasWidth * mbPxRatio;
30173 h = r.canvasHeight * mbPxRatio;
30174 } else {
30175 ePan = effectivePan;
30176 eZoom = effectiveZoom;
30177 w = r.canvasWidth;
30178 h = r.canvasHeight;
30179 }
30180 context.setTransform(1, 0, 0, 1, 0, 0);
30181 if (clear === 'motionBlur') {
30182 mbclear(context, 0, 0, w, h);
30183 } else if (!forcedContext && (clear === undefined || clear)) {
30184 context.clearRect(0, 0, w, h);
30185 }
30186 if (!drawAllLayers) {
30187 context.translate(ePan.x, ePan.y);
30188 context.scale(eZoom, eZoom);
30189 }
30190 if (forcedPan) {
30191 context.translate(forcedPan.x, forcedPan.y);
30192 }
30193 if (forcedZoom) {
30194 context.scale(forcedZoom, forcedZoom);
30195 }
30196 }
30197 if (!textureDraw) {
30198 r.textureDrawLastFrame = false;
30199 }
30200 if (textureDraw) {
30201 r.textureDrawLastFrame = true;
30202 if (!r.textureCache) {
30203 r.textureCache = {};
30204 r.textureCache.bb = cy.mutableElements().boundingBox();
30205 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30206 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30207 cxt.setTransform(1, 0, 0, 1, 0, 0);
30208 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30209 r.render({
30210 forcedContext: cxt,
30211 drawOnlyNodeLayer: true,
30212 forcedPxRatio: pixelRatio * r.textureMult
30213 });
30214 var vp = r.textureCache.viewport = {
30215 zoom: cy.zoom(),
30216 pan: cy.pan(),
30217 width: r.canvasWidth,
30218 height: r.canvasHeight
30219 };
30220 vp.mpan = {
30221 x: (0 - vp.pan.x) / vp.zoom,
30222 y: (0 - vp.pan.y) / vp.zoom
30223 };
30224 }
30225 needDraw[r.DRAG] = false;
30226 needDraw[r.NODE] = false;
30227 var context = data.contexts[r.NODE];
30228 var texture = r.textureCache.texture;
30229 var vp = r.textureCache.viewport;
30230 context.setTransform(1, 0, 0, 1, 0, 0);
30231 if (motionBlur) {
30232 mbclear(context, 0, 0, vp.width, vp.height);
30233 } else {
30234 context.clearRect(0, 0, vp.width, vp.height);
30235 }
30236 var outsideBgColor = style.core('outside-texture-bg-color').value;
30237 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30238 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30239 context.fillRect(0, 0, vp.width, vp.height);
30240 var zoom = cy.zoom();
30241 setContextTransform(context, false);
30242 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30243 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30244 } else if (r.textureOnViewport && !forcedContext) {
30245 // clear the cache since we don't need it
30246 r.textureCache = null;
30247 }
30248 var extent = cy.extent();
30249 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30250 var hideEdges = r.hideEdgesOnViewport && vpManip;
30251 var needMbClear = [];
30252 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30253 if (needMbClear[r.NODE]) {
30254 r.clearedForMotionBlur[r.NODE] = true;
30255 }
30256 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30257 if (needMbClear[r.DRAG]) {
30258 r.clearedForMotionBlur[r.DRAG] = true;
30259 }
30260 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30261 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30262 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30263 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30264 setContextTransform(context, clear);
30265 if (hideEdges) {
30266 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30267 } else {
30268 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30269 }
30270 if (r.debug) {
30271 r.drawDebugPoints(context, eles.nondrag);
30272 }
30273 if (!drawAllLayers && !motionBlur) {
30274 needDraw[r.NODE] = false;
30275 }
30276 }
30277 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30278 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30279 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30280 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30281 if (hideEdges) {
30282 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30283 } else {
30284 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30285 }
30286 if (r.debug) {
30287 r.drawDebugPoints(context, eles.drag);
30288 }
30289 if (!drawAllLayers && !motionBlur) {
30290 needDraw[r.DRAG] = false;
30291 }
30292 }
30293 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30294 var context = forcedContext || data.contexts[r.SELECT_BOX];
30295 setContextTransform(context);
30296 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30297 var zoom = r.cy.zoom();
30298 var borderWidth = style.core('selection-box-border-width').value / zoom;
30299 context.lineWidth = borderWidth;
30300 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 + ')';
30301 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30302 if (borderWidth > 0) {
30303 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 + ')';
30304 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30305 }
30306 }
30307 if (data.bgActivePosistion && !r.hoverData.selecting) {
30308 var zoom = r.cy.zoom();
30309 var pos = data.bgActivePosistion;
30310 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 + ')';
30311 context.beginPath();
30312 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30313 context.fill();
30314 }
30315 var timeToRender = r.lastRedrawTime;
30316 if (r.showFps && timeToRender) {
30317 timeToRender = Math.round(timeToRender);
30318 var fps = Math.round(1000 / timeToRender);
30319 context.setTransform(1, 0, 0, 1, 0, 0);
30320 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30321 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30322 context.lineWidth = 1;
30323 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30324 var maxFps = 60;
30325 context.strokeRect(0, 30, 250, 20);
30326 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30327 }
30328 if (!drawAllLayers) {
30329 needDraw[r.SELECT_BOX] = false;
30330 }
30331 }
30332
30333 // motionblur: blit rendered blurry frames
30334 if (motionBlur && mbPxRatio !== 1) {
30335 var cxtNode = data.contexts[r.NODE];
30336 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30337 var cxtDrag = data.contexts[r.DRAG];
30338 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30339 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30340 cxt.setTransform(1, 0, 0, 1, 0, 0);
30341 if (needClear || !motionBlurFadeEffect) {
30342 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30343 } else {
30344 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30345 }
30346 var pxr = mbPxRatio;
30347 cxt.drawImage(txt,
30348 // img
30349 0, 0,
30350 // sx, sy
30351 r.canvasWidth * pxr, r.canvasHeight * pxr,
30352 // sw, sh
30353 0, 0,
30354 // x, y
30355 r.canvasWidth, r.canvasHeight // w, h
30356 );
30357 };
30358
30359 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30360 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30361 needDraw[r.NODE] = false;
30362 }
30363 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30364 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30365 needDraw[r.DRAG] = false;
30366 }
30367 }
30368 r.prevViewport = vp;
30369 if (r.clearingMotionBlur) {
30370 r.clearingMotionBlur = false;
30371 r.motionBlurCleared = true;
30372 r.motionBlur = true;
30373 }
30374 if (motionBlur) {
30375 r.motionBlurTimeout = setTimeout(function () {
30376 r.motionBlurTimeout = null;
30377 r.clearedForMotionBlur[r.NODE] = false;
30378 r.clearedForMotionBlur[r.DRAG] = false;
30379 r.motionBlur = false;
30380 r.clearingMotionBlur = !textureDraw;
30381 r.mbFrames = 0;
30382 needDraw[r.NODE] = true;
30383 needDraw[r.DRAG] = true;
30384 r.redraw();
30385 }, motionBlurDelay);
30386 }
30387 if (!forcedContext) {
30388 cy.emit('render');
30389 }
30390};
30391
30392var CRp$3 = {};
30393
30394// @O Polygon drawing
30395CRp$3.drawPolygonPath = function (context, x, y, width, height, points) {
30396 var halfW = width / 2;
30397 var halfH = height / 2;
30398 if (context.beginPath) {
30399 context.beginPath();
30400 }
30401 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30402 for (var i = 1; i < points.length / 2; i++) {
30403 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30404 }
30405 context.closePath();
30406};
30407CRp$3.drawRoundPolygonPath = function (context, x, y, width, height, points, corners) {
30408 corners.forEach(function (corner) {
30409 return drawPreparedRoundCorner(context, corner);
30410 });
30411 context.closePath();
30412};
30413
30414// Round rectangle drawing
30415CRp$3.drawRoundRectanglePath = function (context, x, y, width, height, radius) {
30416 var halfWidth = width / 2;
30417 var halfHeight = height / 2;
30418 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : Math.min(radius, halfHeight, halfWidth);
30419 if (context.beginPath) {
30420 context.beginPath();
30421 }
30422
30423 // Start at top middle
30424 context.moveTo(x, y - halfHeight);
30425 // Arc from middle top to right side
30426 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius);
30427 // Arc from right side to bottom
30428 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30429 // Arc from bottom to left side
30430 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30431 // Arc from left side to topBorder
30432 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius);
30433 // Join line
30434 context.lineTo(x, y - halfHeight);
30435 context.closePath();
30436};
30437CRp$3.drawBottomRoundRectanglePath = function (context, x, y, width, height, radius) {
30438 var halfWidth = width / 2;
30439 var halfHeight = height / 2;
30440 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : radius;
30441 if (context.beginPath) {
30442 context.beginPath();
30443 }
30444
30445 // Start at top middle
30446 context.moveTo(x, y - halfHeight);
30447 context.lineTo(x + halfWidth, y - halfHeight);
30448 context.lineTo(x + halfWidth, y);
30449 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30450 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30451 context.lineTo(x - halfWidth, y - halfHeight);
30452 context.lineTo(x, y - halfHeight);
30453 context.closePath();
30454};
30455CRp$3.drawCutRectanglePath = function (context, x, y, width, height, points, corners) {
30456 var halfWidth = width / 2;
30457 var halfHeight = height / 2;
30458 var cornerLength = corners === 'auto' ? getCutRectangleCornerLength() : corners;
30459 if (context.beginPath) {
30460 context.beginPath();
30461 }
30462 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30463 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30464 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30465 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30466 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30467 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30468 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30469 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30470 context.closePath();
30471};
30472CRp$3.drawBarrelPath = function (context, x, y, width, height) {
30473 var halfWidth = width / 2;
30474 var halfHeight = height / 2;
30475 var xBegin = x - halfWidth;
30476 var xEnd = x + halfWidth;
30477 var yBegin = y - halfHeight;
30478 var yEnd = y + halfHeight;
30479 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30480 var wOffset = barrelCurveConstants.widthOffset;
30481 var hOffset = barrelCurveConstants.heightOffset;
30482 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30483 if (context.beginPath) {
30484 context.beginPath();
30485 }
30486 context.moveTo(xBegin, yBegin + hOffset);
30487 context.lineTo(xBegin, yEnd - hOffset);
30488 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30489 context.lineTo(xEnd - wOffset, yEnd);
30490 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30491 context.lineTo(xEnd, yBegin + hOffset);
30492 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30493 context.lineTo(xBegin + wOffset, yBegin);
30494 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30495 context.closePath();
30496};
30497var sin0 = Math.sin(0);
30498var cos0 = Math.cos(0);
30499var sin = {};
30500var cos = {};
30501var ellipseStepSize = Math.PI / 40;
30502for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30503 sin[i] = Math.sin(i);
30504 cos[i] = Math.cos(i);
30505}
30506CRp$3.drawEllipsePath = function (context, centerX, centerY, width, height) {
30507 if (context.beginPath) {
30508 context.beginPath();
30509 }
30510 if (context.ellipse) {
30511 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30512 } else {
30513 var xPos, yPos;
30514 var rw = width / 2;
30515 var rh = height / 2;
30516 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30517 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30518 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30519 if (i === 0) {
30520 context.moveTo(xPos, yPos);
30521 } else {
30522 context.lineTo(xPos, yPos);
30523 }
30524 }
30525 }
30526 context.closePath();
30527};
30528
30529/* global atob, ArrayBuffer, Uint8Array, Blob */
30530var CRp$2 = {};
30531CRp$2.createBuffer = function (w, h) {
30532 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30533 buffer.width = w;
30534 buffer.height = h;
30535 return [buffer, buffer.getContext('2d')];
30536};
30537CRp$2.bufferCanvasImage = function (options) {
30538 var cy = this.cy;
30539 var eles = cy.mutableElements();
30540 var bb = eles.boundingBox();
30541 var ctrRect = this.findContainerClientCoords();
30542 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30543 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30544 var specdMaxDims = number$1(options.maxWidth) || number$1(options.maxHeight);
30545 var pxRatio = this.getPixelRatio();
30546 var scale = 1;
30547 if (options.scale !== undefined) {
30548 width *= options.scale;
30549 height *= options.scale;
30550 scale = options.scale;
30551 } else if (specdMaxDims) {
30552 var maxScaleW = Infinity;
30553 var maxScaleH = Infinity;
30554 if (number$1(options.maxWidth)) {
30555 maxScaleW = scale * options.maxWidth / width;
30556 }
30557 if (number$1(options.maxHeight)) {
30558 maxScaleH = scale * options.maxHeight / height;
30559 }
30560 scale = Math.min(maxScaleW, maxScaleH);
30561 width *= scale;
30562 height *= scale;
30563 }
30564 if (!specdMaxDims) {
30565 width *= pxRatio;
30566 height *= pxRatio;
30567 scale *= pxRatio;
30568 }
30569 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30570
30571 buffCanvas.width = width;
30572 buffCanvas.height = height;
30573 buffCanvas.style.width = width + 'px';
30574 buffCanvas.style.height = height + 'px';
30575 var buffCxt = buffCanvas.getContext('2d');
30576
30577 // Rasterize the layers, but only if container has nonzero size
30578 if (width > 0 && height > 0) {
30579 buffCxt.clearRect(0, 0, width, height);
30580 buffCxt.globalCompositeOperation = 'source-over';
30581 var zsortedEles = this.getCachedZSortedEles();
30582 if (options.full) {
30583 // draw the full bounds of the graph
30584 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30585 buffCxt.scale(scale, scale);
30586 this.drawElements(buffCxt, zsortedEles);
30587 buffCxt.scale(1 / scale, 1 / scale);
30588 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30589 } else {
30590 // draw the current view
30591 var pan = cy.pan();
30592 var translation = {
30593 x: pan.x * scale,
30594 y: pan.y * scale
30595 };
30596 scale *= cy.zoom();
30597 buffCxt.translate(translation.x, translation.y);
30598 buffCxt.scale(scale, scale);
30599 this.drawElements(buffCxt, zsortedEles);
30600 buffCxt.scale(1 / scale, 1 / scale);
30601 buffCxt.translate(-translation.x, -translation.y);
30602 }
30603
30604 // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30605 if (options.bg) {
30606 buffCxt.globalCompositeOperation = 'destination-over';
30607 buffCxt.fillStyle = options.bg;
30608 buffCxt.rect(0, 0, width, height);
30609 buffCxt.fill();
30610 }
30611 }
30612 return buffCanvas;
30613};
30614function b64ToBlob(b64, mimeType) {
30615 var bytes = atob(b64);
30616 var buff = new ArrayBuffer(bytes.length);
30617 var buffUint8 = new Uint8Array(buff);
30618 for (var i = 0; i < bytes.length; i++) {
30619 buffUint8[i] = bytes.charCodeAt(i);
30620 }
30621 return new Blob([buff], {
30622 type: mimeType
30623 });
30624}
30625function b64UriToB64(b64uri) {
30626 var i = b64uri.indexOf(',');
30627 return b64uri.substr(i + 1);
30628}
30629function output(options, canvas, mimeType) {
30630 var getB64Uri = function getB64Uri() {
30631 return canvas.toDataURL(mimeType, options.quality);
30632 };
30633 switch (options.output) {
30634 case 'blob-promise':
30635 return new Promise$1(function (resolve, reject) {
30636 try {
30637 canvas.toBlob(function (blob) {
30638 if (blob != null) {
30639 resolve(blob);
30640 } else {
30641 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30642 }
30643 }, mimeType, options.quality);
30644 } catch (err) {
30645 reject(err);
30646 }
30647 });
30648 case 'blob':
30649 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30650 case 'base64':
30651 return b64UriToB64(getB64Uri());
30652 case 'base64uri':
30653 default:
30654 return getB64Uri();
30655 }
30656}
30657CRp$2.png = function (options) {
30658 return output(options, this.bufferCanvasImage(options), 'image/png');
30659};
30660CRp$2.jpg = function (options) {
30661 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30662};
30663
30664var CRp$1 = {};
30665CRp$1.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points, corners) {
30666 switch (name) {
30667 case 'ellipse':
30668 return this.drawEllipsePath(context, centerX, centerY, width, height);
30669 case 'polygon':
30670 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30671 case 'round-polygon':
30672 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points, corners);
30673 case 'roundrectangle':
30674 case 'round-rectangle':
30675 return this.drawRoundRectanglePath(context, centerX, centerY, width, height, corners);
30676 case 'cutrectangle':
30677 case 'cut-rectangle':
30678 return this.drawCutRectanglePath(context, centerX, centerY, width, height, points, corners);
30679 case 'bottomroundrectangle':
30680 case 'bottom-round-rectangle':
30681 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height, corners);
30682 case 'barrel':
30683 return this.drawBarrelPath(context, centerX, centerY, width, height);
30684 }
30685};
30686
30687var CR = CanvasRenderer;
30688var CRp = CanvasRenderer.prototype;
30689CRp.CANVAS_LAYERS = 3;
30690//
30691CRp.SELECT_BOX = 0;
30692CRp.DRAG = 1;
30693CRp.NODE = 2;
30694CRp.BUFFER_COUNT = 3;
30695//
30696CRp.TEXTURE_BUFFER = 0;
30697CRp.MOTIONBLUR_BUFFER_NODE = 1;
30698CRp.MOTIONBLUR_BUFFER_DRAG = 2;
30699function CanvasRenderer(options) {
30700 var r = this;
30701 var containerWindow = r.cy.window();
30702 var document = containerWindow.document;
30703 r.data = {
30704 canvases: new Array(CRp.CANVAS_LAYERS),
30705 contexts: new Array(CRp.CANVAS_LAYERS),
30706 canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
30707 bufferCanvases: new Array(CRp.BUFFER_COUNT),
30708 bufferContexts: new Array(CRp.CANVAS_LAYERS)
30709 };
30710 var tapHlOffAttr = '-webkit-tap-highlight-color';
30711 var tapHlOffStyle = 'rgba(0,0,0,0)';
30712 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30713 var containerStyle = r.data.canvasContainer.style;
30714 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30715 containerStyle.position = 'relative';
30716 containerStyle.zIndex = '0';
30717 containerStyle.overflow = 'hidden';
30718 var container = options.cy.container();
30719 container.appendChild(r.data.canvasContainer);
30720 container.style[tapHlOffAttr] = tapHlOffStyle;
30721 var styleMap = {
30722 '-webkit-user-select': 'none',
30723 '-moz-user-select': '-moz-none',
30724 'user-select': 'none',
30725 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30726 'outline-style': 'none'
30727 };
30728 if (ms()) {
30729 styleMap['-ms-touch-action'] = 'none';
30730 styleMap['touch-action'] = 'none';
30731 }
30732 for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
30733 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30734 r.data.contexts[i] = canvas.getContext('2d');
30735 Object.keys(styleMap).forEach(function (k) {
30736 canvas.style[k] = styleMap[k];
30737 });
30738 canvas.style.position = 'absolute';
30739 canvas.setAttribute('data-id', 'layer' + i);
30740 canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
30741 r.data.canvasContainer.appendChild(canvas);
30742 r.data.canvasNeedsRedraw[i] = false;
30743 }
30744 r.data.topCanvas = r.data.canvases[0];
30745 r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
30746 r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
30747 r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
30748 for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
30749 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30750 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30751 r.data.bufferCanvases[i].style.position = 'absolute';
30752 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30753 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30754 r.data.bufferCanvases[i].style.visibility = 'hidden';
30755 //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30756 }
30757
30758 r.pathsEnabled = true;
30759 var emptyBb = makeBoundingBox();
30760 var getBoxCenter = function getBoxCenter(bb) {
30761 return {
30762 x: (bb.x1 + bb.x2) / 2,
30763 y: (bb.y1 + bb.y2) / 2
30764 };
30765 };
30766 var getCenterOffset = function getCenterOffset(bb) {
30767 return {
30768 x: -bb.w / 2,
30769 y: -bb.h / 2
30770 };
30771 };
30772 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30773 var _p = ele[0]._private;
30774 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30775 return !same;
30776 };
30777 var getStyleKey = function getStyleKey(ele) {
30778 return ele[0]._private.nodeKey;
30779 };
30780 var getLabelKey = function getLabelKey(ele) {
30781 return ele[0]._private.labelStyleKey;
30782 };
30783 var getSourceLabelKey = function getSourceLabelKey(ele) {
30784 return ele[0]._private.sourceLabelStyleKey;
30785 };
30786 var getTargetLabelKey = function getTargetLabelKey(ele) {
30787 return ele[0]._private.targetLabelStyleKey;
30788 };
30789 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30790 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30791 };
30792 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30793 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30794 };
30795 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30796 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30797 };
30798 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30799 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30800 };
30801 var getElementBox = function getElementBox(ele) {
30802 ele.boundingBox();
30803 return ele[0]._private.bodyBounds;
30804 };
30805 var getLabelBox = function getLabelBox(ele) {
30806 ele.boundingBox();
30807 return ele[0]._private.labelBounds.main || emptyBb;
30808 };
30809 var getSourceLabelBox = function getSourceLabelBox(ele) {
30810 ele.boundingBox();
30811 return ele[0]._private.labelBounds.source || emptyBb;
30812 };
30813 var getTargetLabelBox = function getTargetLabelBox(ele) {
30814 ele.boundingBox();
30815 return ele[0]._private.labelBounds.target || emptyBb;
30816 };
30817 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
30818 return scaledLabelShown;
30819 };
30820 var getElementRotationPoint = function getElementRotationPoint(ele) {
30821 return getBoxCenter(getElementBox(ele));
30822 };
30823 var addTextMargin = function addTextMargin(prefix, pt, ele) {
30824 var pre = prefix ? prefix + '-' : '';
30825 return {
30826 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
30827 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
30828 };
30829 };
30830 var getRsPt = function getRsPt(ele, x, y) {
30831 var rs = ele[0]._private.rscratch;
30832 return {
30833 x: rs[x],
30834 y: rs[y]
30835 };
30836 };
30837 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
30838 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
30839 };
30840 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
30841 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
30842 };
30843 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
30844 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
30845 };
30846 var getElementRotationOffset = function getElementRotationOffset(ele) {
30847 return getCenterOffset(getElementBox(ele));
30848 };
30849 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
30850 return getCenterOffset(getSourceLabelBox(ele));
30851 };
30852 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
30853 return getCenterOffset(getTargetLabelBox(ele));
30854 };
30855 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
30856 var bb = getLabelBox(ele);
30857 var p = getCenterOffset(getLabelBox(ele));
30858 if (ele.isNode()) {
30859 switch (ele.pstyle('text-halign').value) {
30860 case 'left':
30861 p.x = -bb.w;
30862 break;
30863 case 'right':
30864 p.x = 0;
30865 break;
30866 }
30867 switch (ele.pstyle('text-valign').value) {
30868 case 'top':
30869 p.y = -bb.h;
30870 break;
30871 case 'bottom':
30872 p.y = 0;
30873 break;
30874 }
30875 }
30876 return p;
30877 };
30878 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
30879 getKey: getStyleKey,
30880 doesEleInvalidateKey: backgroundTimestampHasChanged,
30881 drawElement: drawElement,
30882 getBoundingBox: getElementBox,
30883 getRotationPoint: getElementRotationPoint,
30884 getRotationOffset: getElementRotationOffset,
30885 allowEdgeTxrCaching: false,
30886 allowParentTxrCaching: false
30887 });
30888 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
30889 getKey: getLabelKey,
30890 drawElement: drawLabel,
30891 getBoundingBox: getLabelBox,
30892 getRotationPoint: getLabelRotationPoint,
30893 getRotationOffset: getLabelRotationOffset,
30894 isVisible: isLabelVisibleAtScale
30895 });
30896 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
30897 getKey: getSourceLabelKey,
30898 drawElement: drawSourceLabel,
30899 getBoundingBox: getSourceLabelBox,
30900 getRotationPoint: getSourceLabelRotationPoint,
30901 getRotationOffset: getSourceLabelRotationOffset,
30902 isVisible: isLabelVisibleAtScale
30903 });
30904 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
30905 getKey: getTargetLabelKey,
30906 drawElement: drawTargetLabel,
30907 getBoundingBox: getTargetLabelBox,
30908 getRotationPoint: getTargetLabelRotationPoint,
30909 getRotationOffset: getTargetLabelRotationOffset,
30910 isVisible: isLabelVisibleAtScale
30911 });
30912 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
30913 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
30914 // each cache should check for sub-key diff to see that the update affects that cache particularly
30915 eleTxrCache.invalidateElements(eles);
30916 lblTxrCache.invalidateElements(eles);
30917 slbTxrCache.invalidateElements(eles);
30918 tlbTxrCache.invalidateElements(eles);
30919
30920 // any change invalidates the layers
30921 lyrTxrCache.invalidateElements(eles);
30922
30923 // update the old bg timestamp so diffs can be done in the ele txr caches
30924 for (var _i = 0; _i < eles.length; _i++) {
30925 var _p = eles[_i]._private;
30926 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
30927 }
30928 });
30929 var refineInLayers = function refineInLayers(reqs) {
30930 for (var i = 0; i < reqs.length; i++) {
30931 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
30932 }
30933 };
30934 eleTxrCache.onDequeue(refineInLayers);
30935 lblTxrCache.onDequeue(refineInLayers);
30936 slbTxrCache.onDequeue(refineInLayers);
30937 tlbTxrCache.onDequeue(refineInLayers);
30938}
30939CRp.redrawHint = function (group, bool) {
30940 var r = this;
30941 switch (group) {
30942 case 'eles':
30943 r.data.canvasNeedsRedraw[CRp.NODE] = bool;
30944 break;
30945 case 'drag':
30946 r.data.canvasNeedsRedraw[CRp.DRAG] = bool;
30947 break;
30948 case 'select':
30949 r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool;
30950 break;
30951 }
30952};
30953
30954// whether to use Path2D caching for drawing
30955var pathsImpld = typeof Path2D !== 'undefined';
30956CRp.path2dEnabled = function (on) {
30957 if (on === undefined) {
30958 return this.pathsEnabled;
30959 }
30960 this.pathsEnabled = on ? true : false;
30961};
30962CRp.usePaths = function () {
30963 return pathsImpld && this.pathsEnabled;
30964};
30965CRp.setImgSmoothing = function (context, bool) {
30966 if (context.imageSmoothingEnabled != null) {
30967 context.imageSmoothingEnabled = bool;
30968 } else {
30969 context.webkitImageSmoothingEnabled = bool;
30970 context.mozImageSmoothingEnabled = bool;
30971 context.msImageSmoothingEnabled = bool;
30972 }
30973};
30974CRp.getImgSmoothing = function (context) {
30975 if (context.imageSmoothingEnabled != null) {
30976 return context.imageSmoothingEnabled;
30977 } else {
30978 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
30979 }
30980};
30981CRp.makeOffscreenCanvas = function (width, height) {
30982 var canvas;
30983 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ("undefined" )) {
30984 canvas = new OffscreenCanvas(width, height);
30985 } else {
30986 var containerWindow = this.cy.window();
30987 var document = containerWindow.document;
30988 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
30989 canvas.width = width;
30990 canvas.height = height;
30991 }
30992 return canvas;
30993};
30994[CRp$a, CRp$9, CRp$8, CRp$7, CRp$6, CRp$5, CRp$4, CRp$3, CRp$2, CRp$1].forEach(function (props) {
30995 extend(CRp, props);
30996});
30997
30998var renderer = [{
30999 name: 'null',
31000 impl: NullRenderer
31001}, {
31002 name: 'base',
31003 impl: BR
31004}, {
31005 name: 'canvas',
31006 impl: CR
31007}];
31008
31009var incExts = [{
31010 type: 'layout',
31011 extensions: layout
31012}, {
31013 type: 'renderer',
31014 extensions: renderer
31015}];
31016
31017// registered extensions to cytoscape, indexed by name
31018var extensions = {};
31019
31020// registered modules for extensions, indexed by name
31021var modules = {};
31022function setExtension(type, name, registrant) {
31023 var ext = registrant;
31024 var overrideErr = function overrideErr(field) {
31025 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31026 };
31027 if (type === 'core') {
31028 if (Core.prototype[name]) {
31029 return overrideErr(name);
31030 } else {
31031 Core.prototype[name] = registrant;
31032 }
31033 } else if (type === 'collection') {
31034 if (Collection.prototype[name]) {
31035 return overrideErr(name);
31036 } else {
31037 Collection.prototype[name] = registrant;
31038 }
31039 } else if (type === 'layout') {
31040 // fill in missing layout functions in the prototype
31041
31042 var Layout = function Layout(options) {
31043 this.options = options;
31044 registrant.call(this, options);
31045
31046 // make sure layout has _private for use w/ std apis like .on()
31047 if (!plainObject(this._private)) {
31048 this._private = {};
31049 }
31050 this._private.cy = options.cy;
31051 this._private.listeners = [];
31052 this.createEmitter();
31053 };
31054 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31055 var optLayoutFns = [];
31056 for (var i = 0; i < optLayoutFns.length; i++) {
31057 var fnName = optLayoutFns[i];
31058 layoutProto[fnName] = layoutProto[fnName] || function () {
31059 return this;
31060 };
31061 }
31062
31063 // either .start() or .run() is defined, so autogen the other
31064 if (layoutProto.start && !layoutProto.run) {
31065 layoutProto.run = function () {
31066 this.start();
31067 return this;
31068 };
31069 } else if (!layoutProto.start && layoutProto.run) {
31070 layoutProto.start = function () {
31071 this.run();
31072 return this;
31073 };
31074 }
31075 var regStop = registrant.prototype.stop;
31076 layoutProto.stop = function () {
31077 var opts = this.options;
31078 if (opts && opts.animate) {
31079 var anis = this.animations;
31080 if (anis) {
31081 for (var _i = 0; _i < anis.length; _i++) {
31082 anis[_i].stop();
31083 }
31084 }
31085 }
31086 if (regStop) {
31087 regStop.call(this);
31088 } else {
31089 this.emit('layoutstop');
31090 }
31091 return this;
31092 };
31093 if (!layoutProto.destroy) {
31094 layoutProto.destroy = function () {
31095 return this;
31096 };
31097 }
31098 layoutProto.cy = function () {
31099 return this._private.cy;
31100 };
31101 var getCy = function getCy(layout) {
31102 return layout._private.cy;
31103 };
31104 var emitterOpts = {
31105 addEventFields: function addEventFields(layout, evt) {
31106 evt.layout = layout;
31107 evt.cy = getCy(layout);
31108 evt.target = layout;
31109 },
31110 bubble: function bubble() {
31111 return true;
31112 },
31113 parent: function parent(layout) {
31114 return getCy(layout);
31115 }
31116 };
31117 extend(layoutProto, {
31118 createEmitter: function createEmitter() {
31119 this._private.emitter = new Emitter(emitterOpts, this);
31120 return this;
31121 },
31122 emitter: function emitter() {
31123 return this._private.emitter;
31124 },
31125 on: function on(evt, cb) {
31126 this.emitter().on(evt, cb);
31127 return this;
31128 },
31129 one: function one(evt, cb) {
31130 this.emitter().one(evt, cb);
31131 return this;
31132 },
31133 once: function once(evt, cb) {
31134 this.emitter().one(evt, cb);
31135 return this;
31136 },
31137 removeListener: function removeListener(evt, cb) {
31138 this.emitter().removeListener(evt, cb);
31139 return this;
31140 },
31141 removeAllListeners: function removeAllListeners() {
31142 this.emitter().removeAllListeners();
31143 return this;
31144 },
31145 emit: function emit(evt, params) {
31146 this.emitter().emit(evt, params);
31147 return this;
31148 }
31149 });
31150 define.eventAliasesOn(layoutProto);
31151 ext = Layout; // replace with our wrapped layout
31152 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31153 // user registered renderers inherit from base
31154
31155 var BaseRenderer = getExtension('renderer', 'base');
31156 var bProto = BaseRenderer.prototype;
31157 var RegistrantRenderer = registrant;
31158 var rProto = registrant.prototype;
31159 var Renderer = function Renderer() {
31160 BaseRenderer.apply(this, arguments);
31161 RegistrantRenderer.apply(this, arguments);
31162 };
31163 var proto = Renderer.prototype;
31164 for (var pName in bProto) {
31165 var pVal = bProto[pName];
31166 var existsInR = rProto[pName] != null;
31167 if (existsInR) {
31168 return overrideErr(pName);
31169 }
31170 proto[pName] = pVal; // take impl from base
31171 }
31172
31173 for (var _pName in rProto) {
31174 proto[_pName] = rProto[_pName]; // take impl from registrant
31175 }
31176
31177 bProto.clientFunctions.forEach(function (name) {
31178 proto[name] = proto[name] || function () {
31179 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31180 };
31181 });
31182 ext = Renderer;
31183 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31184 // to avoid potential prototype pollution
31185 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31186 }
31187 return setMap({
31188 map: extensions,
31189 keys: [type, name],
31190 value: ext
31191 });
31192}
31193function getExtension(type, name) {
31194 return getMap({
31195 map: extensions,
31196 keys: [type, name]
31197 });
31198}
31199function setModule(type, name, moduleType, moduleName, registrant) {
31200 return setMap({
31201 map: modules,
31202 keys: [type, name, moduleType, moduleName],
31203 value: registrant
31204 });
31205}
31206function getModule(type, name, moduleType, moduleName) {
31207 return getMap({
31208 map: modules,
31209 keys: [type, name, moduleType, moduleName]
31210 });
31211}
31212var extension = function extension() {
31213 // e.g. extension('renderer', 'svg')
31214 if (arguments.length === 2) {
31215 return getExtension.apply(null, arguments);
31216 }
31217
31218 // e.g. extension('renderer', 'svg', { ... })
31219 else if (arguments.length === 3) {
31220 return setExtension.apply(null, arguments);
31221 }
31222
31223 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31224 else if (arguments.length === 4) {
31225 return getModule.apply(null, arguments);
31226 }
31227
31228 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31229 else if (arguments.length === 5) {
31230 return setModule.apply(null, arguments);
31231 } else {
31232 error('Invalid extension access syntax');
31233 }
31234};
31235
31236// allows a core instance to access extensions internally
31237Core.prototype.extension = extension;
31238
31239// included extensions
31240incExts.forEach(function (group) {
31241 group.extensions.forEach(function (ext) {
31242 setExtension(group.type, ext.name, ext.impl);
31243 });
31244});
31245
31246// a dummy stylesheet object that doesn't need a reference to the core
31247// (useful for init)
31248var Stylesheet = function Stylesheet() {
31249 if (!(this instanceof Stylesheet)) {
31250 return new Stylesheet();
31251 }
31252 this.length = 0;
31253};
31254var sheetfn = Stylesheet.prototype;
31255sheetfn.instanceString = function () {
31256 return 'stylesheet';
31257};
31258
31259// just store the selector to be parsed later
31260sheetfn.selector = function (selector) {
31261 var i = this.length++;
31262 this[i] = {
31263 selector: selector,
31264 properties: []
31265 };
31266 return this; // chaining
31267};
31268
31269// just store the property to be parsed later
31270sheetfn.css = function (name, value) {
31271 var i = this.length - 1;
31272 if (string(name)) {
31273 this[i].properties.push({
31274 name: name,
31275 value: value
31276 });
31277 } else if (plainObject(name)) {
31278 var map = name;
31279 var propNames = Object.keys(map);
31280 for (var j = 0; j < propNames.length; j++) {
31281 var key = propNames[j];
31282 var mapVal = map[key];
31283 if (mapVal == null) {
31284 continue;
31285 }
31286 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31287 if (prop == null) {
31288 continue;
31289 }
31290 var _name = prop.name;
31291 var _value = mapVal;
31292 this[i].properties.push({
31293 name: _name,
31294 value: _value
31295 });
31296 }
31297 }
31298 return this; // chaining
31299};
31300
31301sheetfn.style = sheetfn.css;
31302
31303// generate a real style object from the dummy stylesheet
31304sheetfn.generateStyle = function (cy) {
31305 var style = new Style(cy);
31306 return this.appendToStyle(style);
31307};
31308
31309// append a dummy stylesheet object on a real style object
31310sheetfn.appendToStyle = function (style) {
31311 for (var i = 0; i < this.length; i++) {
31312 var context = this[i];
31313 var selector = context.selector;
31314 var props = context.properties;
31315 style.selector(selector); // apply selector
31316
31317 for (var j = 0; j < props.length; j++) {
31318 var prop = props[j];
31319 style.css(prop.name, prop.value); // apply property
31320 }
31321 }
31322
31323 return style;
31324};
31325
31326var version = "3.30.2";
31327
31328var cytoscape = function cytoscape(options) {
31329 // if no options specified, use default
31330 if (options === undefined) {
31331 options = {};
31332 }
31333
31334 // create instance
31335 if (plainObject(options)) {
31336 return new Core(options);
31337 }
31338
31339 // allow for registration of extensions
31340 else if (string(options)) {
31341 return extension.apply(extension, arguments);
31342 }
31343};
31344
31345// e.g. cytoscape.use( require('cytoscape-foo'), bar )
31346cytoscape.use = function (ext) {
31347 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31348
31349 args.unshift(cytoscape); // cytoscape is first arg to ext
31350
31351 ext.apply(null, args);
31352 return this;
31353};
31354cytoscape.warnings = function (bool) {
31355 return warnings(bool);
31356};
31357
31358// replaced by build system
31359cytoscape.version = version;
31360
31361// expose public apis (mostly for extensions)
31362cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31363
31364export { cytoscape as default };