UNPKG

872 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2022, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23'use strict';
24
25function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
26
27var util = _interopDefault(require('lodash.debounce'));
28var Heap = _interopDefault(require('heap'));
29var get = _interopDefault(require('lodash.get'));
30var set = _interopDefault(require('lodash.set'));
31var toPath = _interopDefault(require('lodash.topath'));
32
33function _typeof(obj) {
34 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
35 _typeof = function (obj) {
36 return typeof obj;
37 };
38 } else {
39 _typeof = function (obj) {
40 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
41 };
42 }
43
44 return _typeof(obj);
45}
46
47function _classCallCheck(instance, Constructor) {
48 if (!(instance instanceof Constructor)) {
49 throw new TypeError("Cannot call a class as a function");
50 }
51}
52
53function _defineProperties(target, props) {
54 for (var i = 0; i < props.length; i++) {
55 var descriptor = props[i];
56 descriptor.enumerable = descriptor.enumerable || false;
57 descriptor.configurable = true;
58 if ("value" in descriptor) descriptor.writable = true;
59 Object.defineProperty(target, descriptor.key, descriptor);
60 }
61}
62
63function _createClass(Constructor, protoProps, staticProps) {
64 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
65 if (staticProps) _defineProperties(Constructor, staticProps);
66 return Constructor;
67}
68
69function _defineProperty(obj, key, value) {
70 if (key in obj) {
71 Object.defineProperty(obj, key, {
72 value: value,
73 enumerable: true,
74 configurable: true,
75 writable: true
76 });
77 } else {
78 obj[key] = value;
79 }
80
81 return obj;
82}
83
84function _slicedToArray(arr, i) {
85 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
86}
87
88function _arrayWithHoles(arr) {
89 if (Array.isArray(arr)) return arr;
90}
91
92function _iterableToArrayLimit(arr, i) {
93 var _arr = [];
94 var _n = true;
95 var _d = false;
96 var _e = undefined;
97
98 try {
99 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
100 _arr.push(_s.value);
101
102 if (i && _arr.length === i) break;
103 }
104 } catch (err) {
105 _d = true;
106 _e = err;
107 } finally {
108 try {
109 if (!_n && _i["return"] != null) _i["return"]();
110 } finally {
111 if (_d) throw _e;
112 }
113 }
114
115 return _arr;
116}
117
118function _nonIterableRest() {
119 throw new TypeError("Invalid attempt to destructure non-iterable instance");
120}
121
122var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
123
124var navigator = window$1 ? window$1.navigator : null;
125var document$1 = window$1 ? window$1.document : null;
126
127var typeofstr = _typeof('');
128
129var typeofobj = _typeof({});
130
131var typeoffn = _typeof(function () {});
132
133var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
134
135var instanceStr = function instanceStr(obj) {
136 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
137};
138
139var string = function string(obj) {
140 return obj != null && _typeof(obj) == typeofstr;
141};
142var fn = function fn(obj) {
143 return obj != null && _typeof(obj) === typeoffn;
144};
145var array = function array(obj) {
146 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
147};
148var plainObject = function plainObject(obj) {
149 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
150};
151var object = function object(obj) {
152 return obj != null && _typeof(obj) === typeofobj;
153};
154var number = function number(obj) {
155 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
156};
157var integer = function integer(obj) {
158 return number(obj) && Math.floor(obj) === obj;
159};
160var htmlElement = function htmlElement(obj) {
161 if ('undefined' === typeofhtmlele) {
162 return undefined;
163 } else {
164 return null != obj && obj instanceof HTMLElement;
165 }
166};
167var elementOrCollection = function elementOrCollection(obj) {
168 return element(obj) || collection(obj);
169};
170var element = function element(obj) {
171 return instanceStr(obj) === 'collection' && obj._private.single;
172};
173var collection = function collection(obj) {
174 return instanceStr(obj) === 'collection' && !obj._private.single;
175};
176var core = function core(obj) {
177 return instanceStr(obj) === 'core';
178};
179var stylesheet = function stylesheet(obj) {
180 return instanceStr(obj) === 'stylesheet';
181};
182var event = function event(obj) {
183 return instanceStr(obj) === 'event';
184};
185var emptyString = function emptyString(obj) {
186 if (obj === undefined || obj === null) {
187 // null is empty
188 return true;
189 } else if (obj === '' || obj.match(/^\s+$/)) {
190 return true; // empty string is empty
191 }
192
193 return false; // otherwise, we don't know what we've got
194};
195var domElement = function domElement(obj) {
196 if (typeof HTMLElement === 'undefined') {
197 return false; // we're not in a browser so it doesn't matter
198 } else {
199 return obj instanceof HTMLElement;
200 }
201};
202var boundingBox = function boundingBox(obj) {
203 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
204};
205var promise = function promise(obj) {
206 return object(obj) && fn(obj.then);
207};
208var ms = function ms() {
209 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
210}; // probably a better way to detect this...
211
212var memoize = function memoize(fn, keyFn) {
213 if (!keyFn) {
214 keyFn = function keyFn() {
215 if (arguments.length === 1) {
216 return arguments[0];
217 } else if (arguments.length === 0) {
218 return 'undefined';
219 }
220
221 var args = [];
222
223 for (var i = 0; i < arguments.length; i++) {
224 args.push(arguments[i]);
225 }
226
227 return args.join('$');
228 };
229 }
230
231 var memoizedFn = function memoizedFn() {
232 var self = this;
233 var args = arguments;
234 var ret;
235 var k = keyFn.apply(self, args);
236 var cache = memoizedFn.cache;
237
238 if (!(ret = cache[k])) {
239 ret = cache[k] = fn.apply(self, args);
240 }
241
242 return ret;
243 };
244
245 memoizedFn.cache = {};
246 return memoizedFn;
247};
248
249var camel2dash = memoize(function (str) {
250 return str.replace(/([A-Z])/g, function (v) {
251 return '-' + v.toLowerCase();
252 });
253});
254var dash2camel = memoize(function (str) {
255 return str.replace(/(-\w)/g, function (v) {
256 return v[1].toUpperCase();
257 });
258});
259var prependCamel = memoize(function (prefix, str) {
260 return prefix + str[0].toUpperCase() + str.substring(1);
261}, function (prefix, str) {
262 return prefix + '$' + str;
263});
264var capitalize = function capitalize(str) {
265 if (emptyString(str)) {
266 return str;
267 }
268
269 return str.charAt(0).toUpperCase() + str.substring(1);
270};
271
272var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
273var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
274var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
275var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
276var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
277var hex3 = '\\#[0-9a-fA-F]{3}';
278var hex6 = '\\#[0-9a-fA-F]{6}';
279
280var ascending = function ascending(a, b) {
281 if (a < b) {
282 return -1;
283 } else if (a > b) {
284 return 1;
285 } else {
286 return 0;
287 }
288};
289var descending = function descending(a, b) {
290 return -1 * ascending(a, b);
291};
292
293var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
294 var args = arguments;
295
296 for (var i = 1; i < args.length; i++) {
297 var obj = args[i];
298
299 if (obj == null) {
300 continue;
301 }
302
303 var keys = Object.keys(obj);
304
305 for (var j = 0; j < keys.length; j++) {
306 var k = keys[j];
307 tgt[k] = obj[k];
308 }
309 }
310
311 return tgt;
312};
313
314var hex2tuple = function hex2tuple(hex) {
315 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
316 return;
317 }
318
319 var shortHex = hex.length === 4;
320 var r, g, b;
321 var base = 16;
322
323 if (shortHex) {
324 r = parseInt(hex[1] + hex[1], base);
325 g = parseInt(hex[2] + hex[2], base);
326 b = parseInt(hex[3] + hex[3], base);
327 } else {
328 r = parseInt(hex[1] + hex[2], base);
329 g = parseInt(hex[3] + hex[4], base);
330 b = parseInt(hex[5] + hex[6], base);
331 }
332
333 return [r, g, b];
334}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
335
336var hsl2tuple = function hsl2tuple(hsl) {
337 var ret;
338 var h, s, l, a, r, g, b;
339
340 function hue2rgb(p, q, t) {
341 if (t < 0) t += 1;
342 if (t > 1) t -= 1;
343 if (t < 1 / 6) return p + (q - p) * 6 * t;
344 if (t < 1 / 2) return q;
345 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
346 return p;
347 }
348
349 var m = new RegExp('^' + hsla + '$').exec(hsl);
350
351 if (m) {
352 // get hue
353 h = parseInt(m[1]);
354
355 if (h < 0) {
356 h = (360 - -1 * h % 360) % 360;
357 } else if (h > 360) {
358 h = h % 360;
359 }
360
361 h /= 360; // normalise on [0, 1]
362
363 s = parseFloat(m[2]);
364
365 if (s < 0 || s > 100) {
366 return;
367 } // saturation is [0, 100]
368
369
370 s = s / 100; // normalise on [0, 1]
371
372 l = parseFloat(m[3]);
373
374 if (l < 0 || l > 100) {
375 return;
376 } // lightness is [0, 100]
377
378
379 l = l / 100; // normalise on [0, 1]
380
381 a = m[4];
382
383 if (a !== undefined) {
384 a = parseFloat(a);
385
386 if (a < 0 || a > 1) {
387 return;
388 } // alpha is [0, 1]
389
390 } // now, convert to rgb
391 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
392
393
394 if (s === 0) {
395 r = g = b = Math.round(l * 255); // achromatic
396 } else {
397 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
398 var p = 2 * l - q;
399 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
400 g = Math.round(255 * hue2rgb(p, q, h));
401 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
402 }
403
404 ret = [r, g, b, a];
405 }
406
407 return ret;
408}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
409
410var rgb2tuple = function rgb2tuple(rgb) {
411 var ret;
412 var m = new RegExp('^' + rgba + '$').exec(rgb);
413
414 if (m) {
415 ret = [];
416 var isPct = [];
417
418 for (var i = 1; i <= 3; i++) {
419 var channel = m[i];
420
421 if (channel[channel.length - 1] === '%') {
422 isPct[i] = true;
423 }
424
425 channel = parseFloat(channel);
426
427 if (isPct[i]) {
428 channel = channel / 100 * 255; // normalise to [0, 255]
429 }
430
431 if (channel < 0 || channel > 255) {
432 return;
433 } // invalid channel value
434
435
436 ret.push(Math.floor(channel));
437 }
438
439 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
440 var allArePct = isPct[1] && isPct[2] && isPct[3];
441
442 if (atLeastOneIsPct && !allArePct) {
443 return;
444 } // must all be percent values if one is
445
446
447 var alpha = m[4];
448
449 if (alpha !== undefined) {
450 alpha = parseFloat(alpha);
451
452 if (alpha < 0 || alpha > 1) {
453 return;
454 } // invalid alpha value
455
456
457 ret.push(alpha);
458 }
459 }
460
461 return ret;
462};
463var colorname2tuple = function colorname2tuple(color) {
464 return colors[color.toLowerCase()];
465};
466var color2tuple = function color2tuple(color) {
467 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
468};
469var colors = {
470 // special colour names
471 transparent: [0, 0, 0, 0],
472 // NB alpha === 0
473 // regular colours
474 aliceblue: [240, 248, 255],
475 antiquewhite: [250, 235, 215],
476 aqua: [0, 255, 255],
477 aquamarine: [127, 255, 212],
478 azure: [240, 255, 255],
479 beige: [245, 245, 220],
480 bisque: [255, 228, 196],
481 black: [0, 0, 0],
482 blanchedalmond: [255, 235, 205],
483 blue: [0, 0, 255],
484 blueviolet: [138, 43, 226],
485 brown: [165, 42, 42],
486 burlywood: [222, 184, 135],
487 cadetblue: [95, 158, 160],
488 chartreuse: [127, 255, 0],
489 chocolate: [210, 105, 30],
490 coral: [255, 127, 80],
491 cornflowerblue: [100, 149, 237],
492 cornsilk: [255, 248, 220],
493 crimson: [220, 20, 60],
494 cyan: [0, 255, 255],
495 darkblue: [0, 0, 139],
496 darkcyan: [0, 139, 139],
497 darkgoldenrod: [184, 134, 11],
498 darkgray: [169, 169, 169],
499 darkgreen: [0, 100, 0],
500 darkgrey: [169, 169, 169],
501 darkkhaki: [189, 183, 107],
502 darkmagenta: [139, 0, 139],
503 darkolivegreen: [85, 107, 47],
504 darkorange: [255, 140, 0],
505 darkorchid: [153, 50, 204],
506 darkred: [139, 0, 0],
507 darksalmon: [233, 150, 122],
508 darkseagreen: [143, 188, 143],
509 darkslateblue: [72, 61, 139],
510 darkslategray: [47, 79, 79],
511 darkslategrey: [47, 79, 79],
512 darkturquoise: [0, 206, 209],
513 darkviolet: [148, 0, 211],
514 deeppink: [255, 20, 147],
515 deepskyblue: [0, 191, 255],
516 dimgray: [105, 105, 105],
517 dimgrey: [105, 105, 105],
518 dodgerblue: [30, 144, 255],
519 firebrick: [178, 34, 34],
520 floralwhite: [255, 250, 240],
521 forestgreen: [34, 139, 34],
522 fuchsia: [255, 0, 255],
523 gainsboro: [220, 220, 220],
524 ghostwhite: [248, 248, 255],
525 gold: [255, 215, 0],
526 goldenrod: [218, 165, 32],
527 gray: [128, 128, 128],
528 grey: [128, 128, 128],
529 green: [0, 128, 0],
530 greenyellow: [173, 255, 47],
531 honeydew: [240, 255, 240],
532 hotpink: [255, 105, 180],
533 indianred: [205, 92, 92],
534 indigo: [75, 0, 130],
535 ivory: [255, 255, 240],
536 khaki: [240, 230, 140],
537 lavender: [230, 230, 250],
538 lavenderblush: [255, 240, 245],
539 lawngreen: [124, 252, 0],
540 lemonchiffon: [255, 250, 205],
541 lightblue: [173, 216, 230],
542 lightcoral: [240, 128, 128],
543 lightcyan: [224, 255, 255],
544 lightgoldenrodyellow: [250, 250, 210],
545 lightgray: [211, 211, 211],
546 lightgreen: [144, 238, 144],
547 lightgrey: [211, 211, 211],
548 lightpink: [255, 182, 193],
549 lightsalmon: [255, 160, 122],
550 lightseagreen: [32, 178, 170],
551 lightskyblue: [135, 206, 250],
552 lightslategray: [119, 136, 153],
553 lightslategrey: [119, 136, 153],
554 lightsteelblue: [176, 196, 222],
555 lightyellow: [255, 255, 224],
556 lime: [0, 255, 0],
557 limegreen: [50, 205, 50],
558 linen: [250, 240, 230],
559 magenta: [255, 0, 255],
560 maroon: [128, 0, 0],
561 mediumaquamarine: [102, 205, 170],
562 mediumblue: [0, 0, 205],
563 mediumorchid: [186, 85, 211],
564 mediumpurple: [147, 112, 219],
565 mediumseagreen: [60, 179, 113],
566 mediumslateblue: [123, 104, 238],
567 mediumspringgreen: [0, 250, 154],
568 mediumturquoise: [72, 209, 204],
569 mediumvioletred: [199, 21, 133],
570 midnightblue: [25, 25, 112],
571 mintcream: [245, 255, 250],
572 mistyrose: [255, 228, 225],
573 moccasin: [255, 228, 181],
574 navajowhite: [255, 222, 173],
575 navy: [0, 0, 128],
576 oldlace: [253, 245, 230],
577 olive: [128, 128, 0],
578 olivedrab: [107, 142, 35],
579 orange: [255, 165, 0],
580 orangered: [255, 69, 0],
581 orchid: [218, 112, 214],
582 palegoldenrod: [238, 232, 170],
583 palegreen: [152, 251, 152],
584 paleturquoise: [175, 238, 238],
585 palevioletred: [219, 112, 147],
586 papayawhip: [255, 239, 213],
587 peachpuff: [255, 218, 185],
588 peru: [205, 133, 63],
589 pink: [255, 192, 203],
590 plum: [221, 160, 221],
591 powderblue: [176, 224, 230],
592 purple: [128, 0, 128],
593 red: [255, 0, 0],
594 rosybrown: [188, 143, 143],
595 royalblue: [65, 105, 225],
596 saddlebrown: [139, 69, 19],
597 salmon: [250, 128, 114],
598 sandybrown: [244, 164, 96],
599 seagreen: [46, 139, 87],
600 seashell: [255, 245, 238],
601 sienna: [160, 82, 45],
602 silver: [192, 192, 192],
603 skyblue: [135, 206, 235],
604 slateblue: [106, 90, 205],
605 slategray: [112, 128, 144],
606 slategrey: [112, 128, 144],
607 snow: [255, 250, 250],
608 springgreen: [0, 255, 127],
609 steelblue: [70, 130, 180],
610 tan: [210, 180, 140],
611 teal: [0, 128, 128],
612 thistle: [216, 191, 216],
613 tomato: [255, 99, 71],
614 turquoise: [64, 224, 208],
615 violet: [238, 130, 238],
616 wheat: [245, 222, 179],
617 white: [255, 255, 255],
618 whitesmoke: [245, 245, 245],
619 yellow: [255, 255, 0],
620 yellowgreen: [154, 205, 50]
621};
622
623var setMap = function setMap(options) {
624 var obj = options.map;
625 var keys = options.keys;
626 var l = keys.length;
627
628 for (var i = 0; i < l; i++) {
629 var key = keys[i];
630
631 if (plainObject(key)) {
632 throw Error('Tried to set map with object key');
633 }
634
635 if (i < keys.length - 1) {
636 // extend the map if necessary
637 if (obj[key] == null) {
638 obj[key] = {};
639 }
640
641 obj = obj[key];
642 } else {
643 // set the value
644 obj[key] = options.value;
645 }
646 }
647}; // gets the value in a map even if it's not built in places
648
649var getMap = function getMap(options) {
650 var obj = options.map;
651 var keys = options.keys;
652 var l = keys.length;
653
654 for (var i = 0; i < l; i++) {
655 var key = keys[i];
656
657 if (plainObject(key)) {
658 throw Error('Tried to get map with object key');
659 }
660
661 obj = obj[key];
662
663 if (obj == null) {
664 return obj;
665 }
666 }
667
668 return obj;
669}; // deletes the entry in the map
670
671var performance = window$1 ? window$1.performance : null;
672var pnow = performance && performance.now ? function () {
673 return performance.now();
674} : function () {
675 return Date.now();
676};
677
678var raf = function () {
679 if (window$1) {
680 if (window$1.requestAnimationFrame) {
681 return function (fn) {
682 window$1.requestAnimationFrame(fn);
683 };
684 } else if (window$1.mozRequestAnimationFrame) {
685 return function (fn) {
686 window$1.mozRequestAnimationFrame(fn);
687 };
688 } else if (window$1.webkitRequestAnimationFrame) {
689 return function (fn) {
690 window$1.webkitRequestAnimationFrame(fn);
691 };
692 } else if (window$1.msRequestAnimationFrame) {
693 return function (fn) {
694 window$1.msRequestAnimationFrame(fn);
695 };
696 }
697 }
698
699 return function (fn) {
700 if (fn) {
701 setTimeout(function () {
702 fn(pnow());
703 }, 1000 / 60);
704 }
705 };
706}();
707
708var requestAnimationFrame = function requestAnimationFrame(fn) {
709 return raf(fn);
710};
711var performanceNow = pnow;
712
713var DEFAULT_HASH_SEED = 9261;
714var K = 65599; // 37 also works pretty well
715
716var DEFAULT_HASH_SEED_ALT = 5381;
717var hashIterableInts = function hashIterableInts(iterator) {
718 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
719 // sdbm/string-hash
720 var hash = seed;
721 var entry;
722
723 for (;;) {
724 entry = iterator.next();
725
726 if (entry.done) {
727 break;
728 }
729
730 hash = hash * K + entry.value | 0;
731 }
732
733 return hash;
734};
735var hashInt = function hashInt(num) {
736 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
737 // sdbm/string-hash
738 return seed * K + num | 0;
739};
740var hashIntAlt = function hashIntAlt(num) {
741 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
742 // djb2/string-hash
743 return (seed << 5) + seed + num | 0;
744};
745var combineHashes = function combineHashes(hash1, hash2) {
746 return hash1 * 0x200000 + hash2;
747};
748var combineHashesArray = function combineHashesArray(hashes) {
749 return hashes[0] * 0x200000 + hashes[1];
750};
751var hashArrays = function hashArrays(hashes1, hashes2) {
752 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
753};
754var hashIntsArray = function hashIntsArray(ints, seed) {
755 var entry = {
756 value: 0,
757 done: false
758 };
759 var i = 0;
760 var length = ints.length;
761 var iterator = {
762 next: function next() {
763 if (i < length) {
764 entry.value = ints[i++];
765 } else {
766 entry.done = true;
767 }
768
769 return entry;
770 }
771 };
772 return hashIterableInts(iterator, seed);
773};
774var hashString = function hashString(str, seed) {
775 var entry = {
776 value: 0,
777 done: false
778 };
779 var i = 0;
780 var length = str.length;
781 var iterator = {
782 next: function next() {
783 if (i < length) {
784 entry.value = str.charCodeAt(i++);
785 } else {
786 entry.done = true;
787 }
788
789 return entry;
790 }
791 };
792 return hashIterableInts(iterator, seed);
793};
794var hashStrings = function hashStrings() {
795 return hashStringsArray(arguments);
796};
797var hashStringsArray = function hashStringsArray(strs) {
798 var hash;
799
800 for (var i = 0; i < strs.length; i++) {
801 var str = strs[i];
802
803 if (i === 0) {
804 hash = hashString(str);
805 } else {
806 hash = hashString(str, hash);
807 }
808 }
809
810 return hash;
811};
812
813/*global console */
814var warningsEnabled = true;
815var warnSupported = console.warn != null; // eslint-disable-line no-console
816
817var traceSupported = console.trace != null; // eslint-disable-line no-console
818
819var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
820var trueify = function trueify() {
821 return true;
822};
823var falsify = function falsify() {
824 return false;
825};
826var zeroify = function zeroify() {
827 return 0;
828};
829var noop = function noop() {};
830var error = function error(msg) {
831 throw new Error(msg);
832};
833var warnings = function warnings(enabled) {
834 if (enabled !== undefined) {
835 warningsEnabled = !!enabled;
836 } else {
837 return warningsEnabled;
838 }
839};
840var warn = function warn(msg) {
841 /* eslint-disable no-console */
842 if (!warnings()) {
843 return;
844 }
845
846 if (warnSupported) {
847 console.warn(msg);
848 } else {
849 console.log(msg);
850
851 if (traceSupported) {
852 console.trace();
853 }
854 }
855};
856/* eslint-enable */
857
858var clone = function clone(obj) {
859 return extend({}, obj);
860}; // gets a shallow copy of the argument
861
862var copy = function copy(obj) {
863 if (obj == null) {
864 return obj;
865 }
866
867 if (array(obj)) {
868 return obj.slice();
869 } else if (plainObject(obj)) {
870 return clone(obj);
871 } else {
872 return obj;
873 }
874};
875var copyArray = function copyArray(arr) {
876 return arr.slice();
877};
878var uuid = function uuid(a, b
879/* placeholders */
880) {
881 for ( // loop :)
882 b = a = ''; // b - result , a - numeric letiable
883 a++ < 36; //
884 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
885 ? // return a random number or 4
886 (a ^ 15 // if "a" is not 15
887 ? // genetate a random number from 0 to 15
888 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
889 : 4 // otherwise 4
890 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
891 ) {
892 }
893
894 return b;
895};
896var _staticEmptyObject = {};
897var staticEmptyObject = function staticEmptyObject() {
898 return _staticEmptyObject;
899};
900var defaults = function defaults(_defaults) {
901 var keys = Object.keys(_defaults);
902 return function (opts) {
903 var filledOpts = {};
904
905 for (var i = 0; i < keys.length; i++) {
906 var key = keys[i];
907 var optVal = opts == null ? undefined : opts[key];
908 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
909 }
910
911 return filledOpts;
912 };
913};
914var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
915 for (var i = arr.length - 1; i >= 0; i--) {
916 if (arr[i] === ele) {
917 arr.splice(i, 1);
918
919 if (oneCopy) {
920 break;
921 }
922 }
923 }
924};
925var clearArray = function clearArray(arr) {
926 arr.splice(0, arr.length);
927};
928var push = function push(arr, otherArr) {
929 for (var i = 0; i < otherArr.length; i++) {
930 var el = otherArr[i];
931 arr.push(el);
932 }
933};
934var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
935 if (prefix) {
936 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
937 }
938
939 return obj[propName];
940};
941var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
942 if (prefix) {
943 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
944 }
945
946 obj[propName] = value;
947};
948
949/* global Map */
950var ObjectMap =
951/*#__PURE__*/
952function () {
953 function ObjectMap() {
954 _classCallCheck(this, ObjectMap);
955
956 this._obj = {};
957 }
958
959 _createClass(ObjectMap, [{
960 key: "set",
961 value: function set(key, val) {
962 this._obj[key] = val;
963 return this;
964 }
965 }, {
966 key: "delete",
967 value: function _delete(key) {
968 this._obj[key] = undefined;
969 return this;
970 }
971 }, {
972 key: "clear",
973 value: function clear() {
974 this._obj = {};
975 }
976 }, {
977 key: "has",
978 value: function has(key) {
979 return this._obj[key] !== undefined;
980 }
981 }, {
982 key: "get",
983 value: function get(key) {
984 return this._obj[key];
985 }
986 }]);
987
988 return ObjectMap;
989}();
990
991var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
992
993/* global Set */
994var undef = "undefined" ;
995
996var ObjectSet =
997/*#__PURE__*/
998function () {
999 function ObjectSet(arrayOrObjectSet) {
1000 _classCallCheck(this, ObjectSet);
1001
1002 this._obj = Object.create(null);
1003 this.size = 0;
1004
1005 if (arrayOrObjectSet != null) {
1006 var arr;
1007
1008 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1009 arr = arrayOrObjectSet.toArray();
1010 } else {
1011 arr = arrayOrObjectSet;
1012 }
1013
1014 for (var i = 0; i < arr.length; i++) {
1015 this.add(arr[i]);
1016 }
1017 }
1018 }
1019
1020 _createClass(ObjectSet, [{
1021 key: "instanceString",
1022 value: function instanceString() {
1023 return 'set';
1024 }
1025 }, {
1026 key: "add",
1027 value: function add(val) {
1028 var o = this._obj;
1029
1030 if (o[val] !== 1) {
1031 o[val] = 1;
1032 this.size++;
1033 }
1034 }
1035 }, {
1036 key: "delete",
1037 value: function _delete(val) {
1038 var o = this._obj;
1039
1040 if (o[val] === 1) {
1041 o[val] = 0;
1042 this.size--;
1043 }
1044 }
1045 }, {
1046 key: "clear",
1047 value: function clear() {
1048 this._obj = Object.create(null);
1049 }
1050 }, {
1051 key: "has",
1052 value: function has(val) {
1053 return this._obj[val] === 1;
1054 }
1055 }, {
1056 key: "toArray",
1057 value: function toArray() {
1058 var _this = this;
1059
1060 return Object.keys(this._obj).filter(function (key) {
1061 return _this.has(key);
1062 });
1063 }
1064 }, {
1065 key: "forEach",
1066 value: function forEach(callback, thisArg) {
1067 return this.toArray().forEach(callback, thisArg);
1068 }
1069 }]);
1070
1071 return ObjectSet;
1072}();
1073
1074var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1075
1076var Element = function Element(cy, params) {
1077 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1078
1079 if (cy === undefined || params === undefined || !core(cy)) {
1080 error('An element must have a core reference and parameters set');
1081 return;
1082 }
1083
1084 var group = params.group; // try to automatically infer the group if unspecified
1085
1086 if (group == null) {
1087 if (params.data && params.data.source != null && params.data.target != null) {
1088 group = 'edges';
1089 } else {
1090 group = 'nodes';
1091 }
1092 } // validate group
1093
1094
1095 if (group !== 'nodes' && group !== 'edges') {
1096 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1097 return;
1098 } // make the element array-like, just like a collection
1099
1100
1101 this.length = 1;
1102 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1103
1104 var _p = this._private = {
1105 cy: cy,
1106 single: true,
1107 // indicates this is an element
1108 data: params.data || {},
1109 // data object
1110 position: params.position || {
1111 x: 0,
1112 y: 0
1113 },
1114 // (x, y) position pair
1115 autoWidth: undefined,
1116 // width and height of nodes calculated by the renderer when set to special 'auto' value
1117 autoHeight: undefined,
1118 autoPadding: undefined,
1119 compoundBoundsClean: false,
1120 // whether the compound dimensions need to be recalculated the next time dimensions are read
1121 listeners: [],
1122 // array of bound listeners
1123 group: group,
1124 // string; 'nodes' or 'edges'
1125 style: {},
1126 // properties as set by the style
1127 rstyle: {},
1128 // properties for style sent from the renderer to the core
1129 styleCxts: [],
1130 // applied style contexts from the styler
1131 styleKeys: {},
1132 // per-group keys of style property values
1133 removed: true,
1134 // whether it's inside the vis; true if removed (set true here since we call restore)
1135 selected: params.selected ? true : false,
1136 // whether it's selected
1137 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1138 // whether it's selectable
1139 locked: params.locked ? true : false,
1140 // whether the element is locked (cannot be moved)
1141 grabbed: false,
1142 // whether the element is grabbed by the mouse; renderer sets this privately
1143 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1144 // whether the element can be grabbed
1145 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1146 // whether the element has passthrough panning enabled
1147 active: false,
1148 // whether the element is active from user interaction
1149 classes: new Set$1(),
1150 // map ( className => true )
1151 animation: {
1152 // object for currently-running animations
1153 current: [],
1154 queue: []
1155 },
1156 rscratch: {},
1157 // object in which the renderer can store information
1158 scratch: params.scratch || {},
1159 // scratch objects
1160 edges: [],
1161 // array of connected edges
1162 children: [],
1163 // array of children
1164 parent: null,
1165 // parent ref
1166 traversalCache: {},
1167 // cache of output of traversal functions
1168 backgrounding: false,
1169 // whether background images are loading
1170 bbCache: null,
1171 // cache of the current bounding box
1172 bbCacheShift: {
1173 x: 0,
1174 y: 0
1175 },
1176 // shift applied to cached bb to be applied on next get
1177 bodyBounds: null,
1178 // bounds cache of element body, w/o overlay
1179 overlayBounds: null,
1180 // bounds cache of element body, including overlay
1181 labelBounds: {
1182 // bounds cache of labels
1183 all: null,
1184 source: null,
1185 target: null,
1186 main: null
1187 },
1188 arrowBounds: {
1189 // bounds cache of edge arrows
1190 source: null,
1191 target: null,
1192 'mid-source': null,
1193 'mid-target': null
1194 }
1195 };
1196
1197 if (_p.position.x == null) {
1198 _p.position.x = 0;
1199 }
1200
1201 if (_p.position.y == null) {
1202 _p.position.y = 0;
1203 } // renderedPosition overrides if specified
1204
1205
1206 if (params.renderedPosition) {
1207 var rpos = params.renderedPosition;
1208 var pan = cy.pan();
1209 var zoom = cy.zoom();
1210 _p.position = {
1211 x: (rpos.x - pan.x) / zoom,
1212 y: (rpos.y - pan.y) / zoom
1213 };
1214 }
1215
1216 var classes = [];
1217
1218 if (array(params.classes)) {
1219 classes = params.classes;
1220 } else if (string(params.classes)) {
1221 classes = params.classes.split(/\s+/);
1222 }
1223
1224 for (var i = 0, l = classes.length; i < l; i++) {
1225 var cls = classes[i];
1226
1227 if (!cls || cls === '') {
1228 continue;
1229 }
1230
1231 _p.classes.add(cls);
1232 }
1233
1234 this.createEmitter();
1235 var bypass = params.style || params.css;
1236
1237 if (bypass) {
1238 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1239 this.style(bypass);
1240 }
1241
1242 if (restore === undefined || restore) {
1243 this.restore();
1244 }
1245};
1246
1247var defineSearch = function defineSearch(params) {
1248 params = {
1249 bfs: params.bfs || !params.dfs,
1250 dfs: params.dfs || !params.bfs
1251 }; // from pseudocode on wikipedia
1252
1253 return function searchFn(roots, fn$1, directed) {
1254 var options;
1255
1256 if (plainObject(roots) && !elementOrCollection(roots)) {
1257 options = roots;
1258 roots = options.roots || options.root;
1259 fn$1 = options.visit;
1260 directed = options.directed;
1261 }
1262
1263 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1264 fn$1 = fn(fn$1) ? fn$1 : function () {};
1265 var cy = this._private.cy;
1266 var v = roots = string(roots) ? this.filter(roots) : roots;
1267 var Q = [];
1268 var connectedNodes = [];
1269 var connectedBy = {};
1270 var id2depth = {};
1271 var V = {};
1272 var j = 0;
1273 var found;
1274
1275 var _this$byGroup = this.byGroup(),
1276 nodes = _this$byGroup.nodes,
1277 edges = _this$byGroup.edges; // enqueue v
1278
1279
1280 for (var i = 0; i < v.length; i++) {
1281 var vi = v[i];
1282 var viId = vi.id();
1283
1284 if (vi.isNode()) {
1285 Q.unshift(vi);
1286
1287 if (params.bfs) {
1288 V[viId] = true;
1289 connectedNodes.push(vi);
1290 }
1291
1292 id2depth[viId] = 0;
1293 }
1294 }
1295
1296 var _loop2 = function _loop2() {
1297 var v = params.bfs ? Q.shift() : Q.pop();
1298 var vId = v.id();
1299
1300 if (params.dfs) {
1301 if (V[vId]) {
1302 return "continue";
1303 }
1304
1305 V[vId] = true;
1306 connectedNodes.push(v);
1307 }
1308
1309 var depth = id2depth[vId];
1310 var prevEdge = connectedBy[vId];
1311 var src = prevEdge != null ? prevEdge.source() : null;
1312 var tgt = prevEdge != null ? prevEdge.target() : null;
1313 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1314 var ret = void 0;
1315 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1316
1317 if (ret === true) {
1318 found = v;
1319 return "break";
1320 }
1321
1322 if (ret === false) {
1323 return "break";
1324 }
1325
1326 var vwEdges = v.connectedEdges().filter(function (e) {
1327 return (!directed || e.source().same(v)) && edges.has(e);
1328 });
1329
1330 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1331 var e = vwEdges[_i2];
1332 var w = e.connectedNodes().filter(function (n) {
1333 return !n.same(v) && nodes.has(n);
1334 });
1335 var wId = w.id();
1336
1337 if (w.length !== 0 && !V[wId]) {
1338 w = w[0];
1339 Q.push(w);
1340
1341 if (params.bfs) {
1342 V[wId] = true;
1343 connectedNodes.push(w);
1344 }
1345
1346 connectedBy[wId] = e;
1347 id2depth[wId] = id2depth[vId] + 1;
1348 }
1349 }
1350 };
1351
1352 _loop: while (Q.length !== 0) {
1353 var _ret = _loop2();
1354
1355 switch (_ret) {
1356 case "continue":
1357 continue;
1358
1359 case "break":
1360 break _loop;
1361 }
1362 }
1363
1364 var connectedEles = cy.collection();
1365
1366 for (var _i = 0; _i < connectedNodes.length; _i++) {
1367 var node = connectedNodes[_i];
1368 var edge = connectedBy[node.id()];
1369
1370 if (edge != null) {
1371 connectedEles.push(edge);
1372 }
1373
1374 connectedEles.push(node);
1375 }
1376
1377 return {
1378 path: cy.collection(connectedEles),
1379 found: cy.collection(found)
1380 };
1381 };
1382}; // search, spanning trees, etc
1383
1384
1385var elesfn = {
1386 breadthFirstSearch: defineSearch({
1387 bfs: true
1388 }),
1389 depthFirstSearch: defineSearch({
1390 dfs: true
1391 })
1392}; // nice, short mathemathical alias
1393
1394elesfn.bfs = elesfn.breadthFirstSearch;
1395elesfn.dfs = elesfn.depthFirstSearch;
1396
1397var dijkstraDefaults = defaults({
1398 root: null,
1399 weight: function weight(edge) {
1400 return 1;
1401 },
1402 directed: false
1403});
1404var elesfn$1 = {
1405 dijkstra: function dijkstra(options) {
1406 if (!plainObject(options)) {
1407 var args = arguments;
1408 options = {
1409 root: args[0],
1410 weight: args[1],
1411 directed: args[2]
1412 };
1413 }
1414
1415 var _dijkstraDefaults = dijkstraDefaults(options),
1416 root = _dijkstraDefaults.root,
1417 weight = _dijkstraDefaults.weight,
1418 directed = _dijkstraDefaults.directed;
1419
1420 var eles = this;
1421 var weightFn = weight;
1422 var source = string(root) ? this.filter(root)[0] : root[0];
1423 var dist = {};
1424 var prev = {};
1425 var knownDist = {};
1426
1427 var _this$byGroup = this.byGroup(),
1428 nodes = _this$byGroup.nodes,
1429 edges = _this$byGroup.edges;
1430
1431 edges.unmergeBy(function (ele) {
1432 return ele.isLoop();
1433 });
1434
1435 var getDist = function getDist(node) {
1436 return dist[node.id()];
1437 };
1438
1439 var setDist = function setDist(node, d) {
1440 dist[node.id()] = d;
1441 Q.updateItem(node);
1442 };
1443
1444 var Q = new Heap(function (a, b) {
1445 return getDist(a) - getDist(b);
1446 });
1447
1448 for (var i = 0; i < nodes.length; i++) {
1449 var node = nodes[i];
1450 dist[node.id()] = node.same(source) ? 0 : Infinity;
1451 Q.push(node);
1452 }
1453
1454 var distBetween = function distBetween(u, v) {
1455 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1456 var smallestDistance = Infinity;
1457 var smallestEdge;
1458
1459 for (var _i = 0; _i < uvs.length; _i++) {
1460 var edge = uvs[_i];
1461
1462 var _weight = weightFn(edge);
1463
1464 if (_weight < smallestDistance || !smallestEdge) {
1465 smallestDistance = _weight;
1466 smallestEdge = edge;
1467 }
1468 }
1469
1470 return {
1471 edge: smallestEdge,
1472 dist: smallestDistance
1473 };
1474 };
1475
1476 while (Q.size() > 0) {
1477 var u = Q.pop();
1478 var smalletsDist = getDist(u);
1479 var uid = u.id();
1480 knownDist[uid] = smalletsDist;
1481
1482 if (smalletsDist === Infinity) {
1483 continue;
1484 }
1485
1486 var neighbors = u.neighborhood().intersect(nodes);
1487
1488 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1489 var v = neighbors[_i2];
1490 var vid = v.id();
1491 var vDist = distBetween(u, v);
1492 var alt = smalletsDist + vDist.dist;
1493
1494 if (alt < getDist(v)) {
1495 setDist(v, alt);
1496 prev[vid] = {
1497 node: u,
1498 edge: vDist.edge
1499 };
1500 }
1501 } // for
1502
1503 } // while
1504
1505
1506 return {
1507 distanceTo: function distanceTo(node) {
1508 var target = string(node) ? nodes.filter(node)[0] : node[0];
1509 return knownDist[target.id()];
1510 },
1511 pathTo: function pathTo(node) {
1512 var target = string(node) ? nodes.filter(node)[0] : node[0];
1513 var S = [];
1514 var u = target;
1515 var uid = u.id();
1516
1517 if (target.length > 0) {
1518 S.unshift(target);
1519
1520 while (prev[uid]) {
1521 var p = prev[uid];
1522 S.unshift(p.edge);
1523 S.unshift(p.node);
1524 u = p.node;
1525 uid = u.id();
1526 }
1527 }
1528
1529 return eles.spawn(S);
1530 }
1531 };
1532 }
1533};
1534
1535var elesfn$2 = {
1536 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1537 // implemented from pseudocode from wikipedia
1538 kruskal: function kruskal(weightFn) {
1539 weightFn = weightFn || function (edge) {
1540 return 1;
1541 };
1542
1543 var _this$byGroup = this.byGroup(),
1544 nodes = _this$byGroup.nodes,
1545 edges = _this$byGroup.edges;
1546
1547 var numNodes = nodes.length;
1548 var forest = new Array(numNodes);
1549 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1550
1551 var findSetIndex = function findSetIndex(ele) {
1552 for (var i = 0; i < forest.length; i++) {
1553 var eles = forest[i];
1554
1555 if (eles.has(ele)) {
1556 return i;
1557 }
1558 }
1559 }; // start with one forest per node
1560
1561
1562 for (var i = 0; i < numNodes; i++) {
1563 forest[i] = this.spawn(nodes[i]);
1564 }
1565
1566 var S = edges.sort(function (a, b) {
1567 return weightFn(a) - weightFn(b);
1568 });
1569
1570 for (var _i = 0; _i < S.length; _i++) {
1571 var edge = S[_i];
1572 var u = edge.source()[0];
1573 var v = edge.target()[0];
1574 var setUIndex = findSetIndex(u);
1575 var setVIndex = findSetIndex(v);
1576 var setU = forest[setUIndex];
1577 var setV = forest[setVIndex];
1578
1579 if (setUIndex !== setVIndex) {
1580 A.merge(edge); // combine forests for u and v
1581
1582 setU.merge(setV);
1583 forest.splice(setVIndex, 1);
1584 }
1585 }
1586
1587 return A;
1588 }
1589};
1590
1591var aStarDefaults = defaults({
1592 root: null,
1593 goal: null,
1594 weight: function weight(edge) {
1595 return 1;
1596 },
1597 heuristic: function heuristic(edge) {
1598 return 0;
1599 },
1600 directed: false
1601});
1602var elesfn$3 = {
1603 // Implemented from pseudocode from wikipedia
1604 aStar: function aStar(options) {
1605 var cy = this.cy();
1606
1607 var _aStarDefaults = aStarDefaults(options),
1608 root = _aStarDefaults.root,
1609 goal = _aStarDefaults.goal,
1610 heuristic = _aStarDefaults.heuristic,
1611 directed = _aStarDefaults.directed,
1612 weight = _aStarDefaults.weight;
1613
1614 root = cy.collection(root)[0];
1615 goal = cy.collection(goal)[0];
1616 var sid = root.id();
1617 var tid = goal.id();
1618 var gScore = {};
1619 var fScore = {};
1620 var closedSetIds = {};
1621 var openSet = new Heap(function (a, b) {
1622 return fScore[a.id()] - fScore[b.id()];
1623 });
1624 var openSetIds = new Set$1();
1625 var cameFrom = {};
1626 var cameFromEdge = {};
1627
1628 var addToOpenSet = function addToOpenSet(ele, id) {
1629 openSet.push(ele);
1630 openSetIds.add(id);
1631 };
1632
1633 var cMin, cMinId;
1634
1635 var popFromOpenSet = function popFromOpenSet() {
1636 cMin = openSet.pop();
1637 cMinId = cMin.id();
1638 openSetIds["delete"](cMinId);
1639 };
1640
1641 var isInOpenSet = function isInOpenSet(id) {
1642 return openSetIds.has(id);
1643 };
1644
1645 addToOpenSet(root, sid);
1646 gScore[sid] = 0;
1647 fScore[sid] = heuristic(root); // Counter
1648
1649 var steps = 0; // Main loop
1650
1651 while (openSet.size() > 0) {
1652 popFromOpenSet();
1653 steps++; // If we've found our goal, then we are done
1654
1655 if (cMinId === tid) {
1656 var path = [];
1657 var pathNode = goal;
1658 var pathNodeId = tid;
1659 var pathEdge = cameFromEdge[pathNodeId];
1660
1661 for (;;) {
1662 path.unshift(pathNode);
1663
1664 if (pathEdge != null) {
1665 path.unshift(pathEdge);
1666 }
1667
1668 pathNode = cameFrom[pathNodeId];
1669
1670 if (pathNode == null) {
1671 break;
1672 }
1673
1674 pathNodeId = pathNode.id();
1675 pathEdge = cameFromEdge[pathNodeId];
1676 }
1677
1678 return {
1679 found: true,
1680 distance: gScore[cMinId],
1681 path: this.spawn(path),
1682 steps: steps
1683 };
1684 } // Add cMin to processed nodes
1685
1686
1687 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1688 // Take into account if graph is directed or not
1689
1690 var vwEdges = cMin._private.edges;
1691
1692 for (var i = 0; i < vwEdges.length; i++) {
1693 var e = vwEdges[i]; // edge must be in set of calling eles
1694
1695 if (!this.hasElementWithId(e.id())) {
1696 continue;
1697 } // cMin must be the source of edge if directed
1698
1699
1700 if (directed && e.data('source') !== cMinId) {
1701 continue;
1702 }
1703
1704 var wSrc = e.source();
1705 var wTgt = e.target();
1706 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1707 var wid = w.id(); // node must be in set of calling eles
1708
1709 if (!this.hasElementWithId(wid)) {
1710 continue;
1711 } // if node is in closedSet, ignore it
1712
1713
1714 if (closedSetIds[wid]) {
1715 continue;
1716 } // New tentative score for node w
1717
1718
1719 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1720 // w not present in openSet
1721 // OR
1722 // tentative gScore is less than previous value
1723 // w not in openSet
1724
1725 if (!isInOpenSet(wid)) {
1726 gScore[wid] = tempScore;
1727 fScore[wid] = tempScore + heuristic(w);
1728 addToOpenSet(w, wid);
1729 cameFrom[wid] = cMin;
1730 cameFromEdge[wid] = e;
1731 continue;
1732 } // w already in openSet, but with greater gScore
1733
1734
1735 if (tempScore < gScore[wid]) {
1736 gScore[wid] = tempScore;
1737 fScore[wid] = tempScore + heuristic(w);
1738 cameFrom[wid] = cMin;
1739 cameFromEdge[wid] = e;
1740 }
1741 } // End of neighbors update
1742
1743 } // End of main loop
1744 // If we've reached here, then we've not reached our goal
1745
1746
1747 return {
1748 found: false,
1749 distance: undefined,
1750 path: undefined,
1751 steps: steps
1752 };
1753 }
1754}; // elesfn
1755
1756var floydWarshallDefaults = defaults({
1757 weight: function weight(edge) {
1758 return 1;
1759 },
1760 directed: false
1761});
1762var elesfn$4 = {
1763 // Implemented from pseudocode from wikipedia
1764 floydWarshall: function floydWarshall(options) {
1765 var cy = this.cy();
1766
1767 var _floydWarshallDefault = floydWarshallDefaults(options),
1768 weight = _floydWarshallDefault.weight,
1769 directed = _floydWarshallDefault.directed;
1770
1771 var weightFn = weight;
1772
1773 var _this$byGroup = this.byGroup(),
1774 nodes = _this$byGroup.nodes,
1775 edges = _this$byGroup.edges;
1776
1777 var N = nodes.length;
1778 var Nsq = N * N;
1779
1780 var indexOf = function indexOf(node) {
1781 return nodes.indexOf(node);
1782 };
1783
1784 var atIndex = function atIndex(i) {
1785 return nodes[i];
1786 }; // Initialize distance matrix
1787
1788
1789 var dist = new Array(Nsq);
1790
1791 for (var n = 0; n < Nsq; n++) {
1792 var j = n % N;
1793 var i = (n - j) / N;
1794
1795 if (i === j) {
1796 dist[n] = 0;
1797 } else {
1798 dist[n] = Infinity;
1799 }
1800 } // Initialize matrix used for path reconstruction
1801 // Initialize distance matrix
1802
1803
1804 var next = new Array(Nsq);
1805 var edgeNext = new Array(Nsq); // Process edges
1806
1807 for (var _i = 0; _i < edges.length; _i++) {
1808 var edge = edges[_i];
1809 var src = edge.source()[0];
1810 var tgt = edge.target()[0];
1811
1812 if (src === tgt) {
1813 continue;
1814 } // exclude loops
1815
1816
1817 var s = indexOf(src);
1818 var t = indexOf(tgt);
1819 var st = s * N + t; // source to target index
1820
1821 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1822
1823
1824 if (dist[st] > _weight) {
1825 dist[st] = _weight;
1826 next[st] = t;
1827 edgeNext[st] = edge;
1828 } // If undirected graph, process 'reversed' edge
1829
1830
1831 if (!directed) {
1832 var ts = t * N + s; // target to source index
1833
1834 if (!directed && dist[ts] > _weight) {
1835 dist[ts] = _weight;
1836 next[ts] = s;
1837 edgeNext[ts] = edge;
1838 }
1839 }
1840 } // Main loop
1841
1842
1843 for (var k = 0; k < N; k++) {
1844 for (var _i2 = 0; _i2 < N; _i2++) {
1845 var ik = _i2 * N + k;
1846
1847 for (var _j = 0; _j < N; _j++) {
1848 var ij = _i2 * N + _j;
1849 var kj = k * N + _j;
1850
1851 if (dist[ik] + dist[kj] < dist[ij]) {
1852 dist[ij] = dist[ik] + dist[kj];
1853 next[ij] = next[ik];
1854 }
1855 }
1856 }
1857 }
1858
1859 var getArgEle = function getArgEle(ele) {
1860 return (string(ele) ? cy.filter(ele) : ele)[0];
1861 };
1862
1863 var indexOfArgEle = function indexOfArgEle(ele) {
1864 return indexOf(getArgEle(ele));
1865 };
1866
1867 var res = {
1868 distance: function distance(from, to) {
1869 var i = indexOfArgEle(from);
1870 var j = indexOfArgEle(to);
1871 return dist[i * N + j];
1872 },
1873 path: function path(from, to) {
1874 var i = indexOfArgEle(from);
1875 var j = indexOfArgEle(to);
1876 var fromNode = atIndex(i);
1877
1878 if (i === j) {
1879 return fromNode.collection();
1880 }
1881
1882 if (next[i * N + j] == null) {
1883 return cy.collection();
1884 }
1885
1886 var path = cy.collection();
1887 var prev = i;
1888 var edge;
1889 path.merge(fromNode);
1890
1891 while (i !== j) {
1892 prev = i;
1893 i = next[i * N + j];
1894 edge = edgeNext[prev * N + i];
1895 path.merge(edge);
1896 path.merge(atIndex(i));
1897 }
1898
1899 return path;
1900 }
1901 };
1902 return res;
1903 } // floydWarshall
1904
1905}; // elesfn
1906
1907var bellmanFordDefaults = defaults({
1908 weight: function weight(edge) {
1909 return 1;
1910 },
1911 directed: false,
1912 root: null
1913});
1914var elesfn$5 = {
1915 // Implemented from pseudocode from wikipedia
1916 bellmanFord: function bellmanFord(options) {
1917 var _this = this;
1918
1919 var _bellmanFordDefaults = bellmanFordDefaults(options),
1920 weight = _bellmanFordDefaults.weight,
1921 directed = _bellmanFordDefaults.directed,
1922 root = _bellmanFordDefaults.root;
1923
1924 var weightFn = weight;
1925 var eles = this;
1926 var cy = this.cy();
1927
1928 var _this$byGroup = this.byGroup(),
1929 edges = _this$byGroup.edges,
1930 nodes = _this$byGroup.nodes;
1931
1932 var numNodes = nodes.length;
1933 var infoMap = new Map$1();
1934 var hasNegativeWeightCycle = false;
1935 var negativeWeightCycles = [];
1936 root = cy.collection(root)[0]; // in case selector passed
1937
1938 edges.unmergeBy(function (edge) {
1939 return edge.isLoop();
1940 });
1941 var numEdges = edges.length;
1942
1943 var getInfo = function getInfo(node) {
1944 var obj = infoMap.get(node.id());
1945
1946 if (!obj) {
1947 obj = {};
1948 infoMap.set(node.id(), obj);
1949 }
1950
1951 return obj;
1952 };
1953
1954 var getNodeFromTo = function getNodeFromTo(to) {
1955 return (string(to) ? cy.$(to) : to)[0];
1956 };
1957
1958 var distanceTo = function distanceTo(to) {
1959 return getInfo(getNodeFromTo(to)).dist;
1960 };
1961
1962 var pathTo = function pathTo(to) {
1963 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1964 var end = getNodeFromTo(to);
1965 var path = [];
1966 var node = end;
1967
1968 for (;;) {
1969 if (node == null) {
1970 return _this.spawn();
1971 }
1972
1973 var _getInfo = getInfo(node),
1974 edge = _getInfo.edge,
1975 pred = _getInfo.pred;
1976
1977 path.unshift(node[0]);
1978
1979 if (node.same(thisStart) && path.length > 0) {
1980 break;
1981 }
1982
1983 if (edge != null) {
1984 path.unshift(edge);
1985 }
1986
1987 node = pred;
1988 }
1989
1990 return eles.spawn(path);
1991 }; // Initializations { dist, pred, edge }
1992
1993
1994 for (var i = 0; i < numNodes; i++) {
1995 var node = nodes[i];
1996 var info = getInfo(node);
1997
1998 if (node.same(root)) {
1999 info.dist = 0;
2000 } else {
2001 info.dist = Infinity;
2002 }
2003
2004 info.pred = null;
2005 info.edge = null;
2006 } // Edges relaxation
2007
2008
2009 var replacedEdge = false;
2010
2011 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2012 var dist = info1.dist + weight;
2013
2014 if (dist < info2.dist && !edge.same(info1.edge)) {
2015 info2.dist = dist;
2016 info2.pred = node1;
2017 info2.edge = edge;
2018 replacedEdge = true;
2019 }
2020 };
2021
2022 for (var _i = 1; _i < numNodes; _i++) {
2023 replacedEdge = false;
2024
2025 for (var e = 0; e < numEdges; e++) {
2026 var edge = edges[e];
2027 var src = edge.source();
2028 var tgt = edge.target();
2029
2030 var _weight = weightFn(edge);
2031
2032 var srcInfo = getInfo(src);
2033 var tgtInfo = getInfo(tgt);
2034 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2035
2036 if (!directed) {
2037 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2038 }
2039 }
2040
2041 if (!replacedEdge) {
2042 break;
2043 }
2044 }
2045
2046 if (replacedEdge) {
2047 // Check for negative weight cycles
2048 var negativeWeightCycleIds = [];
2049
2050 for (var _e = 0; _e < numEdges; _e++) {
2051 var _edge = edges[_e];
2052
2053 var _src = _edge.source();
2054
2055 var _tgt = _edge.target();
2056
2057 var _weight2 = weightFn(_edge);
2058
2059 var srcDist = getInfo(_src).dist;
2060 var tgtDist = getInfo(_tgt).dist;
2061
2062 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2063 if (!hasNegativeWeightCycle) {
2064 warn('Graph contains a negative weight cycle for Bellman-Ford');
2065 hasNegativeWeightCycle = true;
2066 }
2067
2068 if (options.findNegativeWeightCycles !== false) {
2069 var negativeNodes = [];
2070
2071 if (srcDist + _weight2 < tgtDist) {
2072 negativeNodes.push(_src);
2073 }
2074
2075 if (!directed && tgtDist + _weight2 < srcDist) {
2076 negativeNodes.push(_tgt);
2077 }
2078
2079 var numNegativeNodes = negativeNodes.length;
2080
2081 for (var n = 0; n < numNegativeNodes; n++) {
2082 var start = negativeNodes[n];
2083 var cycle = [start];
2084 cycle.push(getInfo(start).edge);
2085 var _node = getInfo(start).pred;
2086
2087 while (cycle.indexOf(_node) === -1) {
2088 cycle.push(_node);
2089 cycle.push(getInfo(_node).edge);
2090 _node = getInfo(_node).pred;
2091 }
2092
2093 cycle = cycle.slice(cycle.indexOf(_node));
2094 var smallestId = cycle[0].id();
2095 var smallestIndex = 0;
2096
2097 for (var c = 2; c < cycle.length; c += 2) {
2098 if (cycle[c].id() < smallestId) {
2099 smallestId = cycle[c].id();
2100 smallestIndex = c;
2101 }
2102 }
2103
2104 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2105 cycle.push(cycle[0]);
2106 var cycleId = cycle.map(function (el) {
2107 return el.id();
2108 }).join(",");
2109
2110 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2111 negativeWeightCycles.push(eles.spawn(cycle));
2112 negativeWeightCycleIds.push(cycleId);
2113 }
2114 }
2115 } else {
2116 break;
2117 }
2118 }
2119 }
2120 }
2121
2122 return {
2123 distanceTo: distanceTo,
2124 pathTo: pathTo,
2125 hasNegativeWeightCycle: hasNegativeWeightCycle,
2126 negativeWeightCycles: negativeWeightCycles
2127 };
2128 } // bellmanFord
2129
2130}; // elesfn
2131
2132var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2133// Updates the remaining edge lists
2134// Receives as a paramater the edge which causes the collapse
2135
2136var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2137 if (remainingEdges.length === 0) {
2138 error("Karger-Stein must be run on a connected (sub)graph");
2139 }
2140
2141 var edgeInfo = remainingEdges[edgeIndex];
2142 var sourceIn = edgeInfo[1];
2143 var targetIn = edgeInfo[2];
2144 var partition1 = nodeMap[sourceIn];
2145 var partition2 = nodeMap[targetIn];
2146 var newEdges = remainingEdges; // re-use array
2147 // Delete all edges between partition1 and partition2
2148
2149 for (var i = newEdges.length - 1; i >= 0; i--) {
2150 var edge = newEdges[i];
2151 var src = edge[1];
2152 var tgt = edge[2];
2153
2154 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2155 newEdges.splice(i, 1);
2156 }
2157 } // All edges pointing to partition2 should now point to partition1
2158
2159
2160 for (var _i = 0; _i < newEdges.length; _i++) {
2161 var _edge = newEdges[_i];
2162
2163 if (_edge[1] === partition2) {
2164 // Check source
2165 newEdges[_i] = _edge.slice(); // copy
2166
2167 newEdges[_i][1] = partition1;
2168 } else if (_edge[2] === partition2) {
2169 // Check target
2170 newEdges[_i] = _edge.slice(); // copy
2171
2172 newEdges[_i][2] = partition1;
2173 }
2174 } // Move all nodes from partition2 to partition1
2175
2176
2177 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2178 if (nodeMap[_i2] === partition2) {
2179 nodeMap[_i2] = partition1;
2180 }
2181 }
2182
2183 return newEdges;
2184}; // Contracts a graph until we reach a certain number of meta nodes
2185
2186
2187var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2188 while (size > sizeLimit) {
2189 // Choose an edge randomly
2190 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2191
2192 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2193 size--;
2194 }
2195
2196 return remainingEdges;
2197};
2198
2199var elesfn$6 = {
2200 // Computes the minimum cut of an undirected graph
2201 // Returns the correct answer with high probability
2202 kargerStein: function kargerStein() {
2203 var _this = this;
2204
2205 var _this$byGroup = this.byGroup(),
2206 nodes = _this$byGroup.nodes,
2207 edges = _this$byGroup.edges;
2208
2209 edges.unmergeBy(function (edge) {
2210 return edge.isLoop();
2211 });
2212 var numNodes = nodes.length;
2213 var numEdges = edges.length;
2214 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2215 var stopSize = Math.floor(numNodes / sqrt2);
2216
2217 if (numNodes < 2) {
2218 error('At least 2 nodes are required for Karger-Stein algorithm');
2219 return undefined;
2220 } // Now store edge destination as indexes
2221 // Format for each edge (edge index, source node index, target node index)
2222
2223
2224 var edgeIndexes = [];
2225
2226 for (var i = 0; i < numEdges; i++) {
2227 var e = edges[i];
2228 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2229 } // We will store the best cut found here
2230
2231
2232 var minCutSize = Infinity;
2233 var minCutEdgeIndexes = [];
2234 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2235
2236 var metaNodeMap = new Array(numNodes);
2237 var metaNodeMap2 = new Array(numNodes);
2238
2239 var copyNodesMap = function copyNodesMap(from, to) {
2240 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2241 to[_i3] = from[_i3];
2242 }
2243 }; // Main loop
2244
2245
2246 for (var iter = 0; iter <= numIter; iter++) {
2247 // Reset meta node partition
2248 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2249 metaNodeMap[_i4] = _i4;
2250 } // Contract until stop point (stopSize nodes)
2251
2252
2253 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2254 var edgesState2 = edgesState.slice(); // copy
2255 // Create a copy of the colapsed nodes state
2256
2257 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2258
2259 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2260 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2261
2262 if (res1.length <= res2.length && res1.length < minCutSize) {
2263 minCutSize = res1.length;
2264 minCutEdgeIndexes = res1;
2265 copyNodesMap(metaNodeMap, minCutNodeMap);
2266 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2267 minCutSize = res2.length;
2268 minCutEdgeIndexes = res2;
2269 copyNodesMap(metaNodeMap2, minCutNodeMap);
2270 }
2271 } // end of main loop
2272 // Construct result
2273
2274
2275 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2276 return edges[e[0]];
2277 }));
2278 var partition1 = this.spawn();
2279 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2280
2281 var witnessNodePartition = minCutNodeMap[0];
2282
2283 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2284 var partitionId = minCutNodeMap[_i5];
2285 var node = nodes[_i5];
2286
2287 if (partitionId === witnessNodePartition) {
2288 partition1.merge(node);
2289 } else {
2290 partition2.merge(node);
2291 }
2292 } // construct components corresponding to each disjoint subset of nodes
2293
2294
2295 var constructComponent = function constructComponent(subset) {
2296 var component = _this.spawn();
2297
2298 subset.forEach(function (node) {
2299 component.merge(node);
2300 node.connectedEdges().forEach(function (edge) {
2301 // ensure edge is within calling collection and edge is not in cut
2302 if (_this.contains(edge) && !cut.contains(edge)) {
2303 component.merge(edge);
2304 }
2305 });
2306 });
2307 return component;
2308 };
2309
2310 var components = [constructComponent(partition1), constructComponent(partition2)];
2311 var ret = {
2312 cut: cut,
2313 components: components,
2314 // n.b. partitions are included to be compatible with the old api spec
2315 // (could be removed in a future major version)
2316 partition1: partition1,
2317 partition2: partition2
2318 };
2319 return ret;
2320 }
2321}; // elesfn
2322
2323var copyPosition = function copyPosition(p) {
2324 return {
2325 x: p.x,
2326 y: p.y
2327 };
2328};
2329var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2330 return {
2331 x: p.x * zoom + pan.x,
2332 y: p.y * zoom + pan.y
2333 };
2334};
2335var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2336 return {
2337 x: (p.x - pan.x) / zoom,
2338 y: (p.y - pan.y) / zoom
2339 };
2340};
2341var array2point = function array2point(arr) {
2342 return {
2343 x: arr[0],
2344 y: arr[1]
2345 };
2346};
2347var min = function min(arr) {
2348 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2349 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2350 var min = Infinity;
2351
2352 for (var i = begin; i < end; i++) {
2353 var val = arr[i];
2354
2355 if (isFinite(val)) {
2356 min = Math.min(val, min);
2357 }
2358 }
2359
2360 return min;
2361};
2362var max = function max(arr) {
2363 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2364 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2365 var max = -Infinity;
2366
2367 for (var i = begin; i < end; i++) {
2368 var val = arr[i];
2369
2370 if (isFinite(val)) {
2371 max = Math.max(val, max);
2372 }
2373 }
2374
2375 return max;
2376};
2377var mean = function mean(arr) {
2378 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2379 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2380 var total = 0;
2381 var n = 0;
2382
2383 for (var i = begin; i < end; i++) {
2384 var val = arr[i];
2385
2386 if (isFinite(val)) {
2387 total += val;
2388 n++;
2389 }
2390 }
2391
2392 return total / n;
2393};
2394var median = function median(arr) {
2395 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2396 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2397 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2398 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2399 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2400
2401 if (copy) {
2402 arr = arr.slice(begin, end);
2403 } else {
2404 if (end < arr.length) {
2405 arr.splice(end, arr.length - end);
2406 }
2407
2408 if (begin > 0) {
2409 arr.splice(0, begin);
2410 }
2411 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2412
2413
2414 var off = 0; // offset from non-finite values
2415
2416 for (var i = arr.length - 1; i >= 0; i--) {
2417 var v = arr[i];
2418
2419 if (includeHoles) {
2420 if (!isFinite(v)) {
2421 arr[i] = -Infinity;
2422 off++;
2423 }
2424 } else {
2425 // just remove it if we don't want to consider holes
2426 arr.splice(i, 1);
2427 }
2428 }
2429
2430 if (sort) {
2431 arr.sort(function (a, b) {
2432 return a - b;
2433 }); // requires copy = true if you don't want to change the orig
2434 }
2435
2436 var len = arr.length;
2437 var mid = Math.floor(len / 2);
2438
2439 if (len % 2 !== 0) {
2440 return arr[mid + 1 + off];
2441 } else {
2442 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2443 }
2444};
2445var deg2rad = function deg2rad(deg) {
2446 return Math.PI * deg / 180;
2447};
2448var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2449 return Math.atan2(dispY, dispX) - Math.PI / 2;
2450};
2451var log2 = Math.log2 || function (n) {
2452 return Math.log(n) / Math.log(2);
2453};
2454var signum = function signum(x) {
2455 if (x > 0) {
2456 return 1;
2457 } else if (x < 0) {
2458 return -1;
2459 } else {
2460 return 0;
2461 }
2462};
2463var dist = function dist(p1, p2) {
2464 return Math.sqrt(sqdist(p1, p2));
2465};
2466var sqdist = function sqdist(p1, p2) {
2467 var dx = p2.x - p1.x;
2468 var dy = p2.y - p1.y;
2469 return dx * dx + dy * dy;
2470};
2471var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2472 var length = v.length; // First, get sum of all elements
2473
2474 var total = 0;
2475
2476 for (var i = 0; i < length; i++) {
2477 total += v[i];
2478 } // Now, divide each by the sum of all elements
2479
2480
2481 for (var _i = 0; _i < length; _i++) {
2482 v[_i] = v[_i] / total;
2483 }
2484
2485 return v;
2486};
2487
2488var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2489 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2490};
2491var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2492 return {
2493 x: qbezierAt(p0.x, p1.x, p2.x, t),
2494 y: qbezierAt(p0.y, p1.y, p2.y, t)
2495 };
2496};
2497var lineAt = function lineAt(p0, p1, t, d) {
2498 var vec = {
2499 x: p1.x - p0.x,
2500 y: p1.y - p0.y
2501 };
2502 var vecDist = dist(p0, p1);
2503 var normVec = {
2504 x: vec.x / vecDist,
2505 y: vec.y / vecDist
2506 };
2507 t = t == null ? 0 : t;
2508 d = d != null ? d : t * vecDist;
2509 return {
2510 x: p0.x + normVec.x * d,
2511 y: p0.y + normVec.y * d
2512 };
2513};
2514var bound = function bound(min, val, max) {
2515 return Math.max(min, Math.min(max, val));
2516}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2517
2518var makeBoundingBox = function makeBoundingBox(bb) {
2519 if (bb == null) {
2520 return {
2521 x1: Infinity,
2522 y1: Infinity,
2523 x2: -Infinity,
2524 y2: -Infinity,
2525 w: 0,
2526 h: 0
2527 };
2528 } else if (bb.x1 != null && bb.y1 != null) {
2529 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2530 return {
2531 x1: bb.x1,
2532 y1: bb.y1,
2533 x2: bb.x2,
2534 y2: bb.y2,
2535 w: bb.x2 - bb.x1,
2536 h: bb.y2 - bb.y1
2537 };
2538 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2539 return {
2540 x1: bb.x1,
2541 y1: bb.y1,
2542 x2: bb.x1 + bb.w,
2543 y2: bb.y1 + bb.h,
2544 w: bb.w,
2545 h: bb.h
2546 };
2547 }
2548 }
2549};
2550var copyBoundingBox = function copyBoundingBox(bb) {
2551 return {
2552 x1: bb.x1,
2553 x2: bb.x2,
2554 w: bb.w,
2555 y1: bb.y1,
2556 y2: bb.y2,
2557 h: bb.h
2558 };
2559};
2560var clearBoundingBox = function clearBoundingBox(bb) {
2561 bb.x1 = Infinity;
2562 bb.y1 = Infinity;
2563 bb.x2 = -Infinity;
2564 bb.y2 = -Infinity;
2565 bb.w = 0;
2566 bb.h = 0;
2567};
2568var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2569 // update bb1 with bb2 bounds
2570 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2571 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2572 bb1.w = bb1.x2 - bb1.x1;
2573 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2574 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2575 bb1.h = bb1.y2 - bb1.y1;
2576};
2577var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2578 bb.x1 = Math.min(bb.x1, x);
2579 bb.x2 = Math.max(bb.x2, x);
2580 bb.w = bb.x2 - bb.x1;
2581 bb.y1 = Math.min(bb.y1, y);
2582 bb.y2 = Math.max(bb.y2, y);
2583 bb.h = bb.y2 - bb.y1;
2584};
2585var expandBoundingBox = function expandBoundingBox(bb) {
2586 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2587 bb.x1 -= padding;
2588 bb.x2 += padding;
2589 bb.y1 -= padding;
2590 bb.y2 += padding;
2591 bb.w = bb.x2 - bb.x1;
2592 bb.h = bb.y2 - bb.y1;
2593 return bb;
2594};
2595var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2596 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2597 var top, right, bottom, left;
2598
2599 if (padding.length === 1) {
2600 top = right = bottom = left = padding[0];
2601 } else if (padding.length === 2) {
2602 top = bottom = padding[0];
2603 left = right = padding[1];
2604 } else if (padding.length === 4) {
2605 var _padding = _slicedToArray(padding, 4);
2606
2607 top = _padding[0];
2608 right = _padding[1];
2609 bottom = _padding[2];
2610 left = _padding[3];
2611 }
2612
2613 bb.x1 -= left;
2614 bb.x2 += right;
2615 bb.y1 -= top;
2616 bb.y2 += bottom;
2617 bb.w = bb.x2 - bb.x1;
2618 bb.h = bb.y2 - bb.y1;
2619 return bb;
2620};
2621
2622var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2623 bb1.x1 = bb2.x1;
2624 bb1.y1 = bb2.y1;
2625 bb1.x2 = bb2.x2;
2626 bb1.y2 = bb2.y2;
2627 bb1.w = bb1.x2 - bb1.x1;
2628 bb1.h = bb1.y2 - bb1.y1;
2629};
2630var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2631 // case: one bb to right of other
2632 if (bb1.x1 > bb2.x2) {
2633 return false;
2634 }
2635
2636 if (bb2.x1 > bb1.x2) {
2637 return false;
2638 } // case: one bb to left of other
2639
2640
2641 if (bb1.x2 < bb2.x1) {
2642 return false;
2643 }
2644
2645 if (bb2.x2 < bb1.x1) {
2646 return false;
2647 } // case: one bb above other
2648
2649
2650 if (bb1.y2 < bb2.y1) {
2651 return false;
2652 }
2653
2654 if (bb2.y2 < bb1.y1) {
2655 return false;
2656 } // case: one bb below other
2657
2658
2659 if (bb1.y1 > bb2.y2) {
2660 return false;
2661 }
2662
2663 if (bb2.y1 > bb1.y2) {
2664 return false;
2665 } // otherwise, must have some overlap
2666
2667
2668 return true;
2669};
2670var inBoundingBox = function inBoundingBox(bb, x, y) {
2671 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2672};
2673var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2674 return inBoundingBox(bb, pt.x, pt.y);
2675};
2676var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2677 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2678};
2679var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2680 var cornerRadius = getRoundRectangleRadius(width, height);
2681 var halfWidth = width / 2;
2682 var halfHeight = height / 2; // Check intersections with straight line segments
2683
2684 var straightLineIntersections; // Top segment, left to right
2685
2686 {
2687 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2688 var topStartY = nodeY - halfHeight - padding;
2689 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2690 var topEndY = topStartY;
2691 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2692
2693 if (straightLineIntersections.length > 0) {
2694 return straightLineIntersections;
2695 }
2696 } // Right segment, top to bottom
2697
2698 {
2699 var rightStartX = nodeX + halfWidth + padding;
2700 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2701 var rightEndX = rightStartX;
2702 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2703 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2704
2705 if (straightLineIntersections.length > 0) {
2706 return straightLineIntersections;
2707 }
2708 } // Bottom segment, left to right
2709
2710 {
2711 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2712 var bottomStartY = nodeY + halfHeight + padding;
2713 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2714 var bottomEndY = bottomStartY;
2715 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2716
2717 if (straightLineIntersections.length > 0) {
2718 return straightLineIntersections;
2719 }
2720 } // Left segment, top to bottom
2721
2722 {
2723 var leftStartX = nodeX - halfWidth - padding;
2724 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2725 var leftEndX = leftStartX;
2726 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2727 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2728
2729 if (straightLineIntersections.length > 0) {
2730 return straightLineIntersections;
2731 }
2732 } // Check intersections with arc segments
2733
2734 var arcIntersections; // Top Left
2735
2736 {
2737 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2738 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2739 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2740
2741 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2742 return [arcIntersections[0], arcIntersections[1]];
2743 }
2744 } // Top Right
2745
2746 {
2747 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2748 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2749 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2750
2751 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2752 return [arcIntersections[0], arcIntersections[1]];
2753 }
2754 } // Bottom Right
2755
2756 {
2757 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2758 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2759 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2760
2761 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2762 return [arcIntersections[0], arcIntersections[1]];
2763 }
2764 } // Bottom Left
2765
2766 {
2767 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2768 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2769 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2770
2771 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2772 return [arcIntersections[0], arcIntersections[1]];
2773 }
2774 }
2775 return []; // if nothing
2776};
2777var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2778 var t = tolerance;
2779 var x1 = Math.min(lx1, lx2);
2780 var x2 = Math.max(lx1, lx2);
2781 var y1 = Math.min(ly1, ly2);
2782 var y2 = Math.max(ly1, ly2);
2783 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2784};
2785var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2786 var bb = {
2787 x1: Math.min(x1, x3, x2) - tolerance,
2788 x2: Math.max(x1, x3, x2) + tolerance,
2789 y1: Math.min(y1, y3, y2) - tolerance,
2790 y2: Math.max(y1, y3, y2) + tolerance
2791 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2792
2793 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2794 // console.log('bezier out of rough bb')
2795 return false;
2796 } else {
2797 // console.log('do more expensive check');
2798 return true;
2799 }
2800};
2801var solveQuadratic = function solveQuadratic(a, b, c, val) {
2802 c -= val;
2803 var r = b * b - 4 * a * c;
2804
2805 if (r < 0) {
2806 return [];
2807 }
2808
2809 var sqrtR = Math.sqrt(r);
2810 var denom = 2 * a;
2811 var root1 = (-b + sqrtR) / denom;
2812 var root2 = (-b - sqrtR) / denom;
2813 return [root1, root2];
2814};
2815var solveCubic = function solveCubic(a, b, c, d, result) {
2816 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2817 // r is the real component, i is the imaginary component
2818 // An implementation of the Cardano method from the year 1545
2819 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2820 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2821
2822 if (a === 0) {
2823 a = epsilon;
2824 }
2825
2826 b /= a;
2827 c /= a;
2828 d /= a;
2829 var discriminant, q, r, dum1, s, t, term1, r13;
2830 q = (3.0 * c - b * b) / 9.0;
2831 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2832 r /= 54.0;
2833 discriminant = q * q * q + r * r;
2834 result[1] = 0;
2835 term1 = b / 3.0;
2836
2837 if (discriminant > 0) {
2838 s = r + Math.sqrt(discriminant);
2839 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2840 t = r - Math.sqrt(discriminant);
2841 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2842 result[0] = -term1 + s + t;
2843 term1 += (s + t) / 2.0;
2844 result[4] = result[2] = -term1;
2845 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2846 result[3] = term1;
2847 result[5] = -term1;
2848 return;
2849 }
2850
2851 result[5] = result[3] = 0;
2852
2853 if (discriminant === 0) {
2854 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2855 result[0] = -term1 + 2.0 * r13;
2856 result[4] = result[2] = -(r13 + term1);
2857 return;
2858 }
2859
2860 q = -q;
2861 dum1 = q * q * q;
2862 dum1 = Math.acos(r / Math.sqrt(dum1));
2863 r13 = 2.0 * Math.sqrt(q);
2864 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2865 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2866 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2867 return;
2868};
2869var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2870 // Find minimum distance by using the minimum of the distance
2871 // function between the given point and the curve
2872 // This gives the coefficients of the resulting cubic equation
2873 // whose roots tell us where a possible minimum is
2874 // (Coefficients are divided by 4)
2875 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;
2876 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;
2877 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;
2878 var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y; // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
2879
2880 var roots = []; // Use the cubic solving algorithm
2881
2882 solveCubic(a, b, c, d, roots);
2883 var zeroThreshold = 0.0000001;
2884 var params = [];
2885
2886 for (var index = 0; index < 6; index += 2) {
2887 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2888 params.push(roots[index]);
2889 }
2890 }
2891
2892 params.push(1.0);
2893 params.push(0.0);
2894 var minDistanceSquared = -1;
2895 var curX, curY, distSquared;
2896
2897 for (var i = 0; i < params.length; i++) {
2898 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2899 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2900 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2901
2902 if (minDistanceSquared >= 0) {
2903 if (distSquared < minDistanceSquared) {
2904 minDistanceSquared = distSquared;
2905 }
2906 } else {
2907 minDistanceSquared = distSquared;
2908 }
2909 }
2910
2911 return minDistanceSquared;
2912};
2913var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2914 var offset = [x - x1, y - y1];
2915 var line = [x2 - x1, y2 - y1];
2916 var lineSq = line[0] * line[0] + line[1] * line[1];
2917 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2918 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2919 var adjSq = dotProduct * dotProduct / lineSq;
2920
2921 if (dotProduct < 0) {
2922 return hypSq;
2923 }
2924
2925 if (adjSq > lineSq) {
2926 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2927 }
2928
2929 return hypSq - adjSq;
2930};
2931var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2932 var x1, y1, x2, y2;
2933 var y3; // Intersect with vertical line through (x, y)
2934
2935 var up = 0; // let down = 0;
2936
2937 for (var i = 0; i < points.length / 2; i++) {
2938 x1 = points[i * 2];
2939 y1 = points[i * 2 + 1];
2940
2941 if (i + 1 < points.length / 2) {
2942 x2 = points[(i + 1) * 2];
2943 y2 = points[(i + 1) * 2 + 1];
2944 } else {
2945 x2 = points[(i + 1 - points.length / 2) * 2];
2946 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2947 }
2948
2949 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2950 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2951
2952 if (y3 > y) {
2953 up++;
2954 } // if( y3 < y ){
2955 // down++;
2956 // }
2957
2958 } else {
2959 continue;
2960 }
2961 }
2962
2963 if (up % 2 === 0) {
2964 return false;
2965 } else {
2966 return true;
2967 }
2968};
2969var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2970 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2971
2972 var angle;
2973
2974 if (direction[0] != null) {
2975 angle = Math.atan(direction[1] / direction[0]);
2976
2977 if (direction[0] < 0) {
2978 angle = angle + Math.PI / 2;
2979 } else {
2980 angle = -angle - Math.PI / 2;
2981 }
2982 } else {
2983 angle = direction;
2984 }
2985
2986 var cos = Math.cos(-angle);
2987 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2988
2989 for (var i = 0; i < transformedPoints.length / 2; i++) {
2990 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2991 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2992 transformedPoints[i * 2] += centerX;
2993 transformedPoints[i * 2 + 1] += centerY;
2994 }
2995
2996 var points;
2997
2998 if (padding > 0) {
2999 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3000 points = joinLines(expandedLineSet);
3001 } else {
3002 points = transformedPoints;
3003 }
3004
3005 return pointInsidePolygonPoints(x, y, points);
3006};
3007var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3008 var cutPolygonPoints = new Array(basePoints.length);
3009 var halfW = width / 2;
3010 var halfH = height / 2;
3011 var cornerRadius = getRoundPolygonRadius(width, height);
3012 var squaredCornerRadius = cornerRadius * cornerRadius;
3013
3014 for (var i = 0; i < basePoints.length / 4; i++) {
3015 var sourceUv = void 0,
3016 destUv = void 0;
3017
3018 if (i === 0) {
3019 sourceUv = basePoints.length - 2;
3020 } else {
3021 sourceUv = i * 4 - 2;
3022 }
3023
3024 destUv = i * 4 + 2;
3025 var px = centerX + halfW * basePoints[i * 4];
3026 var py = centerY + halfH * basePoints[i * 4 + 1];
3027 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3028 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3029 var cp0x = px - offset * basePoints[sourceUv];
3030 var cp0y = py - offset * basePoints[sourceUv + 1];
3031 var cp1x = px + offset * basePoints[destUv];
3032 var cp1y = py + offset * basePoints[destUv + 1];
3033 cutPolygonPoints[i * 4] = cp0x;
3034 cutPolygonPoints[i * 4 + 1] = cp0y;
3035 cutPolygonPoints[i * 4 + 2] = cp1x;
3036 cutPolygonPoints[i * 4 + 3] = cp1y;
3037 var orthx = basePoints[sourceUv + 1];
3038 var orthy = -basePoints[sourceUv];
3039 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3040
3041 if (cosAlpha < 0) {
3042 orthx *= -1;
3043 orthy *= -1;
3044 }
3045
3046 var cx = cp0x + orthx * cornerRadius;
3047 var cy = cp0y + orthy * cornerRadius;
3048 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3049
3050 if (squaredDistance <= squaredCornerRadius) {
3051 return true;
3052 }
3053 }
3054
3055 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3056};
3057var joinLines = function joinLines(lineSet) {
3058 var vertices = new Array(lineSet.length / 2);
3059 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3060 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3061
3062 for (var i = 0; i < lineSet.length / 4; i++) {
3063 currentLineStartX = lineSet[i * 4];
3064 currentLineStartY = lineSet[i * 4 + 1];
3065 currentLineEndX = lineSet[i * 4 + 2];
3066 currentLineEndY = lineSet[i * 4 + 3];
3067
3068 if (i < lineSet.length / 4 - 1) {
3069 nextLineStartX = lineSet[(i + 1) * 4];
3070 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3071 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3072 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3073 } else {
3074 nextLineStartX = lineSet[0];
3075 nextLineStartY = lineSet[1];
3076 nextLineEndX = lineSet[2];
3077 nextLineEndY = lineSet[3];
3078 }
3079
3080 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3081 vertices[i * 2] = intersection[0];
3082 vertices[i * 2 + 1] = intersection[1];
3083 }
3084
3085 return vertices;
3086};
3087var expandPolygon = function expandPolygon(points, pad) {
3088 var expandedLineSet = new Array(points.length * 2);
3089 var currentPointX, currentPointY, nextPointX, nextPointY;
3090
3091 for (var i = 0; i < points.length / 2; i++) {
3092 currentPointX = points[i * 2];
3093 currentPointY = points[i * 2 + 1];
3094
3095 if (i < points.length / 2 - 1) {
3096 nextPointX = points[(i + 1) * 2];
3097 nextPointY = points[(i + 1) * 2 + 1];
3098 } else {
3099 nextPointX = points[0];
3100 nextPointY = points[1];
3101 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3102 // Assume CCW polygon winding
3103
3104
3105 var offsetX = nextPointY - currentPointY;
3106 var offsetY = -(nextPointX - currentPointX); // Normalize
3107
3108 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3109 var normalizedOffsetX = offsetX / offsetLength;
3110 var normalizedOffsetY = offsetY / offsetLength;
3111 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3112 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3113 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3114 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3115 }
3116
3117 return expandedLineSet;
3118};
3119var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3120 var dispX = centerX - x;
3121 var dispY = centerY - y;
3122 dispX /= ellipseWradius;
3123 dispY /= ellipseHradius;
3124 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3125 var newLength = len - 1;
3126
3127 if (newLength < 0) {
3128 return [];
3129 }
3130
3131 var lenProportion = newLength / len;
3132 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3133};
3134var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3135 x -= centerX;
3136 y -= centerY;
3137 x /= width / 2 + padding;
3138 y /= height / 2 + padding;
3139 return x * x + y * y <= 1;
3140}; // Returns intersections of increasing distance from line's start point
3141
3142var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3143 // Calculate d, direction vector of line
3144 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3145
3146 var f = [x1 - centerX, y1 - centerY];
3147 var a = d[0] * d[0] + d[1] * d[1];
3148 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3149 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3150 var discriminant = b * b - 4 * a * c;
3151
3152 if (discriminant < 0) {
3153 return [];
3154 }
3155
3156 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3157 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3158 var tMin = Math.min(t1, t2);
3159 var tMax = Math.max(t1, t2);
3160 var inRangeParams = [];
3161
3162 if (tMin >= 0 && tMin <= 1) {
3163 inRangeParams.push(tMin);
3164 }
3165
3166 if (tMax >= 0 && tMax <= 1) {
3167 inRangeParams.push(tMax);
3168 }
3169
3170 if (inRangeParams.length === 0) {
3171 return [];
3172 }
3173
3174 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3175 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3176
3177 if (inRangeParams.length > 1) {
3178 if (inRangeParams[0] == inRangeParams[1]) {
3179 return [nearIntersectionX, nearIntersectionY];
3180 } else {
3181 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3182 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3183 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3184 }
3185 } else {
3186 return [nearIntersectionX, nearIntersectionY];
3187 }
3188};
3189var midOfThree = function midOfThree(a, b, c) {
3190 if (b <= a && a <= c || c <= a && a <= b) {
3191 return a;
3192 } else if (a <= b && b <= c || c <= b && b <= a) {
3193 return b;
3194 } else {
3195 return c;
3196 }
3197}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3198
3199var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3200 var dx13 = x1 - x3;
3201 var dx21 = x2 - x1;
3202 var dx43 = x4 - x3;
3203 var dy13 = y1 - y3;
3204 var dy21 = y2 - y1;
3205 var dy43 = y4 - y3;
3206 var ua_t = dx43 * dy13 - dy43 * dx13;
3207 var ub_t = dx21 * dy13 - dy21 * dx13;
3208 var u_b = dy43 * dx21 - dx43 * dy21;
3209
3210 if (u_b !== 0) {
3211 var ua = ua_t / u_b;
3212 var ub = ub_t / u_b;
3213 var flptThreshold = 0.001;
3214
3215 var _min = 0 - flptThreshold;
3216
3217 var _max = 1 + flptThreshold;
3218
3219 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3220 return [x1 + ua * dx21, y1 + ua * dy21];
3221 } else {
3222 if (!infiniteLines) {
3223 return [];
3224 } else {
3225 return [x1 + ua * dx21, y1 + ua * dy21];
3226 }
3227 }
3228 } else {
3229 if (ua_t === 0 || ub_t === 0) {
3230 // Parallel, coincident lines. Check if overlap
3231 // Check endpoint of second line
3232 if (midOfThree(x1, x2, x4) === x4) {
3233 return [x4, y4];
3234 } // Check start point of second line
3235
3236
3237 if (midOfThree(x1, x2, x3) === x3) {
3238 return [x3, y3];
3239 } // Endpoint of first line
3240
3241
3242 if (midOfThree(x3, x4, x2) === x2) {
3243 return [x2, y2];
3244 }
3245
3246 return [];
3247 } else {
3248 // Parallel, non-coincident
3249 return [];
3250 }
3251 }
3252}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3253// intersect a node polygon (pts transformed)
3254//
3255// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3256// intersect the points (no transform)
3257
3258var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3259 var intersections = [];
3260 var intersection;
3261 var transformedPoints = new Array(basePoints.length);
3262 var doTransform = true;
3263
3264 if (width == null) {
3265 doTransform = false;
3266 }
3267
3268 var points;
3269
3270 if (doTransform) {
3271 for (var i = 0; i < transformedPoints.length / 2; i++) {
3272 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3273 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3274 }
3275
3276 if (padding > 0) {
3277 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3278 points = joinLines(expandedLineSet);
3279 } else {
3280 points = transformedPoints;
3281 }
3282 } else {
3283 points = basePoints;
3284 }
3285
3286 var currentX, currentY, nextX, nextY;
3287
3288 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3289 currentX = points[_i2 * 2];
3290 currentY = points[_i2 * 2 + 1];
3291
3292 if (_i2 < points.length / 2 - 1) {
3293 nextX = points[(_i2 + 1) * 2];
3294 nextY = points[(_i2 + 1) * 2 + 1];
3295 } else {
3296 nextX = points[0];
3297 nextY = points[1];
3298 }
3299
3300 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3301
3302 if (intersection.length !== 0) {
3303 intersections.push(intersection[0], intersection[1]);
3304 }
3305 }
3306
3307 return intersections;
3308};
3309var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3310 var intersections = [];
3311 var intersection;
3312 var lines = new Array(basePoints.length);
3313 var halfW = width / 2;
3314 var halfH = height / 2;
3315 var cornerRadius = getRoundPolygonRadius(width, height);
3316
3317 for (var i = 0; i < basePoints.length / 4; i++) {
3318 var sourceUv = void 0,
3319 destUv = void 0;
3320
3321 if (i === 0) {
3322 sourceUv = basePoints.length - 2;
3323 } else {
3324 sourceUv = i * 4 - 2;
3325 }
3326
3327 destUv = i * 4 + 2;
3328 var px = centerX + halfW * basePoints[i * 4];
3329 var py = centerY + halfH * basePoints[i * 4 + 1];
3330 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3331 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3332 var cp0x = px - offset * basePoints[sourceUv];
3333 var cp0y = py - offset * basePoints[sourceUv + 1];
3334 var cp1x = px + offset * basePoints[destUv];
3335 var cp1y = py + offset * basePoints[destUv + 1];
3336
3337 if (i === 0) {
3338 lines[basePoints.length - 2] = cp0x;
3339 lines[basePoints.length - 1] = cp0y;
3340 } else {
3341 lines[i * 4 - 2] = cp0x;
3342 lines[i * 4 - 1] = cp0y;
3343 }
3344
3345 lines[i * 4] = cp1x;
3346 lines[i * 4 + 1] = cp1y;
3347 var orthx = basePoints[sourceUv + 1];
3348 var orthy = -basePoints[sourceUv];
3349 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3350
3351 if (cosAlpha < 0) {
3352 orthx *= -1;
3353 orthy *= -1;
3354 }
3355
3356 var cx = cp0x + orthx * cornerRadius;
3357 var cy = cp0y + orthy * cornerRadius;
3358 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3359
3360 if (intersection.length !== 0) {
3361 intersections.push(intersection[0], intersection[1]);
3362 }
3363 }
3364
3365 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3366 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3367
3368 if (intersection.length !== 0) {
3369 intersections.push(intersection[0], intersection[1]);
3370 }
3371 }
3372
3373 if (intersections.length > 2) {
3374 var lowestIntersection = [intersections[0], intersections[1]];
3375 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3376
3377 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3378 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3379
3380 if (squaredDistance <= lowestSquaredDistance) {
3381 lowestIntersection[0] = intersections[_i4 * 2];
3382 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3383 lowestSquaredDistance = squaredDistance;
3384 }
3385 }
3386
3387 return lowestIntersection;
3388 }
3389
3390 return intersections;
3391};
3392var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3393 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3394 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3395 var lenRatio = (length - amount) / length;
3396
3397 if (lenRatio < 0) {
3398 lenRatio = 0.00001;
3399 }
3400
3401 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3402};
3403var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3404 var points = generateUnitNgonPoints(sides, rotationRadians);
3405 points = fitPolygonToSquare(points);
3406 return points;
3407};
3408var fitPolygonToSquare = function fitPolygonToSquare(points) {
3409 var x, y;
3410 var sides = points.length / 2;
3411 var minX = Infinity,
3412 minY = Infinity,
3413 maxX = -Infinity,
3414 maxY = -Infinity;
3415
3416 for (var i = 0; i < sides; i++) {
3417 x = points[2 * i];
3418 y = points[2 * i + 1];
3419 minX = Math.min(minX, x);
3420 maxX = Math.max(maxX, x);
3421 minY = Math.min(minY, y);
3422 maxY = Math.max(maxY, y);
3423 } // stretch factors
3424
3425
3426 var sx = 2 / (maxX - minX);
3427 var sy = 2 / (maxY - minY);
3428
3429 for (var _i5 = 0; _i5 < sides; _i5++) {
3430 x = points[2 * _i5] = points[2 * _i5] * sx;
3431 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3432 minX = Math.min(minX, x);
3433 maxX = Math.max(maxX, x);
3434 minY = Math.min(minY, y);
3435 maxY = Math.max(maxY, y);
3436 }
3437
3438 if (minY < -1) {
3439 for (var _i6 = 0; _i6 < sides; _i6++) {
3440 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3441 }
3442 }
3443
3444 return points;
3445};
3446var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3447 var increment = 1.0 / sides * 2 * Math.PI;
3448 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3449 startAngle += rotationRadians;
3450 var points = new Array(sides * 2);
3451 var currentAngle;
3452
3453 for (var i = 0; i < sides; i++) {
3454 currentAngle = i * increment + startAngle;
3455 points[2 * i] = Math.cos(currentAngle); // x
3456
3457 points[2 * i + 1] = Math.sin(-currentAngle); // y
3458 }
3459
3460 return points;
3461}; // Set the default radius, unless half of width or height is smaller than default
3462
3463var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3464 return Math.min(width / 4, height / 4, 8);
3465}; // Set the default radius
3466
3467var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3468 return Math.min(width / 10, height / 10, 8);
3469};
3470var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3471 return 8;
3472};
3473var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3474 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3475}; // get curve width, height, and control point position offsets as a percentage of node height / width
3476
3477var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3478 return {
3479 heightOffset: Math.min(15, 0.05 * height),
3480 widthOffset: Math.min(100, 0.25 * width),
3481 ctrlPtOffsetPct: 0.05
3482 };
3483};
3484
3485var pageRankDefaults = defaults({
3486 dampingFactor: 0.8,
3487 precision: 0.000001,
3488 iterations: 200,
3489 weight: function weight(edge) {
3490 return 1;
3491 }
3492});
3493var elesfn$7 = {
3494 pageRank: function pageRank(options) {
3495 var _pageRankDefaults = pageRankDefaults(options),
3496 dampingFactor = _pageRankDefaults.dampingFactor,
3497 precision = _pageRankDefaults.precision,
3498 iterations = _pageRankDefaults.iterations,
3499 weight = _pageRankDefaults.weight;
3500
3501 var cy = this._private.cy;
3502
3503 var _this$byGroup = this.byGroup(),
3504 nodes = _this$byGroup.nodes,
3505 edges = _this$byGroup.edges;
3506
3507 var numNodes = nodes.length;
3508 var numNodesSqd = numNodes * numNodes;
3509 var numEdges = edges.length; // Construct transposed adjacency matrix
3510 // First lets have a zeroed matrix of the right size
3511 // We'll also keep track of the sum of each column
3512
3513 var matrix = new Array(numNodesSqd);
3514 var columnSum = new Array(numNodes);
3515 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3516
3517 for (var i = 0; i < numNodes; i++) {
3518 for (var j = 0; j < numNodes; j++) {
3519 var n = i * numNodes + j;
3520 matrix[n] = 0;
3521 }
3522
3523 columnSum[i] = 0;
3524 } // Now, process edges
3525
3526
3527 for (var _i = 0; _i < numEdges; _i++) {
3528 var edge = edges[_i];
3529 var srcId = edge.data('source');
3530 var tgtId = edge.data('target'); // Don't include loops in the matrix
3531
3532 if (srcId === tgtId) {
3533 continue;
3534 }
3535
3536 var s = nodes.indexOfId(srcId);
3537 var t = nodes.indexOfId(tgtId);
3538 var w = weight(edge);
3539
3540 var _n = t * numNodes + s; // Update matrix
3541
3542
3543 matrix[_n] += w; // Update column sum
3544
3545 columnSum[s] += w;
3546 } // Add additional probability based on damping factor
3547 // Also, take into account columns that have sum = 0
3548
3549
3550 var p = 1.0 / numNodes + additionalProb; // Shorthand
3551 // Traverse matrix, column by column
3552
3553 for (var _j = 0; _j < numNodes; _j++) {
3554 if (columnSum[_j] === 0) {
3555 // No 'links' out from node jth, assume equal probability for each possible node
3556 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3557 var _n2 = _i2 * numNodes + _j;
3558
3559 matrix[_n2] = p;
3560 }
3561 } else {
3562 // Node jth has outgoing link, compute normalized probabilities
3563 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3564 var _n3 = _i3 * numNodes + _j;
3565
3566 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3567 }
3568 }
3569 } // Compute dominant eigenvector using power method
3570
3571
3572 var eigenvector = new Array(numNodes);
3573 var temp = new Array(numNodes);
3574 var previous; // Start with a vector of all 1's
3575 // Also, initialize a null vector which will be used as shorthand
3576
3577 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3578 eigenvector[_i4] = 1;
3579 }
3580
3581 for (var iter = 0; iter < iterations; iter++) {
3582 // Temp array with all 0's
3583 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3584 temp[_i5] = 0;
3585 } // Multiply matrix with previous result
3586
3587
3588 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3589 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3590 var _n4 = _i6 * numNodes + _j2;
3591
3592 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3593 }
3594 }
3595
3596 inPlaceSumNormalize(temp);
3597 previous = eigenvector;
3598 eigenvector = temp;
3599 temp = previous;
3600 var diff = 0; // Compute difference (squared module) of both vectors
3601
3602 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3603 var delta = previous[_i7] - eigenvector[_i7];
3604 diff += delta * delta;
3605 } // If difference is less than the desired threshold, stop iterating
3606
3607
3608 if (diff < precision) {
3609 break;
3610 }
3611 } // Construct result
3612
3613
3614 var res = {
3615 rank: function rank(node) {
3616 node = cy.collection(node)[0];
3617 return eigenvector[nodes.indexOf(node)];
3618 }
3619 };
3620 return res;
3621 } // pageRank
3622
3623}; // elesfn
3624
3625var defaults$1 = defaults({
3626 root: null,
3627 weight: function weight(edge) {
3628 return 1;
3629 },
3630 directed: false,
3631 alpha: 0
3632});
3633var elesfn$8 = {
3634 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3635 options = defaults$1(options);
3636 var cy = this.cy();
3637 var nodes = this.nodes();
3638 var numNodes = nodes.length;
3639
3640 if (!options.directed) {
3641 var degrees = {};
3642 var maxDegree = 0;
3643
3644 for (var i = 0; i < numNodes; i++) {
3645 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3646
3647 options.root = node;
3648 var currDegree = this.degreeCentrality(options);
3649
3650 if (maxDegree < currDegree.degree) {
3651 maxDegree = currDegree.degree;
3652 }
3653
3654 degrees[node.id()] = currDegree.degree;
3655 }
3656
3657 return {
3658 degree: function degree(node) {
3659 if (maxDegree === 0) {
3660 return 0;
3661 }
3662
3663 if (string(node)) {
3664 // from is a selector string
3665 node = cy.filter(node);
3666 }
3667
3668 return degrees[node.id()] / maxDegree;
3669 }
3670 };
3671 } else {
3672 var indegrees = {};
3673 var outdegrees = {};
3674 var maxIndegree = 0;
3675 var maxOutdegree = 0;
3676
3677 for (var _i = 0; _i < numNodes; _i++) {
3678 var _node = nodes[_i];
3679
3680 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3681
3682
3683 options.root = _node;
3684
3685 var _currDegree = this.degreeCentrality(options);
3686
3687 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3688 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3689 indegrees[id] = _currDegree.indegree;
3690 outdegrees[id] = _currDegree.outdegree;
3691 }
3692
3693 return {
3694 indegree: function indegree(node) {
3695 if (maxIndegree == 0) {
3696 return 0;
3697 }
3698
3699 if (string(node)) {
3700 // from is a selector string
3701 node = cy.filter(node);
3702 }
3703
3704 return indegrees[node.id()] / maxIndegree;
3705 },
3706 outdegree: function outdegree(node) {
3707 if (maxOutdegree === 0) {
3708 return 0;
3709 }
3710
3711 if (string(node)) {
3712 // from is a selector string
3713 node = cy.filter(node);
3714 }
3715
3716 return outdegrees[node.id()] / maxOutdegree;
3717 }
3718 };
3719 }
3720 },
3721 // degreeCentralityNormalized
3722 // Implemented from the algorithm in Opsahl's paper
3723 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3724 // check the heading 2 "Degree"
3725 degreeCentrality: function degreeCentrality(options) {
3726 options = defaults$1(options);
3727 var cy = this.cy();
3728 var callingEles = this;
3729 var _options = options,
3730 root = _options.root,
3731 weight = _options.weight,
3732 directed = _options.directed,
3733 alpha = _options.alpha;
3734 root = cy.collection(root)[0];
3735
3736 if (!directed) {
3737 var connEdges = root.connectedEdges().intersection(callingEles);
3738 var k = connEdges.length;
3739 var s = 0; // Now, sum edge weights
3740
3741 for (var i = 0; i < connEdges.length; i++) {
3742 s += weight(connEdges[i]);
3743 }
3744
3745 return {
3746 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3747 };
3748 } else {
3749 var edges = root.connectedEdges();
3750 var incoming = edges.filter(function (edge) {
3751 return edge.target().same(root) && callingEles.has(edge);
3752 });
3753 var outgoing = edges.filter(function (edge) {
3754 return edge.source().same(root) && callingEles.has(edge);
3755 });
3756 var k_in = incoming.length;
3757 var k_out = outgoing.length;
3758 var s_in = 0;
3759 var s_out = 0; // Now, sum incoming edge weights
3760
3761 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3762 s_in += weight(incoming[_i2]);
3763 } // Now, sum outgoing edge weights
3764
3765
3766 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3767 s_out += weight(outgoing[_i3]);
3768 }
3769
3770 return {
3771 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3772 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3773 };
3774 }
3775 } // degreeCentrality
3776
3777}; // elesfn
3778// nice, short mathemathical alias
3779
3780elesfn$8.dc = elesfn$8.degreeCentrality;
3781elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3782
3783var defaults$2 = defaults({
3784 harmonic: true,
3785 weight: function weight() {
3786 return 1;
3787 },
3788 directed: false,
3789 root: null
3790});
3791var elesfn$9 = {
3792 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3793 var _defaults = defaults$2(options),
3794 harmonic = _defaults.harmonic,
3795 weight = _defaults.weight,
3796 directed = _defaults.directed;
3797
3798 var cy = this.cy();
3799 var closenesses = {};
3800 var maxCloseness = 0;
3801 var nodes = this.nodes();
3802 var fw = this.floydWarshall({
3803 weight: weight,
3804 directed: directed
3805 }); // Compute closeness for every node and find the maximum closeness
3806
3807 for (var i = 0; i < nodes.length; i++) {
3808 var currCloseness = 0;
3809 var node_i = nodes[i];
3810
3811 for (var j = 0; j < nodes.length; j++) {
3812 if (i !== j) {
3813 var d = fw.distance(node_i, nodes[j]);
3814
3815 if (harmonic) {
3816 currCloseness += 1 / d;
3817 } else {
3818 currCloseness += d;
3819 }
3820 }
3821 }
3822
3823 if (!harmonic) {
3824 currCloseness = 1 / currCloseness;
3825 }
3826
3827 if (maxCloseness < currCloseness) {
3828 maxCloseness = currCloseness;
3829 }
3830
3831 closenesses[node_i.id()] = currCloseness;
3832 }
3833
3834 return {
3835 closeness: function closeness(node) {
3836 if (maxCloseness == 0) {
3837 return 0;
3838 }
3839
3840 if (string(node)) {
3841 // from is a selector string
3842 node = cy.filter(node)[0].id();
3843 } else {
3844 // from is a node
3845 node = node.id();
3846 }
3847
3848 return closenesses[node] / maxCloseness;
3849 }
3850 };
3851 },
3852 // Implemented from pseudocode from wikipedia
3853 closenessCentrality: function closenessCentrality(options) {
3854 var _defaults2 = defaults$2(options),
3855 root = _defaults2.root,
3856 weight = _defaults2.weight,
3857 directed = _defaults2.directed,
3858 harmonic = _defaults2.harmonic;
3859
3860 root = this.filter(root)[0]; // we need distance from this node to every other node
3861
3862 var dijkstra = this.dijkstra({
3863 root: root,
3864 weight: weight,
3865 directed: directed
3866 });
3867 var totalDistance = 0;
3868 var nodes = this.nodes();
3869
3870 for (var i = 0; i < nodes.length; i++) {
3871 var n = nodes[i];
3872
3873 if (!n.same(root)) {
3874 var d = dijkstra.distanceTo(n);
3875
3876 if (harmonic) {
3877 totalDistance += 1 / d;
3878 } else {
3879 totalDistance += d;
3880 }
3881 }
3882 }
3883
3884 return harmonic ? totalDistance : 1 / totalDistance;
3885 } // closenessCentrality
3886
3887}; // elesfn
3888// nice, short mathemathical alias
3889
3890elesfn$9.cc = elesfn$9.closenessCentrality;
3891elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3892
3893var defaults$3 = defaults({
3894 weight: null,
3895 directed: false
3896});
3897var elesfn$a = {
3898 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3899 betweennessCentrality: function betweennessCentrality(options) {
3900 var _defaults = defaults$3(options),
3901 directed = _defaults.directed,
3902 weight = _defaults.weight;
3903
3904 var weighted = weight != null;
3905 var cy = this.cy(); // starting
3906
3907 var V = this.nodes();
3908 var A = {};
3909 var _C = {};
3910 var max = 0;
3911 var C = {
3912 set: function set(key, val) {
3913 _C[key] = val;
3914
3915 if (val > max) {
3916 max = val;
3917 }
3918 },
3919 get: function get(key) {
3920 return _C[key];
3921 }
3922 }; // A contains the neighborhoods of every node
3923
3924 for (var i = 0; i < V.length; i++) {
3925 var v = V[i];
3926 var vid = v.id();
3927
3928 if (directed) {
3929 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3930 } else {
3931 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3932 }
3933
3934 C.set(vid, 0);
3935 }
3936
3937 var _loop = function _loop(s) {
3938 var sid = V[s].id();
3939 var S = []; // stack
3940
3941 var P = {};
3942 var g = {};
3943 var d = {};
3944 var Q = new Heap(function (a, b) {
3945 return d[a] - d[b];
3946 }); // queue
3947 // init dictionaries
3948
3949 for (var _i = 0; _i < V.length; _i++) {
3950 var _vid = V[_i].id();
3951
3952 P[_vid] = [];
3953 g[_vid] = 0;
3954 d[_vid] = Infinity;
3955 }
3956
3957 g[sid] = 1; // sigma
3958
3959 d[sid] = 0; // distance to s
3960
3961 Q.push(sid);
3962
3963 while (!Q.empty()) {
3964 var _v = Q.pop();
3965
3966 S.push(_v);
3967
3968 if (weighted) {
3969 for (var j = 0; j < A[_v].length; j++) {
3970 var w = A[_v][j];
3971 var vEle = cy.getElementById(_v);
3972 var edge = void 0;
3973
3974 if (vEle.edgesTo(w).length > 0) {
3975 edge = vEle.edgesTo(w)[0];
3976 } else {
3977 edge = w.edgesTo(vEle)[0];
3978 }
3979
3980 var edgeWeight = weight(edge);
3981 w = w.id();
3982
3983 if (d[w] > d[_v] + edgeWeight) {
3984 d[w] = d[_v] + edgeWeight;
3985
3986 if (Q.nodes.indexOf(w) < 0) {
3987 //if w is not in Q
3988 Q.push(w);
3989 } else {
3990 // update position if w is in Q
3991 Q.updateItem(w);
3992 }
3993
3994 g[w] = 0;
3995 P[w] = [];
3996 }
3997
3998 if (d[w] == d[_v] + edgeWeight) {
3999 g[w] = g[w] + g[_v];
4000 P[w].push(_v);
4001 }
4002 }
4003 } else {
4004 for (var _j = 0; _j < A[_v].length; _j++) {
4005 var _w = A[_v][_j].id();
4006
4007 if (d[_w] == Infinity) {
4008 Q.push(_w);
4009 d[_w] = d[_v] + 1;
4010 }
4011
4012 if (d[_w] == d[_v] + 1) {
4013 g[_w] = g[_w] + g[_v];
4014
4015 P[_w].push(_v);
4016 }
4017 }
4018 }
4019 }
4020
4021 var e = {};
4022
4023 for (var _i2 = 0; _i2 < V.length; _i2++) {
4024 e[V[_i2].id()] = 0;
4025 }
4026
4027 while (S.length > 0) {
4028 var _w2 = S.pop();
4029
4030 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4031 var _v2 = P[_w2][_j2];
4032 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4033 }
4034
4035 if (_w2 != V[s].id()) {
4036 C.set(_w2, C.get(_w2) + e[_w2]);
4037 }
4038 }
4039 };
4040
4041 for (var s = 0; s < V.length; s++) {
4042 _loop(s);
4043 }
4044
4045 var ret = {
4046 betweenness: function betweenness(node) {
4047 var id = cy.collection(node).id();
4048 return C.get(id);
4049 },
4050 betweennessNormalized: function betweennessNormalized(node) {
4051 if (max == 0) {
4052 return 0;
4053 }
4054
4055 var id = cy.collection(node).id();
4056 return C.get(id) / max;
4057 }
4058 }; // alias
4059
4060 ret.betweennessNormalised = ret.betweennessNormalized;
4061 return ret;
4062 } // betweennessCentrality
4063
4064}; // elesfn
4065// nice, short mathemathical alias
4066
4067elesfn$a.bc = elesfn$a.betweennessCentrality;
4068
4069// Implemented by Zoe Xi @zoexi for GSOC 2016
4070/* eslint-disable no-unused-vars */
4071
4072var defaults$4 = defaults({
4073 expandFactor: 2,
4074 // affects time of computation and cluster granularity to some extent: M * M
4075 inflateFactor: 2,
4076 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4077 multFactor: 1,
4078 // optional self loops for each node. Use a neutral value to improve cluster computations.
4079 maxIterations: 20,
4080 // maximum number of iterations of the MCL algorithm in a single run
4081 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4082 function (edge) {
4083 return 1;
4084 }]
4085});
4086/* eslint-enable */
4087
4088var setOptions = function setOptions(options) {
4089 return defaults$4(options);
4090};
4091/* eslint-enable */
4092
4093
4094var getSimilarity = function getSimilarity(edge, attributes) {
4095 var total = 0;
4096
4097 for (var i = 0; i < attributes.length; i++) {
4098 total += attributes[i](edge);
4099 }
4100
4101 return total;
4102};
4103
4104var addLoops = function addLoops(M, n, val) {
4105 for (var i = 0; i < n; i++) {
4106 M[i * n + i] = val;
4107 }
4108};
4109
4110var normalize = function normalize(M, n) {
4111 var sum;
4112
4113 for (var col = 0; col < n; col++) {
4114 sum = 0;
4115
4116 for (var row = 0; row < n; row++) {
4117 sum += M[row * n + col];
4118 }
4119
4120 for (var _row = 0; _row < n; _row++) {
4121 M[_row * n + col] = M[_row * n + col] / sum;
4122 }
4123 }
4124}; // TODO: blocked matrix multiplication?
4125
4126
4127var mmult = function mmult(A, B, n) {
4128 var C = new Array(n * n);
4129
4130 for (var i = 0; i < n; i++) {
4131 for (var j = 0; j < n; j++) {
4132 C[i * n + j] = 0;
4133 }
4134
4135 for (var k = 0; k < n; k++) {
4136 for (var _j = 0; _j < n; _j++) {
4137 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4138 }
4139 }
4140 }
4141
4142 return C;
4143};
4144
4145var expand = function expand(M, n, expandFactor
4146/** power **/
4147) {
4148 var _M = M.slice(0);
4149
4150 for (var p = 1; p < expandFactor; p++) {
4151 M = mmult(M, _M, n);
4152 }
4153
4154 return M;
4155};
4156
4157var inflate = function inflate(M, n, inflateFactor
4158/** r **/
4159) {
4160 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4161
4162
4163 for (var i = 0; i < n * n; i++) {
4164 _M[i] = Math.pow(M[i], inflateFactor);
4165 }
4166
4167 normalize(_M, n);
4168 return _M;
4169};
4170
4171var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4172 // Check that both matrices have the same elements (i,j)
4173 for (var i = 0; i < n2; i++) {
4174 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4175
4176 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4177
4178 if (v1 !== v2) {
4179 return false;
4180 }
4181 }
4182
4183 return true;
4184};
4185
4186var assign = function assign(M, n, nodes, cy) {
4187 var clusters = [];
4188
4189 for (var i = 0; i < n; i++) {
4190 var cluster = [];
4191
4192 for (var j = 0; j < n; j++) {
4193 // Row-wise attractors and elements that they attract belong in same cluster
4194 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4195 cluster.push(nodes[j]);
4196 }
4197 }
4198
4199 if (cluster.length !== 0) {
4200 clusters.push(cy.collection(cluster));
4201 }
4202 }
4203
4204 return clusters;
4205};
4206
4207var isDuplicate = function isDuplicate(c1, c2) {
4208 for (var i = 0; i < c1.length; i++) {
4209 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4210 return false;
4211 }
4212 }
4213
4214 return true;
4215};
4216
4217var removeDuplicates = function removeDuplicates(clusters) {
4218 for (var i = 0; i < clusters.length; i++) {
4219 for (var j = 0; j < clusters.length; j++) {
4220 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4221 clusters.splice(j, 1);
4222 }
4223 }
4224 }
4225
4226 return clusters;
4227};
4228
4229var markovClustering = function markovClustering(options) {
4230 var nodes = this.nodes();
4231 var edges = this.edges();
4232 var cy = this.cy(); // Set parameters of algorithm:
4233
4234 var opts = setOptions(options); // Map each node to its position in node array
4235
4236 var id2position = {};
4237
4238 for (var i = 0; i < nodes.length; i++) {
4239 id2position[nodes[i].id()] = i;
4240 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4241
4242
4243 var n = nodes.length,
4244 n2 = n * n;
4245
4246 var M = new Array(n2),
4247 _M;
4248
4249 for (var _i = 0; _i < n2; _i++) {
4250 M[_i] = 0;
4251 }
4252
4253 for (var e = 0; e < edges.length; e++) {
4254 var edge = edges[e];
4255 var _i2 = id2position[edge.source().id()];
4256 var j = id2position[edge.target().id()];
4257 var sim = getSimilarity(edge, opts.attributes);
4258 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4259
4260 M[j * n + _i2] += sim;
4261 } // Begin Markov cluster algorithm
4262 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4263
4264
4265 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4266
4267 normalize(M, n);
4268 var isStillMoving = true;
4269 var iterations = 0;
4270
4271 while (isStillMoving && iterations < opts.maxIterations) {
4272 isStillMoving = false; // Step 3:
4273
4274 _M = expand(M, n, opts.expandFactor); // Step 4:
4275
4276 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4277
4278 if (!hasConverged(M, _M, n2, 4)) {
4279 isStillMoving = true;
4280 }
4281
4282 iterations++;
4283 } // Build clusters from matrix
4284
4285
4286 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4287
4288 clusters = removeDuplicates(clusters);
4289 return clusters;
4290};
4291
4292var markovClustering$1 = {
4293 markovClustering: markovClustering,
4294 mcl: markovClustering
4295};
4296
4297// Common distance metrics for clustering algorithms
4298
4299var identity = function identity(x) {
4300 return x;
4301};
4302
4303var absDiff = function absDiff(p, q) {
4304 return Math.abs(q - p);
4305};
4306
4307var addAbsDiff = function addAbsDiff(total, p, q) {
4308 return total + absDiff(p, q);
4309};
4310
4311var addSquaredDiff = function addSquaredDiff(total, p, q) {
4312 return total + Math.pow(q - p, 2);
4313};
4314
4315var sqrt = function sqrt(x) {
4316 return Math.sqrt(x);
4317};
4318
4319var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4320 return Math.max(currentMax, absDiff(p, q));
4321};
4322
4323var getDistance = function getDistance(length, getP, getQ, init, visit) {
4324 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4325 var ret = init;
4326 var p, q;
4327
4328 for (var dim = 0; dim < length; dim++) {
4329 p = getP(dim);
4330 q = getQ(dim);
4331 ret = visit(ret, p, q);
4332 }
4333
4334 return post(ret);
4335};
4336
4337var distances = {
4338 euclidean: function euclidean(length, getP, getQ) {
4339 if (length >= 2) {
4340 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4341 } else {
4342 // for single attr case, more efficient to avoid sqrt
4343 return getDistance(length, getP, getQ, 0, addAbsDiff);
4344 }
4345 },
4346 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4347 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4348 },
4349 manhattan: function manhattan(length, getP, getQ) {
4350 return getDistance(length, getP, getQ, 0, addAbsDiff);
4351 },
4352 max: function max(length, getP, getQ) {
4353 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4354 }
4355}; // in case the user accidentally doesn't use camel case
4356
4357distances['squared-euclidean'] = distances['squaredEuclidean'];
4358distances['squaredeuclidean'] = distances['squaredEuclidean'];
4359function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4360 var impl;
4361
4362 if (fn(method)) {
4363 impl = method;
4364 } else {
4365 impl = distances[method] || distances.euclidean;
4366 }
4367
4368 if (length === 0 && fn(method)) {
4369 return impl(nodeP, nodeQ);
4370 } else {
4371 return impl(length, getP, getQ, nodeP, nodeQ);
4372 }
4373}
4374
4375var defaults$5 = defaults({
4376 k: 2,
4377 m: 2,
4378 sensitivityThreshold: 0.0001,
4379 distance: 'euclidean',
4380 maxIterations: 10,
4381 attributes: [],
4382 testMode: false,
4383 testCentroids: null
4384});
4385
4386var setOptions$1 = function setOptions(options) {
4387 return defaults$5(options);
4388};
4389/* eslint-enable */
4390
4391
4392var getDist = function getDist(type, node, centroid, attributes, mode) {
4393 var noNodeP = mode !== 'kMedoids';
4394 var getP = noNodeP ? function (i) {
4395 return centroid[i];
4396 } : function (i) {
4397 return attributes[i](centroid);
4398 };
4399
4400 var getQ = function getQ(i) {
4401 return attributes[i](node);
4402 };
4403
4404 var nodeP = centroid;
4405 var nodeQ = node;
4406 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4407};
4408
4409var randomCentroids = function randomCentroids(nodes, k, attributes) {
4410 var ndim = attributes.length;
4411 var min = new Array(ndim);
4412 var max = new Array(ndim);
4413 var centroids = new Array(k);
4414 var centroid = null; // Find min, max values for each attribute dimension
4415
4416 for (var i = 0; i < ndim; i++) {
4417 min[i] = nodes.min(attributes[i]).value;
4418 max[i] = nodes.max(attributes[i]).value;
4419 } // Build k centroids, each represented as an n-dim feature vector
4420
4421
4422 for (var c = 0; c < k; c++) {
4423 centroid = [];
4424
4425 for (var _i = 0; _i < ndim; _i++) {
4426 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4427 }
4428
4429 centroids[c] = centroid;
4430 }
4431
4432 return centroids;
4433};
4434
4435var classify = function classify(node, centroids, distance, attributes, type) {
4436 var min = Infinity;
4437 var index = 0;
4438
4439 for (var i = 0; i < centroids.length; i++) {
4440 var dist = getDist(distance, node, centroids[i], attributes, type);
4441
4442 if (dist < min) {
4443 min = dist;
4444 index = i;
4445 }
4446 }
4447
4448 return index;
4449};
4450
4451var buildCluster = function buildCluster(centroid, nodes, assignment) {
4452 var cluster = [];
4453 var node = null;
4454
4455 for (var n = 0; n < nodes.length; n++) {
4456 node = nodes[n];
4457
4458 if (assignment[node.id()] === centroid) {
4459 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4460 cluster.push(node);
4461 }
4462 }
4463
4464 return cluster;
4465};
4466
4467var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4468 return Math.abs(v2 - v1) <= sensitivityThreshold;
4469};
4470
4471var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4472 for (var i = 0; i < v1.length; i++) {
4473 for (var j = 0; j < v1[i].length; j++) {
4474 var diff = Math.abs(v1[i][j] - v2[i][j]);
4475
4476 if (diff > sensitivityThreshold) {
4477 return false;
4478 }
4479 }
4480 }
4481
4482 return true;
4483};
4484
4485var seenBefore = function seenBefore(node, medoids, n) {
4486 for (var i = 0; i < n; i++) {
4487 if (node === medoids[i]) return true;
4488 }
4489
4490 return false;
4491};
4492
4493var randomMedoids = function randomMedoids(nodes, k) {
4494 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4495 // so we need to check to see if we've already seen or chose this node before.
4496
4497 if (nodes.length < 50) {
4498 // Randomly select k medoids from the n nodes
4499 for (var i = 0; i < k; i++) {
4500 var node = nodes[Math.floor(Math.random() * nodes.length)]; // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
4501 // Instead choose a different random node.
4502
4503 while (seenBefore(node, medoids, i)) {
4504 node = nodes[Math.floor(Math.random() * nodes.length)];
4505 }
4506
4507 medoids[i] = node;
4508 }
4509 } else {
4510 // Relatively large data set, so pretty safe to not check and just select random nodes
4511 for (var _i2 = 0; _i2 < k; _i2++) {
4512 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4513 }
4514 }
4515
4516 return medoids;
4517};
4518
4519var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4520 var cost = 0;
4521
4522 for (var n = 0; n < cluster.length; n++) {
4523 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4524 }
4525
4526 return cost;
4527};
4528
4529var kMeans = function kMeans(options) {
4530 var cy = this.cy();
4531 var nodes = this.nodes();
4532 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4533
4534 var opts = setOptions$1(options); // Begin k-means algorithm
4535
4536 var clusters = new Array(opts.k);
4537 var assignment = {};
4538 var centroids; // Step 1: Initialize centroid positions
4539
4540 if (opts.testMode) {
4541 if (typeof opts.testCentroids === 'number') {
4542 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4543 } else if (_typeof(opts.testCentroids) === 'object') {
4544 centroids = opts.testCentroids;
4545 } else {
4546 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4547 }
4548 } else {
4549 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4550 }
4551
4552 var isStillMoving = true;
4553 var iterations = 0;
4554
4555 while (isStillMoving && iterations < opts.maxIterations) {
4556 // Step 2: Assign nodes to the nearest centroid
4557 for (var n = 0; n < nodes.length; n++) {
4558 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4559
4560 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4561 } // Step 3: For each of the k clusters, update its centroid
4562
4563
4564 isStillMoving = false;
4565
4566 for (var c = 0; c < opts.k; c++) {
4567 // Get all nodes that belong to this cluster
4568 var cluster = buildCluster(c, nodes, assignment);
4569
4570 if (cluster.length === 0) {
4571 // If cluster is empty, break out early & move to next cluster
4572 continue;
4573 } // Update centroids by calculating avg of all nodes within the cluster.
4574
4575
4576 var ndim = opts.attributes.length;
4577 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4578
4579 var newCentroid = new Array(ndim);
4580 var sum = new Array(ndim);
4581
4582 for (var d = 0; d < ndim; d++) {
4583 sum[d] = 0.0;
4584
4585 for (var i = 0; i < cluster.length; i++) {
4586 node = cluster[i];
4587 sum[d] += opts.attributes[d](node);
4588 }
4589
4590 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4591
4592 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4593 isStillMoving = true;
4594 }
4595 }
4596
4597 centroids[c] = newCentroid;
4598 clusters[c] = cy.collection(cluster);
4599 }
4600
4601 iterations++;
4602 }
4603
4604 return clusters;
4605};
4606
4607var kMedoids = function kMedoids(options) {
4608 var cy = this.cy();
4609 var nodes = this.nodes();
4610 var node = null;
4611 var opts = setOptions$1(options); // Begin k-medoids algorithm
4612
4613 var clusters = new Array(opts.k);
4614 var medoids;
4615 var assignment = {};
4616 var curCost;
4617 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4618 // Step 1: Initialize k medoids
4619
4620 if (opts.testMode) {
4621 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4622 medoids = opts.testCentroids;
4623 } else {
4624 medoids = randomMedoids(nodes, opts.k);
4625 }
4626 } else {
4627 medoids = randomMedoids(nodes, opts.k);
4628 }
4629
4630 var isStillMoving = true;
4631 var iterations = 0;
4632
4633 while (isStillMoving && iterations < opts.maxIterations) {
4634 // Step 2: Assign nodes to the nearest medoid
4635 for (var n = 0; n < nodes.length; n++) {
4636 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4637
4638 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4639 }
4640
4641 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4642 // select the node with the lowest configuration cost as new medoid.
4643
4644 for (var m = 0; m < medoids.length; m++) {
4645 // Get all nodes that belong to this medoid
4646 var cluster = buildCluster(m, nodes, assignment);
4647
4648 if (cluster.length === 0) {
4649 // If cluster is empty, break out early & move to next cluster
4650 continue;
4651 }
4652
4653 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4654 // Select different medoid if its configuration has the lowest cost
4655
4656 for (var _n = 0; _n < cluster.length; _n++) {
4657 curCost = findCost(cluster[_n], cluster, opts.attributes);
4658
4659 if (curCost < minCosts[m]) {
4660 minCosts[m] = curCost;
4661 medoids[m] = cluster[_n];
4662 isStillMoving = true;
4663 }
4664 }
4665
4666 clusters[m] = cy.collection(cluster);
4667 }
4668
4669 iterations++;
4670 }
4671
4672 return clusters;
4673};
4674
4675var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4676 var numerator, denominator;
4677
4678 for (var n = 0; n < nodes.length; n++) {
4679 for (var c = 0; c < centroids.length; c++) {
4680 weight[n][c] = Math.pow(U[n][c], opts.m);
4681 }
4682 }
4683
4684 for (var _c = 0; _c < centroids.length; _c++) {
4685 for (var dim = 0; dim < opts.attributes.length; dim++) {
4686 numerator = 0;
4687 denominator = 0;
4688
4689 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4690 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4691 denominator += weight[_n2][_c];
4692 }
4693
4694 centroids[_c][dim] = numerator / denominator;
4695 }
4696 }
4697};
4698
4699var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4700 // Save previous step
4701 for (var i = 0; i < U.length; i++) {
4702 _U[i] = U[i].slice();
4703 }
4704
4705 var sum, numerator, denominator;
4706 var pow = 2 / (opts.m - 1);
4707
4708 for (var c = 0; c < centroids.length; c++) {
4709 for (var n = 0; n < nodes.length; n++) {
4710 sum = 0;
4711
4712 for (var k = 0; k < centroids.length; k++) {
4713 // against all other centroids
4714 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4715 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4716 sum += Math.pow(numerator / denominator, pow);
4717 }
4718
4719 U[n][c] = 1 / sum;
4720 }
4721 }
4722};
4723
4724var assign$1 = function assign(nodes, U, opts, cy) {
4725 var clusters = new Array(opts.k);
4726
4727 for (var c = 0; c < clusters.length; c++) {
4728 clusters[c] = [];
4729 }
4730
4731 var max;
4732 var index;
4733
4734 for (var n = 0; n < U.length; n++) {
4735 // for each node (U is N x C matrix)
4736 max = -Infinity;
4737 index = -1; // Determine which cluster the node is most likely to belong in
4738
4739 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4740 if (U[n][_c2] > max) {
4741 max = U[n][_c2];
4742 index = _c2;
4743 }
4744 }
4745
4746 clusters[index].push(nodes[n]);
4747 } // Turn every array into a collection of nodes
4748
4749
4750 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4751 clusters[_c3] = cy.collection(clusters[_c3]);
4752 }
4753
4754 return clusters;
4755};
4756
4757var fuzzyCMeans = function fuzzyCMeans(options) {
4758 var cy = this.cy();
4759 var nodes = this.nodes();
4760 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4761
4762 var clusters;
4763 var centroids;
4764 var U;
4765
4766 var _U;
4767
4768 var weight; // Step 1: Initialize letiables.
4769
4770 _U = new Array(nodes.length);
4771
4772 for (var i = 0; i < nodes.length; i++) {
4773 // N x C matrix
4774 _U[i] = new Array(opts.k);
4775 }
4776
4777 U = new Array(nodes.length);
4778
4779 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4780 // N x C matrix
4781 U[_i3] = new Array(opts.k);
4782 }
4783
4784 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4785 var total = 0;
4786
4787 for (var j = 0; j < opts.k; j++) {
4788 U[_i4][j] = Math.random();
4789 total += U[_i4][j];
4790 }
4791
4792 for (var _j = 0; _j < opts.k; _j++) {
4793 U[_i4][_j] = U[_i4][_j] / total;
4794 }
4795 }
4796
4797 centroids = new Array(opts.k);
4798
4799 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4800 centroids[_i5] = new Array(opts.attributes.length);
4801 }
4802
4803 weight = new Array(nodes.length);
4804
4805 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4806 // N x C matrix
4807 weight[_i6] = new Array(opts.k);
4808 } // end init FCM
4809
4810
4811 var isStillMoving = true;
4812 var iterations = 0;
4813
4814 while (isStillMoving && iterations < opts.maxIterations) {
4815 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4816
4817 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4818
4819 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4820
4821 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4822 isStillMoving = true;
4823 }
4824
4825 iterations++;
4826 } // Assign nodes to clusters with highest probability.
4827
4828
4829 clusters = assign$1(nodes, U, opts, cy);
4830 return {
4831 clusters: clusters,
4832 degreeOfMembership: U
4833 };
4834};
4835
4836var kClustering = {
4837 kMeans: kMeans,
4838 kMedoids: kMedoids,
4839 fuzzyCMeans: fuzzyCMeans,
4840 fcm: fuzzyCMeans
4841};
4842
4843// Implemented by Zoe Xi @zoexi for GSOC 2016
4844var defaults$6 = defaults({
4845 distance: 'euclidean',
4846 // distance metric to compare nodes
4847 linkage: 'min',
4848 // linkage criterion : how to determine the distance between clusters of nodes
4849 mode: 'threshold',
4850 // mode:'threshold' => clusters must be threshold distance apart
4851 threshold: Infinity,
4852 // the distance threshold
4853 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4854 addDendrogram: false,
4855 // whether to add the dendrogram to the graph for viz
4856 dendrogramDepth: 0,
4857 // depth at which dendrogram branches are merged into the returned clusters
4858 attributes: [] // array of attr functions
4859
4860});
4861var linkageAliases = {
4862 'single': 'min',
4863 'complete': 'max'
4864};
4865
4866var setOptions$2 = function setOptions(options) {
4867 var opts = defaults$6(options);
4868 var preferredAlias = linkageAliases[opts.linkage];
4869
4870 if (preferredAlias != null) {
4871 opts.linkage = preferredAlias;
4872 }
4873
4874 return opts;
4875};
4876
4877var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4878 // Find two closest clusters from cached mins
4879 var minKey = 0;
4880 var min = Infinity;
4881 var dist;
4882 var attrs = opts.attributes;
4883
4884 var getDist = function getDist(n1, n2) {
4885 return clusteringDistance(opts.distance, attrs.length, function (i) {
4886 return attrs[i](n1);
4887 }, function (i) {
4888 return attrs[i](n2);
4889 }, n1, n2);
4890 };
4891
4892 for (var i = 0; i < clusters.length; i++) {
4893 var key = clusters[i].key;
4894 var _dist = dists[key][mins[key]];
4895
4896 if (_dist < min) {
4897 minKey = key;
4898 min = _dist;
4899 }
4900 }
4901
4902 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4903 return false;
4904 }
4905
4906 var c1 = index[minKey];
4907 var c2 = index[mins[minKey]];
4908 var merged; // Merge two closest clusters
4909
4910 if (opts.mode === 'dendrogram') {
4911 merged = {
4912 left: c1,
4913 right: c2,
4914 key: c1.key
4915 };
4916 } else {
4917 merged = {
4918 value: c1.value.concat(c2.value),
4919 key: c1.key
4920 };
4921 }
4922
4923 clusters[c1.index] = merged;
4924 clusters.splice(c2.index, 1);
4925 index[c1.key] = merged; // Update distances with new merged cluster
4926
4927 for (var _i = 0; _i < clusters.length; _i++) {
4928 var cur = clusters[_i];
4929
4930 if (c1.key === cur.key) {
4931 dist = Infinity;
4932 } else if (opts.linkage === 'min') {
4933 dist = dists[c1.key][cur.key];
4934
4935 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4936 dist = dists[c2.key][cur.key];
4937 }
4938 } else if (opts.linkage === 'max') {
4939 dist = dists[c1.key][cur.key];
4940
4941 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4942 dist = dists[c2.key][cur.key];
4943 }
4944 } else if (opts.linkage === 'mean') {
4945 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4946 } else {
4947 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4948 }
4949
4950 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4951 } // Update cached mins
4952
4953
4954 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4955 var key1 = clusters[_i2].key;
4956
4957 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4958 var _min = key1;
4959
4960 for (var j = 0; j < clusters.length; j++) {
4961 var key2 = clusters[j].key;
4962
4963 if (dists[key1][key2] < dists[key1][_min]) {
4964 _min = key2;
4965 }
4966 }
4967
4968 mins[key1] = _min;
4969 }
4970
4971 clusters[_i2].index = _i2;
4972 } // Clean up meta data used for clustering
4973
4974
4975 c1.key = c2.key = c1.index = c2.index = null;
4976 return true;
4977};
4978
4979var getAllChildren = function getAllChildren(root, arr, cy) {
4980 if (!root) return;
4981
4982 if (root.value) {
4983 arr.push(root.value);
4984 } else {
4985 if (root.left) getAllChildren(root.left, arr);
4986 if (root.right) getAllChildren(root.right, arr);
4987 }
4988};
4989
4990var buildDendrogram = function buildDendrogram(root, cy) {
4991 if (!root) return '';
4992
4993 if (root.left && root.right) {
4994 var leftStr = buildDendrogram(root.left, cy);
4995 var rightStr = buildDendrogram(root.right, cy);
4996 var node = cy.add({
4997 group: 'nodes',
4998 data: {
4999 id: leftStr + ',' + rightStr
5000 }
5001 });
5002 cy.add({
5003 group: 'edges',
5004 data: {
5005 source: leftStr,
5006 target: node.id()
5007 }
5008 });
5009 cy.add({
5010 group: 'edges',
5011 data: {
5012 source: rightStr,
5013 target: node.id()
5014 }
5015 });
5016 return node.id();
5017 } else if (root.value) {
5018 return root.value.id();
5019 }
5020};
5021
5022var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5023 if (!root) return [];
5024 var left = [],
5025 right = [],
5026 leaves = [];
5027
5028 if (k === 0) {
5029 // don't cut tree, simply return all nodes as 1 single cluster
5030 if (root.left) getAllChildren(root.left, left);
5031 if (root.right) getAllChildren(root.right, right);
5032 leaves = left.concat(right);
5033 return [cy.collection(leaves)];
5034 } else if (k === 1) {
5035 // cut at root
5036 if (root.value) {
5037 // leaf node
5038 return [cy.collection(root.value)];
5039 } else {
5040 if (root.left) getAllChildren(root.left, left);
5041 if (root.right) getAllChildren(root.right, right);
5042 return [cy.collection(left), cy.collection(right)];
5043 }
5044 } else {
5045 if (root.value) {
5046 return [cy.collection(root.value)];
5047 } else {
5048 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5049 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5050 return left.concat(right);
5051 }
5052 }
5053};
5054/* eslint-enable */
5055
5056
5057var hierarchicalClustering = function hierarchicalClustering(options) {
5058 var cy = this.cy();
5059 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5060
5061 var opts = setOptions$2(options);
5062 var attrs = opts.attributes;
5063
5064 var getDist = function getDist(n1, n2) {
5065 return clusteringDistance(opts.distance, attrs.length, function (i) {
5066 return attrs[i](n1);
5067 }, function (i) {
5068 return attrs[i](n2);
5069 }, n1, n2);
5070 }; // Begin hierarchical algorithm
5071
5072
5073 var clusters = [];
5074 var dists = []; // distances between each pair of clusters
5075
5076 var mins = []; // closest cluster for each cluster
5077
5078 var index = []; // hash of all clusters by key
5079 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5080
5081 for (var n = 0; n < nodes.length; n++) {
5082 var cluster = {
5083 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5084 key: n,
5085 index: n
5086 };
5087 clusters[n] = cluster;
5088 index[n] = cluster;
5089 dists[n] = [];
5090 mins[n] = 0;
5091 } // Calculate the distance between each pair of clusters
5092
5093
5094 for (var i = 0; i < clusters.length; i++) {
5095 for (var j = 0; j <= i; j++) {
5096 var dist = void 0;
5097
5098 if (opts.mode === 'dendrogram') {
5099 // modes store cluster values differently
5100 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5101 } else {
5102 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5103 }
5104
5105 dists[i][j] = dist;
5106 dists[j][i] = dist;
5107
5108 if (dist < dists[i][mins[i]]) {
5109 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5110 }
5111 }
5112 } // Find the closest pair of clusters and merge them into a single cluster.
5113 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5114
5115
5116 var merged = mergeClosest(clusters, index, dists, mins, opts);
5117
5118 while (merged) {
5119 merged = mergeClosest(clusters, index, dists, mins, opts);
5120 }
5121
5122 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5123 // in addition to returning the clusters.
5124
5125 if (opts.mode === 'dendrogram') {
5126 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5127 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5128 } else {
5129 // Regular mode simply returns the clusters
5130 retClusters = new Array(clusters.length);
5131 clusters.forEach(function (cluster, i) {
5132 // Clean up meta data used for clustering
5133 cluster.key = cluster.index = null;
5134 retClusters[i] = cy.collection(cluster.value);
5135 });
5136 }
5137
5138 return retClusters;
5139};
5140
5141var hierarchicalClustering$1 = {
5142 hierarchicalClustering: hierarchicalClustering,
5143 hca: hierarchicalClustering
5144};
5145
5146// Implemented by Zoe Xi @zoexi for GSOC 2016
5147var defaults$7 = defaults({
5148 distance: 'euclidean',
5149 // distance metric to compare attributes between two nodes
5150 preference: 'median',
5151 // suitability of a data point to serve as an exemplar
5152 damping: 0.8,
5153 // damping factor between [0.5, 1)
5154 maxIterations: 1000,
5155 // max number of iterations to run
5156 minIterations: 100,
5157 // min number of iterations to run in order for clustering to stop
5158 attributes: [// functions to quantify the similarity between any two points
5159 // e.g. node => node.data('weight')
5160 ]
5161});
5162
5163var setOptions$3 = function setOptions(options) {
5164 var dmp = options.damping;
5165 var pref = options.preference;
5166
5167 if (!(0.5 <= dmp && dmp < 1)) {
5168 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5169 }
5170
5171 var validPrefs = ['median', 'mean', 'min', 'max'];
5172
5173 if (!(validPrefs.some(function (v) {
5174 return v === pref;
5175 }) || number(pref))) {
5176 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5177 return "'".concat(p, "'");
5178 }).join(', '), "] or a number. Got: ").concat(pref));
5179 }
5180
5181 return defaults$7(options);
5182};
5183/* eslint-enable */
5184
5185
5186var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5187 var attr = function attr(n, i) {
5188 return attributes[i](n);
5189 }; // nb negative because similarity should have an inverse relationship to distance
5190
5191
5192 return -clusteringDistance(type, attributes.length, function (i) {
5193 return attr(n1, i);
5194 }, function (i) {
5195 return attr(n2, i);
5196 }, n1, n2);
5197};
5198
5199var getPreference = function getPreference(S, preference) {
5200 // larger preference = greater # of clusters
5201 var p = null;
5202
5203 if (preference === 'median') {
5204 p = median(S);
5205 } else if (preference === 'mean') {
5206 p = mean(S);
5207 } else if (preference === 'min') {
5208 p = min(S);
5209 } else if (preference === 'max') {
5210 p = max(S);
5211 } else {
5212 // Custom preference number, as set by user
5213 p = preference;
5214 }
5215
5216 return p;
5217};
5218
5219var findExemplars = function findExemplars(n, R, A) {
5220 var indices = [];
5221
5222 for (var i = 0; i < n; i++) {
5223 if (R[i * n + i] + A[i * n + i] > 0) {
5224 indices.push(i);
5225 }
5226 }
5227
5228 return indices;
5229};
5230
5231var assignClusters = function assignClusters(n, S, exemplars) {
5232 var clusters = [];
5233
5234 for (var i = 0; i < n; i++) {
5235 var index = -1;
5236 var max = -Infinity;
5237
5238 for (var ei = 0; ei < exemplars.length; ei++) {
5239 var e = exemplars[ei];
5240
5241 if (S[i * n + e] > max) {
5242 index = e;
5243 max = S[i * n + e];
5244 }
5245 }
5246
5247 if (index > 0) {
5248 clusters.push(index);
5249 }
5250 }
5251
5252 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5253 clusters[exemplars[_ei]] = exemplars[_ei];
5254 }
5255
5256 return clusters;
5257};
5258
5259var assign$2 = function assign(n, S, exemplars) {
5260 var clusters = assignClusters(n, S, exemplars);
5261
5262 for (var ei = 0; ei < exemplars.length; ei++) {
5263 var ii = [];
5264
5265 for (var c = 0; c < clusters.length; c++) {
5266 if (clusters[c] === exemplars[ei]) {
5267 ii.push(c);
5268 }
5269 }
5270
5271 var maxI = -1;
5272 var maxSum = -Infinity;
5273
5274 for (var i = 0; i < ii.length; i++) {
5275 var sum = 0;
5276
5277 for (var j = 0; j < ii.length; j++) {
5278 sum += S[ii[j] * n + ii[i]];
5279 }
5280
5281 if (sum > maxSum) {
5282 maxI = i;
5283 maxSum = sum;
5284 }
5285 }
5286
5287 exemplars[ei] = ii[maxI];
5288 }
5289
5290 clusters = assignClusters(n, S, exemplars);
5291 return clusters;
5292};
5293
5294var affinityPropagation = function affinityPropagation(options) {
5295 var cy = this.cy();
5296 var nodes = this.nodes();
5297 var opts = setOptions$3(options); // Map each node to its position in node array
5298
5299 var id2position = {};
5300
5301 for (var i = 0; i < nodes.length; i++) {
5302 id2position[nodes[i].id()] = i;
5303 } // Begin affinity propagation algorithm
5304
5305
5306 var n; // number of data points
5307
5308 var n2; // size of matrices
5309
5310 var S; // similarity matrix (1D array)
5311
5312 var p; // preference/suitability of a data point to serve as an exemplar
5313
5314 var R; // responsibility matrix (1D array)
5315
5316 var A; // availability matrix (1D array)
5317
5318 n = nodes.length;
5319 n2 = n * n; // Initialize and build S similarity matrix
5320
5321 S = new Array(n2);
5322
5323 for (var _i = 0; _i < n2; _i++) {
5324 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5325 }
5326
5327 for (var _i2 = 0; _i2 < n; _i2++) {
5328 for (var j = 0; j < n; j++) {
5329 if (_i2 !== j) {
5330 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5331 }
5332 }
5333 } // Place preferences on the diagonal of S
5334
5335
5336 p = getPreference(S, opts.preference);
5337
5338 for (var _i3 = 0; _i3 < n; _i3++) {
5339 S[_i3 * n + _i3] = p;
5340 } // Initialize R responsibility matrix
5341
5342
5343 R = new Array(n2);
5344
5345 for (var _i4 = 0; _i4 < n2; _i4++) {
5346 R[_i4] = 0.0;
5347 } // Initialize A availability matrix
5348
5349
5350 A = new Array(n2);
5351
5352 for (var _i5 = 0; _i5 < n2; _i5++) {
5353 A[_i5] = 0.0;
5354 }
5355
5356 var old = new Array(n);
5357 var Rp = new Array(n);
5358 var se = new Array(n);
5359
5360 for (var _i6 = 0; _i6 < n; _i6++) {
5361 old[_i6] = 0.0;
5362 Rp[_i6] = 0.0;
5363 se[_i6] = 0;
5364 }
5365
5366 var e = new Array(n * opts.minIterations);
5367
5368 for (var _i7 = 0; _i7 < e.length; _i7++) {
5369 e[_i7] = 0;
5370 }
5371
5372 var iter;
5373
5374 for (iter = 0; iter < opts.maxIterations; iter++) {
5375 // main algorithmic loop
5376 // Update R responsibility matrix
5377 for (var _i8 = 0; _i8 < n; _i8++) {
5378 var max = -Infinity,
5379 max2 = -Infinity,
5380 maxI = -1,
5381 AS = 0.0;
5382
5383 for (var _j = 0; _j < n; _j++) {
5384 old[_j] = R[_i8 * n + _j];
5385 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5386
5387 if (AS >= max) {
5388 max2 = max;
5389 max = AS;
5390 maxI = _j;
5391 } else if (AS > max2) {
5392 max2 = AS;
5393 }
5394 }
5395
5396 for (var _j2 = 0; _j2 < n; _j2++) {
5397 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5398 }
5399
5400 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5401 } // Update A availability matrix
5402
5403
5404 for (var _i9 = 0; _i9 < n; _i9++) {
5405 var sum = 0;
5406
5407 for (var _j3 = 0; _j3 < n; _j3++) {
5408 old[_j3] = A[_j3 * n + _i9];
5409 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5410 sum += Rp[_j3];
5411 }
5412
5413 sum -= Rp[_i9];
5414 Rp[_i9] = R[_i9 * n + _i9];
5415 sum += Rp[_i9];
5416
5417 for (var _j4 = 0; _j4 < n; _j4++) {
5418 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5419 }
5420
5421 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5422 } // Check for convergence
5423
5424
5425 var K = 0;
5426
5427 for (var _i10 = 0; _i10 < n; _i10++) {
5428 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5429 e[iter % opts.minIterations * n + _i10] = E;
5430 K += E;
5431 }
5432
5433 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5434 var _sum = 0;
5435
5436 for (var _i11 = 0; _i11 < n; _i11++) {
5437 se[_i11] = 0;
5438
5439 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5440 se[_i11] += e[_j5 * n + _i11];
5441 }
5442
5443 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5444 _sum++;
5445 }
5446 }
5447
5448 if (_sum === n) {
5449 // then we have convergence
5450 break;
5451 }
5452 }
5453 } // Identify exemplars (cluster centers)
5454
5455
5456 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5457
5458 var clusterIndices = assign$2(n, S, exemplarsIndices);
5459 var clusters = {};
5460
5461 for (var c = 0; c < exemplarsIndices.length; c++) {
5462 clusters[exemplarsIndices[c]] = [];
5463 }
5464
5465 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5466 var pos = id2position[nodes[_i12].id()];
5467
5468 var clusterIndex = clusterIndices[pos];
5469
5470 if (clusterIndex != null) {
5471 // the node may have not been assigned a cluster if no valid attributes were specified
5472 clusters[clusterIndex].push(nodes[_i12]);
5473 }
5474 }
5475
5476 var retClusters = new Array(exemplarsIndices.length);
5477
5478 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5479 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5480 }
5481
5482 return retClusters;
5483};
5484
5485var affinityPropagation$1 = {
5486 affinityPropagation: affinityPropagation,
5487 ap: affinityPropagation
5488};
5489
5490var hierholzerDefaults = defaults({
5491 root: undefined,
5492 directed: false
5493});
5494var elesfn$b = {
5495 hierholzer: function hierholzer(options) {
5496 if (!plainObject(options)) {
5497 var args = arguments;
5498 options = {
5499 root: args[0],
5500 directed: args[1]
5501 };
5502 }
5503
5504 var _hierholzerDefaults = hierholzerDefaults(options),
5505 root = _hierholzerDefaults.root,
5506 directed = _hierholzerDefaults.directed;
5507
5508 var eles = this;
5509 var dflag = false;
5510 var oddIn;
5511 var oddOut;
5512 var startVertex;
5513 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5514 var nodes = {};
5515 var edges = {};
5516
5517 if (directed) {
5518 eles.forEach(function (ele) {
5519 var id = ele.id();
5520
5521 if (ele.isNode()) {
5522 var ind = ele.indegree(true);
5523 var outd = ele.outdegree(true);
5524 var d1 = ind - outd;
5525 var d2 = outd - ind;
5526
5527 if (d1 == 1) {
5528 if (oddIn) dflag = true;else oddIn = id;
5529 } else if (d2 == 1) {
5530 if (oddOut) dflag = true;else oddOut = id;
5531 } else if (d2 > 1 || d1 > 1) {
5532 dflag = true;
5533 }
5534
5535 nodes[id] = [];
5536 ele.outgoers().forEach(function (e) {
5537 if (e.isEdge()) nodes[id].push(e.id());
5538 });
5539 } else {
5540 edges[id] = [undefined, ele.target().id()];
5541 }
5542 });
5543 } else {
5544 eles.forEach(function (ele) {
5545 var id = ele.id();
5546
5547 if (ele.isNode()) {
5548 var d = ele.degree(true);
5549
5550 if (d % 2) {
5551 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5552 }
5553
5554 nodes[id] = [];
5555 ele.connectedEdges().forEach(function (e) {
5556 return nodes[id].push(e.id());
5557 });
5558 } else {
5559 edges[id] = [ele.source().id(), ele.target().id()];
5560 }
5561 });
5562 }
5563
5564 var result = {
5565 found: false,
5566 trail: undefined
5567 };
5568 if (dflag) return result;else if (oddOut && oddIn) {
5569 if (directed) {
5570 if (startVertex && oddOut != startVertex) {
5571 return result;
5572 }
5573
5574 startVertex = oddOut;
5575 } else {
5576 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5577 return result;
5578 } else if (!startVertex) {
5579 startVertex = oddOut;
5580 }
5581 }
5582 } else {
5583 if (!startVertex) startVertex = eles[0].id();
5584 }
5585
5586 var walk = function walk(v) {
5587 var currentNode = v;
5588 var subtour = [v];
5589 var adj, adjTail, adjHead;
5590
5591 while (nodes[currentNode].length) {
5592 adj = nodes[currentNode].shift();
5593 adjTail = edges[adj][0];
5594 adjHead = edges[adj][1];
5595
5596 if (currentNode != adjHead) {
5597 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5598 return e != adj;
5599 });
5600 currentNode = adjHead;
5601 } else if (!directed && currentNode != adjTail) {
5602 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5603 return e != adj;
5604 });
5605 currentNode = adjTail;
5606 }
5607
5608 subtour.unshift(adj);
5609 subtour.unshift(currentNode);
5610 }
5611
5612 return subtour;
5613 };
5614
5615 var trail = [];
5616 var subtour = [];
5617 subtour = walk(startVertex);
5618
5619 while (subtour.length != 1) {
5620 if (nodes[subtour[0]].length == 0) {
5621 trail.unshift(eles.getElementById(subtour.shift()));
5622 trail.unshift(eles.getElementById(subtour.shift()));
5623 } else {
5624 subtour = walk(subtour.shift()).concat(subtour);
5625 }
5626 }
5627
5628 trail.unshift(eles.getElementById(subtour.shift())); // final node
5629
5630 for (var d in nodes) {
5631 if (nodes[d].length) {
5632 return result;
5633 }
5634 }
5635
5636 result.found = true;
5637 result.trail = this.spawn(trail, true);
5638 return result;
5639 }
5640};
5641
5642var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5643 var eles = this;
5644 var nodes = {};
5645 var id = 0;
5646 var edgeCount = 0;
5647 var components = [];
5648 var stack = [];
5649 var visitedEdges = {};
5650
5651 var buildComponent = function buildComponent(x, y) {
5652 var i = stack.length - 1;
5653 var cutset = [];
5654 var component = eles.spawn();
5655
5656 while (stack[i].x != x || stack[i].y != y) {
5657 cutset.push(stack.pop().edge);
5658 i--;
5659 }
5660
5661 cutset.push(stack.pop().edge);
5662 cutset.forEach(function (edge) {
5663 var connectedNodes = edge.connectedNodes().intersection(eles);
5664 component.merge(edge);
5665 connectedNodes.forEach(function (node) {
5666 var nodeId = node.id();
5667 var connectedEdges = node.connectedEdges().intersection(eles);
5668 component.merge(node);
5669
5670 if (!nodes[nodeId].cutVertex) {
5671 component.merge(connectedEdges);
5672 } else {
5673 component.merge(connectedEdges.filter(function (edge) {
5674 return edge.isLoop();
5675 }));
5676 }
5677 });
5678 });
5679 components.push(component);
5680 };
5681
5682 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5683 if (root === parent) edgeCount += 1;
5684 nodes[currentNode] = {
5685 id: id,
5686 low: id++,
5687 cutVertex: false
5688 };
5689 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5690
5691 if (edges.size() === 0) {
5692 components.push(eles.spawn(eles.getElementById(currentNode)));
5693 } else {
5694 var sourceId, targetId, otherNodeId, edgeId;
5695 edges.forEach(function (edge) {
5696 sourceId = edge.source().id();
5697 targetId = edge.target().id();
5698 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5699
5700 if (otherNodeId !== parent) {
5701 edgeId = edge.id();
5702
5703 if (!visitedEdges[edgeId]) {
5704 visitedEdges[edgeId] = true;
5705 stack.push({
5706 x: currentNode,
5707 y: otherNodeId,
5708 edge: edge
5709 });
5710 }
5711
5712 if (!(otherNodeId in nodes)) {
5713 biconnectedSearch(root, otherNodeId, currentNode);
5714 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5715
5716 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5717 nodes[currentNode].cutVertex = true;
5718 buildComponent(currentNode, otherNodeId);
5719 }
5720 } else {
5721 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5722 }
5723 }
5724 });
5725 }
5726 };
5727
5728 eles.forEach(function (ele) {
5729 if (ele.isNode()) {
5730 var nodeId = ele.id();
5731
5732 if (!(nodeId in nodes)) {
5733 edgeCount = 0;
5734 biconnectedSearch(nodeId, nodeId);
5735 nodes[nodeId].cutVertex = edgeCount > 1;
5736 }
5737 }
5738 });
5739 var cutVertices = Object.keys(nodes).filter(function (id) {
5740 return nodes[id].cutVertex;
5741 }).map(function (id) {
5742 return eles.getElementById(id);
5743 });
5744 return {
5745 cut: eles.spawn(cutVertices),
5746 components: components
5747 };
5748};
5749
5750var hopcroftTarjanBiconnected$1 = {
5751 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5752 htbc: hopcroftTarjanBiconnected,
5753 htb: hopcroftTarjanBiconnected,
5754 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5755};
5756
5757var tarjanStronglyConnected = function tarjanStronglyConnected() {
5758 var eles = this;
5759 var nodes = {};
5760 var index = 0;
5761 var components = [];
5762 var stack = [];
5763 var cut = eles.spawn(eles);
5764
5765 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5766 stack.push(sourceNodeId);
5767 nodes[sourceNodeId] = {
5768 index: index,
5769 low: index++,
5770 explored: false
5771 };
5772 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5773 connectedEdges.forEach(function (edge) {
5774 var targetNodeId = edge.target().id();
5775
5776 if (targetNodeId !== sourceNodeId) {
5777 if (!(targetNodeId in nodes)) {
5778 stronglyConnectedSearch(targetNodeId);
5779 }
5780
5781 if (!nodes[targetNodeId].explored) {
5782 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5783 }
5784 }
5785 });
5786
5787 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5788 var componentNodes = eles.spawn();
5789
5790 for (;;) {
5791 var nodeId = stack.pop();
5792 componentNodes.merge(eles.getElementById(nodeId));
5793 nodes[nodeId].low = nodes[sourceNodeId].index;
5794 nodes[nodeId].explored = true;
5795
5796 if (nodeId === sourceNodeId) {
5797 break;
5798 }
5799 }
5800
5801 var componentEdges = componentNodes.edgesWith(componentNodes);
5802 var component = componentNodes.merge(componentEdges);
5803 components.push(component);
5804 cut = cut.difference(component);
5805 }
5806 };
5807
5808 eles.forEach(function (ele) {
5809 if (ele.isNode()) {
5810 var nodeId = ele.id();
5811
5812 if (!(nodeId in nodes)) {
5813 stronglyConnectedSearch(nodeId);
5814 }
5815 }
5816 });
5817 return {
5818 cut: cut,
5819 components: components
5820 };
5821};
5822
5823var tarjanStronglyConnected$1 = {
5824 tarjanStronglyConnected: tarjanStronglyConnected,
5825 tsc: tarjanStronglyConnected,
5826 tscc: tarjanStronglyConnected,
5827 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5828};
5829
5830var elesfn$c = {};
5831[elesfn, elesfn$1, elesfn$2, elesfn$3, elesfn$4, elesfn$5, elesfn$6, elesfn$7, elesfn$8, elesfn$9, elesfn$a, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$b, hopcroftTarjanBiconnected$1, tarjanStronglyConnected$1].forEach(function (props) {
5832 extend(elesfn$c, props);
5833});
5834
5835/*!
5836Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5837Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5838Licensed under The MIT License (http://opensource.org/licenses/MIT)
5839*/
5840
5841/* promise states [Promises/A+ 2.1] */
5842var STATE_PENDING = 0;
5843/* [Promises/A+ 2.1.1] */
5844
5845var STATE_FULFILLED = 1;
5846/* [Promises/A+ 2.1.2] */
5847
5848var STATE_REJECTED = 2;
5849/* [Promises/A+ 2.1.3] */
5850
5851/* promise object constructor */
5852
5853var api = function api(executor) {
5854 /* optionally support non-constructor/plain-function call */
5855 if (!(this instanceof api)) return new api(executor);
5856 /* initialize object */
5857
5858 this.id = 'Thenable/1.0.7';
5859 this.state = STATE_PENDING;
5860 /* initial state */
5861
5862 this.fulfillValue = undefined;
5863 /* initial value */
5864
5865 /* [Promises/A+ 1.3, 2.1.2.2] */
5866
5867 this.rejectReason = undefined;
5868 /* initial reason */
5869
5870 /* [Promises/A+ 1.5, 2.1.3.2] */
5871
5872 this.onFulfilled = [];
5873 /* initial handlers */
5874
5875 this.onRejected = [];
5876 /* initial handlers */
5877
5878 /* provide optional information-hiding proxy */
5879
5880 this.proxy = {
5881 then: this.then.bind(this)
5882 };
5883 /* support optional executor function */
5884
5885 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5886};
5887/* promise API methods */
5888
5889
5890api.prototype = {
5891 /* promise resolving methods */
5892 fulfill: function fulfill(value) {
5893 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5894 },
5895 reject: function reject(value) {
5896 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5897 },
5898
5899 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5900 then: function then(onFulfilled, onRejected) {
5901 var curr = this;
5902 var next = new api();
5903 /* [Promises/A+ 2.2.7] */
5904
5905 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5906 /* [Promises/A+ 2.2.2/2.2.6] */
5907
5908 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5909 /* [Promises/A+ 2.2.3/2.2.6] */
5910
5911 execute(curr);
5912 return next.proxy;
5913 /* [Promises/A+ 2.2.7, 3.3] */
5914 }
5915};
5916/* deliver an action */
5917
5918var deliver = function deliver(curr, state, name, value) {
5919 if (curr.state === STATE_PENDING) {
5920 curr.state = state;
5921 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5922
5923 curr[name] = value;
5924 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5925
5926 execute(curr);
5927 }
5928
5929 return curr;
5930};
5931/* execute all handlers */
5932
5933
5934var execute = function execute(curr) {
5935 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5936};
5937/* execute particular set of handlers */
5938
5939
5940var execute_handlers = function execute_handlers(curr, name, value) {
5941 /* global setImmediate: true */
5942
5943 /* global setTimeout: true */
5944
5945 /* short-circuit processing */
5946 if (curr[name].length === 0) return;
5947 /* iterate over all handlers, exactly once */
5948
5949 var handlers = curr[name];
5950 curr[name] = [];
5951 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5952
5953 var func = function func() {
5954 for (var i = 0; i < handlers.length; i++) {
5955 handlers[i](value);
5956 }
5957 /* [Promises/A+ 2.2.5] */
5958
5959 };
5960 /* execute procedure asynchronously */
5961
5962 /* [Promises/A+ 2.2.4, 3.1] */
5963
5964
5965 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5966};
5967/* generate a resolver function */
5968
5969
5970var resolver = function resolver(cb, next, method) {
5971 return function (value) {
5972 if (typeof cb !== 'function')
5973 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5974 next[method].call(next, value);
5975 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5976 else {
5977 var result;
5978
5979 try {
5980 result = cb(value);
5981 }
5982 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5983 catch (e) {
5984 next.reject(e);
5985 /* [Promises/A+ 2.2.7.2] */
5986
5987 return;
5988 }
5989
5990 resolve(next, result);
5991 /* [Promises/A+ 2.2.7.1] */
5992 }
5993 };
5994};
5995/* "Promise Resolution Procedure" */
5996
5997/* [Promises/A+ 2.3] */
5998
5999
6000var resolve = function resolve(promise, x) {
6001 /* sanity check arguments */
6002
6003 /* [Promises/A+ 2.3.1] */
6004 if (promise === x || promise.proxy === x) {
6005 promise.reject(new TypeError('cannot resolve promise with itself'));
6006 return;
6007 }
6008 /* surgically check for a "then" method
6009 (mainly to just call the "getter" of "then" only once) */
6010
6011
6012 var then;
6013
6014 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6015 try {
6016 then = x.then;
6017 }
6018 /* [Promises/A+ 2.3.3.1, 3.5] */
6019 catch (e) {
6020 promise.reject(e);
6021 /* [Promises/A+ 2.3.3.2] */
6022
6023 return;
6024 }
6025 }
6026 /* handle own Thenables [Promises/A+ 2.3.2]
6027 and similar "thenables" [Promises/A+ 2.3.3] */
6028
6029
6030 if (typeof then === 'function') {
6031 var resolved = false;
6032
6033 try {
6034 /* call retrieved "then" method */
6035
6036 /* [Promises/A+ 2.3.3.3] */
6037 then.call(x,
6038 /* resolvePromise */
6039
6040 /* [Promises/A+ 2.3.3.3.1] */
6041 function (y) {
6042 if (resolved) return;
6043 resolved = true;
6044 /* [Promises/A+ 2.3.3.3.3] */
6045
6046 if (y === x)
6047 /* [Promises/A+ 3.6] */
6048 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6049 },
6050 /* rejectPromise */
6051
6052 /* [Promises/A+ 2.3.3.3.2] */
6053 function (r) {
6054 if (resolved) return;
6055 resolved = true;
6056 /* [Promises/A+ 2.3.3.3.3] */
6057
6058 promise.reject(r);
6059 });
6060 } catch (e) {
6061 if (!resolved)
6062 /* [Promises/A+ 2.3.3.3.3] */
6063 promise.reject(e);
6064 /* [Promises/A+ 2.3.3.3.4] */
6065 }
6066
6067 return;
6068 }
6069 /* handle other values */
6070
6071
6072 promise.fulfill(x);
6073 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6074}; // so we always have Promise.all()
6075
6076
6077api.all = function (ps) {
6078 return new api(function (resolveAll, rejectAll) {
6079 var vals = new Array(ps.length);
6080 var doneCount = 0;
6081
6082 var fulfill = function fulfill(i, val) {
6083 vals[i] = val;
6084 doneCount++;
6085
6086 if (doneCount === ps.length) {
6087 resolveAll(vals);
6088 }
6089 };
6090
6091 for (var i = 0; i < ps.length; i++) {
6092 (function (i) {
6093 var p = ps[i];
6094 var isPromise = p != null && p.then != null;
6095
6096 if (isPromise) {
6097 p.then(function (val) {
6098 fulfill(i, val);
6099 }, function (err) {
6100 rejectAll(err);
6101 });
6102 } else {
6103 var val = p;
6104 fulfill(i, val);
6105 }
6106 })(i);
6107 }
6108 });
6109};
6110
6111api.resolve = function (val) {
6112 return new api(function (resolve, reject) {
6113 resolve(val);
6114 });
6115};
6116
6117api.reject = function (val) {
6118 return new api(function (resolve, reject) {
6119 reject(val);
6120 });
6121};
6122
6123var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6124
6125var Animation = function Animation(target, opts, opts2) {
6126 var isCore = core(target);
6127 var isEle = !isCore;
6128
6129 var _p = this._private = extend({
6130 duration: 1000
6131 }, opts, opts2);
6132
6133 _p.target = target;
6134 _p.style = _p.style || _p.css;
6135 _p.started = false;
6136 _p.playing = false;
6137 _p.hooked = false;
6138 _p.applying = false;
6139 _p.progress = 0;
6140 _p.completes = [];
6141 _p.frames = [];
6142
6143 if (_p.complete && fn(_p.complete)) {
6144 _p.completes.push(_p.complete);
6145 }
6146
6147 if (isEle) {
6148 var pos = target.position();
6149 _p.startPosition = _p.startPosition || {
6150 x: pos.x,
6151 y: pos.y
6152 };
6153 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6154 }
6155
6156 if (isCore) {
6157 var pan = target.pan();
6158 _p.startPan = {
6159 x: pan.x,
6160 y: pan.y
6161 };
6162 _p.startZoom = target.zoom();
6163 } // for future timeline/animations impl
6164
6165
6166 this.length = 1;
6167 this[0] = this;
6168};
6169
6170var anifn = Animation.prototype;
6171extend(anifn, {
6172 instanceString: function instanceString() {
6173 return 'animation';
6174 },
6175 hook: function hook() {
6176 var _p = this._private;
6177
6178 if (!_p.hooked) {
6179 // add to target's animation queue
6180 var q;
6181 var tAni = _p.target._private.animation;
6182
6183 if (_p.queue) {
6184 q = tAni.queue;
6185 } else {
6186 q = tAni.current;
6187 }
6188
6189 q.push(this); // add to the animation loop pool
6190
6191 if (elementOrCollection(_p.target)) {
6192 _p.target.cy().addToAnimationPool(_p.target);
6193 }
6194
6195 _p.hooked = true;
6196 }
6197
6198 return this;
6199 },
6200 play: function play() {
6201 var _p = this._private; // autorewind
6202
6203 if (_p.progress === 1) {
6204 _p.progress = 0;
6205 }
6206
6207 _p.playing = true;
6208 _p.started = false; // needs to be started by animation loop
6209
6210 _p.stopped = false;
6211 this.hook(); // the animation loop will start the animation...
6212
6213 return this;
6214 },
6215 playing: function playing() {
6216 return this._private.playing;
6217 },
6218 apply: function apply() {
6219 var _p = this._private;
6220 _p.applying = true;
6221 _p.started = false; // needs to be started by animation loop
6222
6223 _p.stopped = false;
6224 this.hook(); // the animation loop will apply the animation at this progress
6225
6226 return this;
6227 },
6228 applying: function applying() {
6229 return this._private.applying;
6230 },
6231 pause: function pause() {
6232 var _p = this._private;
6233 _p.playing = false;
6234 _p.started = false;
6235 return this;
6236 },
6237 stop: function stop() {
6238 var _p = this._private;
6239 _p.playing = false;
6240 _p.started = false;
6241 _p.stopped = true; // to be removed from animation queues
6242
6243 return this;
6244 },
6245 rewind: function rewind() {
6246 return this.progress(0);
6247 },
6248 fastforward: function fastforward() {
6249 return this.progress(1);
6250 },
6251 time: function time(t) {
6252 var _p = this._private;
6253
6254 if (t === undefined) {
6255 return _p.progress * _p.duration;
6256 } else {
6257 return this.progress(t / _p.duration);
6258 }
6259 },
6260 progress: function progress(p) {
6261 var _p = this._private;
6262 var wasPlaying = _p.playing;
6263
6264 if (p === undefined) {
6265 return _p.progress;
6266 } else {
6267 if (wasPlaying) {
6268 this.pause();
6269 }
6270
6271 _p.progress = p;
6272 _p.started = false;
6273
6274 if (wasPlaying) {
6275 this.play();
6276 }
6277 }
6278
6279 return this;
6280 },
6281 completed: function completed() {
6282 return this._private.progress === 1;
6283 },
6284 reverse: function reverse() {
6285 var _p = this._private;
6286 var wasPlaying = _p.playing;
6287
6288 if (wasPlaying) {
6289 this.pause();
6290 }
6291
6292 _p.progress = 1 - _p.progress;
6293 _p.started = false;
6294
6295 var swap = function swap(a, b) {
6296 var _pa = _p[a];
6297
6298 if (_pa == null) {
6299 return;
6300 }
6301
6302 _p[a] = _p[b];
6303 _p[b] = _pa;
6304 };
6305
6306 swap('zoom', 'startZoom');
6307 swap('pan', 'startPan');
6308 swap('position', 'startPosition'); // swap styles
6309
6310 if (_p.style) {
6311 for (var i = 0; i < _p.style.length; i++) {
6312 var prop = _p.style[i];
6313 var name = prop.name;
6314 var startStyleProp = _p.startStyle[name];
6315 _p.startStyle[name] = prop;
6316 _p.style[i] = startStyleProp;
6317 }
6318 }
6319
6320 if (wasPlaying) {
6321 this.play();
6322 }
6323
6324 return this;
6325 },
6326 promise: function promise(type) {
6327 var _p = this._private;
6328 var arr;
6329
6330 switch (type) {
6331 case 'frame':
6332 arr = _p.frames;
6333 break;
6334
6335 default:
6336 case 'complete':
6337 case 'completed':
6338 arr = _p.completes;
6339 }
6340
6341 return new Promise$1(function (resolve, reject) {
6342 arr.push(function () {
6343 resolve();
6344 });
6345 });
6346 }
6347});
6348anifn.complete = anifn.completed;
6349anifn.run = anifn.play;
6350anifn.running = anifn.playing;
6351
6352var define = {
6353 animated: function animated() {
6354 return function animatedImpl() {
6355 var self = this;
6356 var selfIsArrayLike = self.length !== undefined;
6357 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6358
6359 var cy = this._private.cy || this;
6360
6361 if (!cy.styleEnabled()) {
6362 return false;
6363 }
6364
6365 var ele = all[0];
6366
6367 if (ele) {
6368 return ele._private.animation.current.length > 0;
6369 }
6370 };
6371 },
6372 // animated
6373 clearQueue: function clearQueue() {
6374 return function clearQueueImpl() {
6375 var self = this;
6376 var selfIsArrayLike = self.length !== undefined;
6377 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6378
6379 var cy = this._private.cy || this;
6380
6381 if (!cy.styleEnabled()) {
6382 return this;
6383 }
6384
6385 for (var i = 0; i < all.length; i++) {
6386 var ele = all[i];
6387 ele._private.animation.queue = [];
6388 }
6389
6390 return this;
6391 };
6392 },
6393 // clearQueue
6394 delay: function delay() {
6395 return function delayImpl(time, complete) {
6396 var cy = this._private.cy || this;
6397
6398 if (!cy.styleEnabled()) {
6399 return this;
6400 }
6401
6402 return this.animate({
6403 delay: time,
6404 duration: time,
6405 complete: complete
6406 });
6407 };
6408 },
6409 // delay
6410 delayAnimation: function delayAnimation() {
6411 return function delayAnimationImpl(time, complete) {
6412 var cy = this._private.cy || this;
6413
6414 if (!cy.styleEnabled()) {
6415 return this;
6416 }
6417
6418 return this.animation({
6419 delay: time,
6420 duration: time,
6421 complete: complete
6422 });
6423 };
6424 },
6425 // delay
6426 animation: function animation() {
6427 return function animationImpl(properties, params) {
6428 var self = this;
6429 var selfIsArrayLike = self.length !== undefined;
6430 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6431
6432 var cy = this._private.cy || this;
6433 var isCore = !selfIsArrayLike;
6434 var isEles = !isCore;
6435
6436 if (!cy.styleEnabled()) {
6437 return this;
6438 }
6439
6440 var style = cy.style();
6441 properties = extend({}, properties, params);
6442 var propertiesEmpty = Object.keys(properties).length === 0;
6443
6444 if (propertiesEmpty) {
6445 return new Animation(all[0], properties); // nothing to animate
6446 }
6447
6448 if (properties.duration === undefined) {
6449 properties.duration = 400;
6450 }
6451
6452 switch (properties.duration) {
6453 case 'slow':
6454 properties.duration = 600;
6455 break;
6456
6457 case 'fast':
6458 properties.duration = 200;
6459 break;
6460 }
6461
6462 if (isEles) {
6463 properties.style = style.getPropsList(properties.style || properties.css);
6464 properties.css = undefined;
6465 }
6466
6467 if (isEles && properties.renderedPosition != null) {
6468 var rpos = properties.renderedPosition;
6469 var pan = cy.pan();
6470 var zoom = cy.zoom();
6471 properties.position = renderedToModelPosition(rpos, zoom, pan);
6472 } // override pan w/ panBy if set
6473
6474
6475 if (isCore && properties.panBy != null) {
6476 var panBy = properties.panBy;
6477 var cyPan = cy.pan();
6478 properties.pan = {
6479 x: cyPan.x + panBy.x,
6480 y: cyPan.y + panBy.y
6481 };
6482 } // override pan w/ center if set
6483
6484
6485 var center = properties.center || properties.centre;
6486
6487 if (isCore && center != null) {
6488 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6489
6490 if (centerPan != null) {
6491 properties.pan = centerPan;
6492 }
6493 } // override pan & zoom w/ fit if set
6494
6495
6496 if (isCore && properties.fit != null) {
6497 var fit = properties.fit;
6498 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6499
6500 if (fitVp != null) {
6501 properties.pan = fitVp.pan;
6502 properties.zoom = fitVp.zoom;
6503 }
6504 } // override zoom (& potentially pan) w/ zoom obj if set
6505
6506
6507 if (isCore && plainObject(properties.zoom)) {
6508 var vp = cy.getZoomedViewport(properties.zoom);
6509
6510 if (vp != null) {
6511 if (vp.zoomed) {
6512 properties.zoom = vp.zoom;
6513 }
6514
6515 if (vp.panned) {
6516 properties.pan = vp.pan;
6517 }
6518 } else {
6519 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6520 }
6521 }
6522
6523 return new Animation(all[0], properties);
6524 };
6525 },
6526 // animate
6527 animate: function animate() {
6528 return function animateImpl(properties, params) {
6529 var self = this;
6530 var selfIsArrayLike = self.length !== undefined;
6531 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6532
6533 var cy = this._private.cy || this;
6534
6535 if (!cy.styleEnabled()) {
6536 return this;
6537 }
6538
6539 if (params) {
6540 properties = extend({}, properties, params);
6541 } // manually hook and run the animation
6542
6543
6544 for (var i = 0; i < all.length; i++) {
6545 var ele = all[i];
6546 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6547 var ani = ele.animation(properties, queue ? {
6548 queue: true
6549 } : undefined);
6550 ani.play();
6551 }
6552
6553 return this; // chaining
6554 };
6555 },
6556 // animate
6557 stop: function stop() {
6558 return function stopImpl(clearQueue, jumpToEnd) {
6559 var self = this;
6560 var selfIsArrayLike = self.length !== undefined;
6561 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6562
6563 var cy = this._private.cy || this;
6564
6565 if (!cy.styleEnabled()) {
6566 return this;
6567 }
6568
6569 for (var i = 0; i < all.length; i++) {
6570 var ele = all[i];
6571 var _p = ele._private;
6572 var anis = _p.animation.current;
6573
6574 for (var j = 0; j < anis.length; j++) {
6575 var ani = anis[j];
6576 var ani_p = ani._private;
6577
6578 if (jumpToEnd) {
6579 // next iteration of the animation loop, the animation
6580 // will go straight to the end and be removed
6581 ani_p.duration = 0;
6582 }
6583 } // clear the queue of future animations
6584
6585
6586 if (clearQueue) {
6587 _p.animation.queue = [];
6588 }
6589
6590 if (!jumpToEnd) {
6591 _p.animation.current = [];
6592 }
6593 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6594
6595
6596 cy.notify('draw');
6597 return this;
6598 };
6599 } // stop
6600
6601}; // define
6602
6603var define$1 = {
6604 // access data field
6605 data: function data(params) {
6606 var defaults = {
6607 field: 'data',
6608 bindingEvent: 'data',
6609 allowBinding: false,
6610 allowSetting: false,
6611 allowGetting: false,
6612 settingEvent: 'data',
6613 settingTriggersEvent: false,
6614 triggerFnName: 'trigger',
6615 immutableKeys: {},
6616 // key => true if immutable
6617 updateStyle: false,
6618 beforeGet: function beforeGet(self) {},
6619 beforeSet: function beforeSet(self, obj) {},
6620 onSet: function onSet(self) {},
6621 canSet: function canSet(self) {
6622 return true;
6623 }
6624 };
6625 params = extend({}, defaults, params);
6626 return function dataImpl(name, value) {
6627 var p = params;
6628 var self = this;
6629 var selfIsArrayLike = self.length !== undefined;
6630 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6631
6632 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6633
6634 if (string(name)) {
6635 // set or get property
6636 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
6637
6638 var path = isPathLike && toPath(name); // .data('foo')
6639
6640 if (p.allowGetting && value === undefined) {
6641 // get
6642 var ret;
6643
6644 if (single) {
6645 p.beforeGet(single); // check if it's path and a field with the same name doesn't exist
6646
6647 if (path && single._private[p.field][name] === undefined) {
6648 ret = get(single._private[p.field], path);
6649 } else {
6650 ret = single._private[p.field][name];
6651 }
6652 }
6653
6654 return ret; // .data('foo', 'bar')
6655 } else if (p.allowSetting && value !== undefined) {
6656 // set
6657 var valid = !p.immutableKeys[name];
6658
6659 if (valid) {
6660 var change = _defineProperty({}, name, value);
6661
6662 p.beforeSet(self, change);
6663
6664 for (var i = 0, l = all.length; i < l; i++) {
6665 var ele = all[i];
6666
6667 if (p.canSet(ele)) {
6668 if (path && single._private[p.field][name] === undefined) {
6669 set(ele._private[p.field], path, value);
6670 } else {
6671 ele._private[p.field][name] = value;
6672 }
6673 }
6674 } // update mappers if asked
6675
6676
6677 if (p.updateStyle) {
6678 self.updateStyle();
6679 } // call onSet callback
6680
6681
6682 p.onSet(self);
6683
6684 if (p.settingTriggersEvent) {
6685 self[p.triggerFnName](p.settingEvent);
6686 }
6687 }
6688 } // .data({ 'foo': 'bar' })
6689
6690 } else if (p.allowSetting && plainObject(name)) {
6691 // extend
6692 var obj = name;
6693 var k, v;
6694 var keys = Object.keys(obj);
6695 p.beforeSet(self, obj);
6696
6697 for (var _i = 0; _i < keys.length; _i++) {
6698 k = keys[_i];
6699 v = obj[k];
6700
6701 var _valid = !p.immutableKeys[k];
6702
6703 if (_valid) {
6704 for (var j = 0; j < all.length; j++) {
6705 var _ele = all[j];
6706
6707 if (p.canSet(_ele)) {
6708 _ele._private[p.field][k] = v;
6709 }
6710 }
6711 }
6712 } // update mappers if asked
6713
6714
6715 if (p.updateStyle) {
6716 self.updateStyle();
6717 } // call onSet callback
6718
6719
6720 p.onSet(self);
6721
6722 if (p.settingTriggersEvent) {
6723 self[p.triggerFnName](p.settingEvent);
6724 } // .data(function(){ ... })
6725
6726 } else if (p.allowBinding && fn(name)) {
6727 // bind to event
6728 var fn$1 = name;
6729 self.on(p.bindingEvent, fn$1); // .data()
6730 } else if (p.allowGetting && name === undefined) {
6731 // get whole object
6732 var _ret;
6733
6734 if (single) {
6735 p.beforeGet(single);
6736 _ret = single._private[p.field];
6737 }
6738
6739 return _ret;
6740 }
6741
6742 return self; // maintain chainability
6743 }; // function
6744 },
6745 // data
6746 // remove data field
6747 removeData: function removeData(params) {
6748 var defaults = {
6749 field: 'data',
6750 event: 'data',
6751 triggerFnName: 'trigger',
6752 triggerEvent: false,
6753 immutableKeys: {} // key => true if immutable
6754
6755 };
6756 params = extend({}, defaults, params);
6757 return function removeDataImpl(names) {
6758 var p = params;
6759 var self = this;
6760 var selfIsArrayLike = self.length !== undefined;
6761 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6762 // .removeData('foo bar')
6763
6764 if (string(names)) {
6765 // then get the list of keys, and delete them
6766 var keys = names.split(/\s+/);
6767 var l = keys.length;
6768
6769 for (var i = 0; i < l; i++) {
6770 // delete each non-empty key
6771 var key = keys[i];
6772
6773 if (emptyString(key)) {
6774 continue;
6775 }
6776
6777 var valid = !p.immutableKeys[key]; // not valid if immutable
6778
6779 if (valid) {
6780 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6781 all[i_a]._private[p.field][key] = undefined;
6782 }
6783 }
6784 }
6785
6786 if (p.triggerEvent) {
6787 self[p.triggerFnName](p.event);
6788 } // .removeData()
6789
6790 } else if (names === undefined) {
6791 // then delete all keys
6792 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6793 var _privateFields = all[_i_a]._private[p.field];
6794
6795 var _keys = Object.keys(_privateFields);
6796
6797 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6798 var _key = _keys[_i2];
6799 var validKeyToDelete = !p.immutableKeys[_key];
6800
6801 if (validKeyToDelete) {
6802 _privateFields[_key] = undefined;
6803 }
6804 }
6805 }
6806
6807 if (p.triggerEvent) {
6808 self[p.triggerFnName](p.event);
6809 }
6810 }
6811
6812 return self; // maintain chaining
6813 }; // function
6814 } // removeData
6815
6816}; // define
6817
6818var define$2 = {
6819 eventAliasesOn: function eventAliasesOn(proto) {
6820 var p = proto;
6821 p.addListener = p.listen = p.bind = p.on;
6822 p.unlisten = p.unbind = p.off = p.removeListener;
6823 p.trigger = p.emit; // this is just a wrapper alias of .on()
6824
6825 p.pon = p.promiseOn = function (events, selector) {
6826 var self = this;
6827 var args = Array.prototype.slice.call(arguments, 0);
6828 return new Promise$1(function (resolve, reject) {
6829 var callback = function callback(e) {
6830 self.off.apply(self, offArgs);
6831 resolve(e);
6832 };
6833
6834 var onArgs = args.concat([callback]);
6835 var offArgs = onArgs.concat([]);
6836 self.on.apply(self, onArgs);
6837 });
6838 };
6839 }
6840}; // define
6841
6842// use this module to cherry pick functions into your prototype
6843var define$3 = {};
6844[define, define$1, define$2].forEach(function (m) {
6845 extend(define$3, m);
6846});
6847
6848var elesfn$d = {
6849 animate: define$3.animate(),
6850 animation: define$3.animation(),
6851 animated: define$3.animated(),
6852 clearQueue: define$3.clearQueue(),
6853 delay: define$3.delay(),
6854 delayAnimation: define$3.delayAnimation(),
6855 stop: define$3.stop()
6856};
6857
6858var elesfn$e = {
6859 classes: function classes(_classes) {
6860 var self = this;
6861
6862 if (_classes === undefined) {
6863 var ret = [];
6864
6865 self[0]._private.classes.forEach(function (cls) {
6866 return ret.push(cls);
6867 });
6868
6869 return ret;
6870 } else if (!array(_classes)) {
6871 // extract classes from string
6872 _classes = (_classes || '').match(/\S+/g) || [];
6873 }
6874
6875 var changed = [];
6876 var classesSet = new Set$1(_classes); // check and update each ele
6877
6878 for (var j = 0; j < self.length; j++) {
6879 var ele = self[j];
6880 var _p = ele._private;
6881 var eleClasses = _p.classes;
6882 var changedEle = false; // check if ele has all of the passed classes
6883
6884 for (var i = 0; i < _classes.length; i++) {
6885 var cls = _classes[i];
6886 var eleHasClass = eleClasses.has(cls);
6887
6888 if (!eleHasClass) {
6889 changedEle = true;
6890 break;
6891 }
6892 } // check if ele has classes outside of those passed
6893
6894
6895 if (!changedEle) {
6896 changedEle = eleClasses.size !== _classes.length;
6897 }
6898
6899 if (changedEle) {
6900 _p.classes = classesSet;
6901 changed.push(ele);
6902 }
6903 } // trigger update style on those eles that had class changes
6904
6905
6906 if (changed.length > 0) {
6907 this.spawn(changed).updateStyle().emit('class');
6908 }
6909
6910 return self;
6911 },
6912 addClass: function addClass(classes) {
6913 return this.toggleClass(classes, true);
6914 },
6915 hasClass: function hasClass(className) {
6916 var ele = this[0];
6917 return ele != null && ele._private.classes.has(className);
6918 },
6919 toggleClass: function toggleClass(classes, toggle) {
6920 if (!array(classes)) {
6921 // extract classes from string
6922 classes = classes.match(/\S+/g) || [];
6923 }
6924
6925 var self = this;
6926 var toggleUndefd = toggle === undefined;
6927 var changed = []; // eles who had classes changed
6928
6929 for (var i = 0, il = self.length; i < il; i++) {
6930 var ele = self[i];
6931 var eleClasses = ele._private.classes;
6932 var changedEle = false;
6933
6934 for (var j = 0; j < classes.length; j++) {
6935 var cls = classes[j];
6936 var hasClass = eleClasses.has(cls);
6937 var changedNow = false;
6938
6939 if (toggle || toggleUndefd && !hasClass) {
6940 eleClasses.add(cls);
6941 changedNow = true;
6942 } else if (!toggle || toggleUndefd && hasClass) {
6943 eleClasses["delete"](cls);
6944 changedNow = true;
6945 }
6946
6947 if (!changedEle && changedNow) {
6948 changed.push(ele);
6949 changedEle = true;
6950 }
6951 } // for j classes
6952
6953 } // for i eles
6954 // trigger update style on those eles that had class changes
6955
6956
6957 if (changed.length > 0) {
6958 this.spawn(changed).updateStyle().emit('class');
6959 }
6960
6961 return self;
6962 },
6963 removeClass: function removeClass(classes) {
6964 return this.toggleClass(classes, false);
6965 },
6966 flashClass: function flashClass(classes, duration) {
6967 var self = this;
6968
6969 if (duration == null) {
6970 duration = 250;
6971 } else if (duration === 0) {
6972 return self; // nothing to do really
6973 }
6974
6975 self.addClass(classes);
6976 setTimeout(function () {
6977 self.removeClass(classes);
6978 }, duration);
6979 return self;
6980 }
6981};
6982elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6983
6984var tokens = {
6985 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6986 // chars we need to escape in let names, etc
6987 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6988 // binary comparison op (used in data selectors)
6989 boolOp: '\\?|\\!|\\^',
6990 // boolean (unary) operators (used in data selectors)
6991 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6992 // string literals (used in data selectors) -- doublequotes | singlequotes
6993 number: number$1,
6994 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6995 meta: 'degree|indegree|outdegree',
6996 // allowed metadata fields (i.e. allowed functions to use from Collection)
6997 separator: '\\s*,\\s*',
6998 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6999 descendant: '\\s+',
7000 child: '\\s+>\\s+',
7001 subject: '\\$',
7002 group: 'node|edge|\\*',
7003 directedEdge: '\\s+->\\s+',
7004 undirectedEdge: '\\s+<->\\s+'
7005};
7006tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
7007
7008tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
7009
7010tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7011
7012tokens.id = tokens.variable; // an element id (follows variable conventions)
7013
7014(function () {
7015 var ops, op, i; // add @ variants to comparatorOp
7016
7017 ops = tokens.comparatorOp.split('|');
7018
7019 for (i = 0; i < ops.length; i++) {
7020 op = ops[i];
7021 tokens.comparatorOp += '|@' + op;
7022 } // add ! variants to comparatorOp
7023
7024
7025 ops = tokens.comparatorOp.split('|');
7026
7027 for (i = 0; i < ops.length; i++) {
7028 op = ops[i];
7029
7030 if (op.indexOf('!') >= 0) {
7031 continue;
7032 } // skip ops that explicitly contain !
7033
7034
7035 if (op === '=') {
7036 continue;
7037 } // skip = b/c != is explicitly defined
7038
7039
7040 tokens.comparatorOp += '|\\!' + op;
7041 }
7042})();
7043
7044/**
7045 * Make a new query object
7046 *
7047 * @prop type {Type} The type enum (int) of the query
7048 * @prop checks List of checks to make against an ele to test for a match
7049 */
7050var newQuery = function newQuery() {
7051 return {
7052 checks: []
7053 };
7054};
7055
7056/**
7057 * A check type enum-like object. Uses integer values for fast match() lookup.
7058 * The ordering does not matter as long as the ints are unique.
7059 */
7060var Type = {
7061 /** E.g. node */
7062 GROUP: 0,
7063
7064 /** A collection of elements */
7065 COLLECTION: 1,
7066
7067 /** A filter(ele) function */
7068 FILTER: 2,
7069
7070 /** E.g. [foo > 1] */
7071 DATA_COMPARE: 3,
7072
7073 /** E.g. [foo] */
7074 DATA_EXIST: 4,
7075
7076 /** E.g. [?foo] */
7077 DATA_BOOL: 5,
7078
7079 /** E.g. [[degree > 2]] */
7080 META_COMPARE: 6,
7081
7082 /** E.g. :selected */
7083 STATE: 7,
7084
7085 /** E.g. #foo */
7086 ID: 8,
7087
7088 /** E.g. .foo */
7089 CLASS: 9,
7090
7091 /** E.g. #foo <-> #bar */
7092 UNDIRECTED_EDGE: 10,
7093
7094 /** E.g. #foo -> #bar */
7095 DIRECTED_EDGE: 11,
7096
7097 /** E.g. $#foo -> #bar */
7098 NODE_SOURCE: 12,
7099
7100 /** E.g. #foo -> $#bar */
7101 NODE_TARGET: 13,
7102
7103 /** E.g. $#foo <-> #bar */
7104 NODE_NEIGHBOR: 14,
7105
7106 /** E.g. #foo > #bar */
7107 CHILD: 15,
7108
7109 /** E.g. #foo #bar */
7110 DESCENDANT: 16,
7111
7112 /** E.g. $#foo > #bar */
7113 PARENT: 17,
7114
7115 /** E.g. $#foo #bar */
7116 ANCESTOR: 18,
7117
7118 /** E.g. #foo > $bar > #baz */
7119 COMPOUND_SPLIT: 19,
7120
7121 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7122 TRUE: 20
7123};
7124
7125var stateSelectors = [{
7126 selector: ':selected',
7127 matches: function matches(ele) {
7128 return ele.selected();
7129 }
7130}, {
7131 selector: ':unselected',
7132 matches: function matches(ele) {
7133 return !ele.selected();
7134 }
7135}, {
7136 selector: ':selectable',
7137 matches: function matches(ele) {
7138 return ele.selectable();
7139 }
7140}, {
7141 selector: ':unselectable',
7142 matches: function matches(ele) {
7143 return !ele.selectable();
7144 }
7145}, {
7146 selector: ':locked',
7147 matches: function matches(ele) {
7148 return ele.locked();
7149 }
7150}, {
7151 selector: ':unlocked',
7152 matches: function matches(ele) {
7153 return !ele.locked();
7154 }
7155}, {
7156 selector: ':visible',
7157 matches: function matches(ele) {
7158 return ele.visible();
7159 }
7160}, {
7161 selector: ':hidden',
7162 matches: function matches(ele) {
7163 return !ele.visible();
7164 }
7165}, {
7166 selector: ':transparent',
7167 matches: function matches(ele) {
7168 return ele.transparent();
7169 }
7170}, {
7171 selector: ':grabbed',
7172 matches: function matches(ele) {
7173 return ele.grabbed();
7174 }
7175}, {
7176 selector: ':free',
7177 matches: function matches(ele) {
7178 return !ele.grabbed();
7179 }
7180}, {
7181 selector: ':removed',
7182 matches: function matches(ele) {
7183 return ele.removed();
7184 }
7185}, {
7186 selector: ':inside',
7187 matches: function matches(ele) {
7188 return !ele.removed();
7189 }
7190}, {
7191 selector: ':grabbable',
7192 matches: function matches(ele) {
7193 return ele.grabbable();
7194 }
7195}, {
7196 selector: ':ungrabbable',
7197 matches: function matches(ele) {
7198 return !ele.grabbable();
7199 }
7200}, {
7201 selector: ':animated',
7202 matches: function matches(ele) {
7203 return ele.animated();
7204 }
7205}, {
7206 selector: ':unanimated',
7207 matches: function matches(ele) {
7208 return !ele.animated();
7209 }
7210}, {
7211 selector: ':parent',
7212 matches: function matches(ele) {
7213 return ele.isParent();
7214 }
7215}, {
7216 selector: ':childless',
7217 matches: function matches(ele) {
7218 return ele.isChildless();
7219 }
7220}, {
7221 selector: ':child',
7222 matches: function matches(ele) {
7223 return ele.isChild();
7224 }
7225}, {
7226 selector: ':orphan',
7227 matches: function matches(ele) {
7228 return ele.isOrphan();
7229 }
7230}, {
7231 selector: ':nonorphan',
7232 matches: function matches(ele) {
7233 return ele.isChild();
7234 }
7235}, {
7236 selector: ':compound',
7237 matches: function matches(ele) {
7238 if (ele.isNode()) {
7239 return ele.isParent();
7240 } else {
7241 return ele.source().isParent() || ele.target().isParent();
7242 }
7243 }
7244}, {
7245 selector: ':loop',
7246 matches: function matches(ele) {
7247 return ele.isLoop();
7248 }
7249}, {
7250 selector: ':simple',
7251 matches: function matches(ele) {
7252 return ele.isSimple();
7253 }
7254}, {
7255 selector: ':active',
7256 matches: function matches(ele) {
7257 return ele.active();
7258 }
7259}, {
7260 selector: ':inactive',
7261 matches: function matches(ele) {
7262 return !ele.active();
7263 }
7264}, {
7265 selector: ':backgrounding',
7266 matches: function matches(ele) {
7267 return ele.backgrounding();
7268 }
7269}, {
7270 selector: ':nonbackgrounding',
7271 matches: function matches(ele) {
7272 return !ele.backgrounding();
7273 }
7274}].sort(function (a, b) {
7275 // n.b. selectors that are starting substrings of others must have the longer ones first
7276 return descending(a.selector, b.selector);
7277});
7278
7279var lookup = function () {
7280 var selToFn = {};
7281 var s;
7282
7283 for (var i = 0; i < stateSelectors.length; i++) {
7284 s = stateSelectors[i];
7285 selToFn[s.selector] = s.matches;
7286 }
7287
7288 return selToFn;
7289}();
7290
7291var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7292 return lookup[sel](ele);
7293};
7294var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7295 return s.selector;
7296}).join('|') + ')';
7297
7298// so that values get compared properly in Selector.filter()
7299
7300var cleanMetaChars = function cleanMetaChars(str) {
7301 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7302 return $1;
7303 });
7304};
7305
7306var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7307 selector[selector.length - 1] = replacementQuery;
7308}; // NOTE: add new expression syntax here to have it recognised by the parser;
7309// - a query contains all adjacent (i.e. no separator in between) expressions;
7310// - the current query is stored in selector[i]
7311// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7312
7313
7314var exprs = [{
7315 name: 'group',
7316 // just used for identifying when debugging
7317 query: true,
7318 regex: '(' + tokens.group + ')',
7319 populate: function populate(selector, query, _ref) {
7320 var _ref2 = _slicedToArray(_ref, 1),
7321 group = _ref2[0];
7322
7323 query.checks.push({
7324 type: Type.GROUP,
7325 value: group === '*' ? group : group + 's'
7326 });
7327 }
7328}, {
7329 name: 'state',
7330 query: true,
7331 regex: stateSelectorRegex,
7332 populate: function populate(selector, query, _ref3) {
7333 var _ref4 = _slicedToArray(_ref3, 1),
7334 state = _ref4[0];
7335
7336 query.checks.push({
7337 type: Type.STATE,
7338 value: state
7339 });
7340 }
7341}, {
7342 name: 'id',
7343 query: true,
7344 regex: '\\#(' + tokens.id + ')',
7345 populate: function populate(selector, query, _ref5) {
7346 var _ref6 = _slicedToArray(_ref5, 1),
7347 id = _ref6[0];
7348
7349 query.checks.push({
7350 type: Type.ID,
7351 value: cleanMetaChars(id)
7352 });
7353 }
7354}, {
7355 name: 'className',
7356 query: true,
7357 regex: '\\.(' + tokens.className + ')',
7358 populate: function populate(selector, query, _ref7) {
7359 var _ref8 = _slicedToArray(_ref7, 1),
7360 className = _ref8[0];
7361
7362 query.checks.push({
7363 type: Type.CLASS,
7364 value: cleanMetaChars(className)
7365 });
7366 }
7367}, {
7368 name: 'dataExists',
7369 query: true,
7370 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7371 populate: function populate(selector, query, _ref9) {
7372 var _ref10 = _slicedToArray(_ref9, 1),
7373 variable = _ref10[0];
7374
7375 query.checks.push({
7376 type: Type.DATA_EXIST,
7377 field: cleanMetaChars(variable)
7378 });
7379 }
7380}, {
7381 name: 'dataCompare',
7382 query: true,
7383 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7384 populate: function populate(selector, query, _ref11) {
7385 var _ref12 = _slicedToArray(_ref11, 3),
7386 variable = _ref12[0],
7387 comparatorOp = _ref12[1],
7388 value = _ref12[2];
7389
7390 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7391
7392 if (valueIsString) {
7393 value = value.substring(1, value.length - 1);
7394 } else {
7395 value = parseFloat(value);
7396 }
7397
7398 query.checks.push({
7399 type: Type.DATA_COMPARE,
7400 field: cleanMetaChars(variable),
7401 operator: comparatorOp,
7402 value: value
7403 });
7404 }
7405}, {
7406 name: 'dataBool',
7407 query: true,
7408 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7409 populate: function populate(selector, query, _ref13) {
7410 var _ref14 = _slicedToArray(_ref13, 2),
7411 boolOp = _ref14[0],
7412 variable = _ref14[1];
7413
7414 query.checks.push({
7415 type: Type.DATA_BOOL,
7416 field: cleanMetaChars(variable),
7417 operator: boolOp
7418 });
7419 }
7420}, {
7421 name: 'metaCompare',
7422 query: true,
7423 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7424 populate: function populate(selector, query, _ref15) {
7425 var _ref16 = _slicedToArray(_ref15, 3),
7426 meta = _ref16[0],
7427 comparatorOp = _ref16[1],
7428 number = _ref16[2];
7429
7430 query.checks.push({
7431 type: Type.META_COMPARE,
7432 field: cleanMetaChars(meta),
7433 operator: comparatorOp,
7434 value: parseFloat(number)
7435 });
7436 }
7437}, {
7438 name: 'nextQuery',
7439 separator: true,
7440 regex: tokens.separator,
7441 populate: function populate(selector, query) {
7442 var currentSubject = selector.currentSubject;
7443 var edgeCount = selector.edgeCount;
7444 var compoundCount = selector.compoundCount;
7445 var lastQ = selector[selector.length - 1];
7446
7447 if (currentSubject != null) {
7448 lastQ.subject = currentSubject;
7449 selector.currentSubject = null;
7450 }
7451
7452 lastQ.edgeCount = edgeCount;
7453 lastQ.compoundCount = compoundCount;
7454 selector.edgeCount = 0;
7455 selector.compoundCount = 0; // go on to next query
7456
7457 var nextQuery = selector[selector.length++] = newQuery();
7458 return nextQuery; // this is the new query to be filled by the following exprs
7459 }
7460}, {
7461 name: 'directedEdge',
7462 separator: true,
7463 regex: tokens.directedEdge,
7464 populate: function populate(selector, query) {
7465 if (selector.currentSubject == null) {
7466 // undirected edge
7467 var edgeQuery = newQuery();
7468 var source = query;
7469 var target = newQuery();
7470 edgeQuery.checks.push({
7471 type: Type.DIRECTED_EDGE,
7472 source: source,
7473 target: target
7474 }); // the query in the selector should be the edge rather than the source
7475
7476 replaceLastQuery(selector, query, edgeQuery);
7477 selector.edgeCount++; // we're now populating the target query with expressions that follow
7478
7479 return target;
7480 } else {
7481 // source/target
7482 var srcTgtQ = newQuery();
7483 var _source = query;
7484
7485 var _target = newQuery();
7486
7487 srcTgtQ.checks.push({
7488 type: Type.NODE_SOURCE,
7489 source: _source,
7490 target: _target
7491 }); // the query in the selector should be the neighbourhood rather than the node
7492
7493 replaceLastQuery(selector, query, srcTgtQ);
7494 selector.edgeCount++;
7495 return _target; // now populating the target with the following expressions
7496 }
7497 }
7498}, {
7499 name: 'undirectedEdge',
7500 separator: true,
7501 regex: tokens.undirectedEdge,
7502 populate: function populate(selector, query) {
7503 if (selector.currentSubject == null) {
7504 // undirected edge
7505 var edgeQuery = newQuery();
7506 var source = query;
7507 var target = newQuery();
7508 edgeQuery.checks.push({
7509 type: Type.UNDIRECTED_EDGE,
7510 nodes: [source, target]
7511 }); // the query in the selector should be the edge rather than the source
7512
7513 replaceLastQuery(selector, query, edgeQuery);
7514 selector.edgeCount++; // we're now populating the target query with expressions that follow
7515
7516 return target;
7517 } else {
7518 // neighbourhood
7519 var nhoodQ = newQuery();
7520 var node = query;
7521 var neighbor = newQuery();
7522 nhoodQ.checks.push({
7523 type: Type.NODE_NEIGHBOR,
7524 node: node,
7525 neighbor: neighbor
7526 }); // the query in the selector should be the neighbourhood rather than the node
7527
7528 replaceLastQuery(selector, query, nhoodQ);
7529 return neighbor; // now populating the neighbor with following expressions
7530 }
7531 }
7532}, {
7533 name: 'child',
7534 separator: true,
7535 regex: tokens.child,
7536 populate: function populate(selector, query) {
7537 if (selector.currentSubject == null) {
7538 // default: child query
7539 var parentChildQuery = newQuery();
7540 var child = newQuery();
7541 var parent = selector[selector.length - 1];
7542 parentChildQuery.checks.push({
7543 type: Type.CHILD,
7544 parent: parent,
7545 child: child
7546 }); // the query in the selector should be the '>' itself
7547
7548 replaceLastQuery(selector, query, parentChildQuery);
7549 selector.compoundCount++; // we're now populating the child query with expressions that follow
7550
7551 return child;
7552 } else if (selector.currentSubject === query) {
7553 // compound split query
7554 var compound = newQuery();
7555 var left = selector[selector.length - 1];
7556 var right = newQuery();
7557 var subject = newQuery();
7558
7559 var _child = newQuery();
7560
7561 var _parent = newQuery(); // set up the root compound q
7562
7563
7564 compound.checks.push({
7565 type: Type.COMPOUND_SPLIT,
7566 left: left,
7567 right: right,
7568 subject: subject
7569 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7570
7571 subject.checks = query.checks; // take the checks from the left
7572
7573 query.checks = [{
7574 type: Type.TRUE
7575 }]; // checks under left refs the subject implicitly
7576 // set up the right q
7577
7578 _parent.checks.push({
7579 type: Type.TRUE
7580 }); // parent implicitly refs the subject
7581
7582
7583 right.checks.push({
7584 type: Type.PARENT,
7585 // type is swapped on right side queries
7586 parent: _parent,
7587 child: _child // empty for now
7588
7589 });
7590 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7591
7592 selector.currentSubject = subject;
7593 selector.compoundCount++;
7594 return _child; // now populating the right side's child
7595 } else {
7596 // parent query
7597 // info for parent query
7598 var _parent2 = newQuery();
7599
7600 var _child2 = newQuery();
7601
7602 var pcQChecks = [{
7603 type: Type.PARENT,
7604 parent: _parent2,
7605 child: _child2
7606 }]; // the parent-child query takes the place of the query previously being populated
7607
7608 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7609
7610 query.checks = pcQChecks; // pc query takes over
7611
7612 selector.compoundCount++;
7613 return _child2; // we're now populating the child
7614 }
7615 }
7616}, {
7617 name: 'descendant',
7618 separator: true,
7619 regex: tokens.descendant,
7620 populate: function populate(selector, query) {
7621 if (selector.currentSubject == null) {
7622 // default: descendant query
7623 var ancChQuery = newQuery();
7624 var descendant = newQuery();
7625 var ancestor = selector[selector.length - 1];
7626 ancChQuery.checks.push({
7627 type: Type.DESCENDANT,
7628 ancestor: ancestor,
7629 descendant: descendant
7630 }); // the query in the selector should be the '>' itself
7631
7632 replaceLastQuery(selector, query, ancChQuery);
7633 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7634
7635 return descendant;
7636 } else if (selector.currentSubject === query) {
7637 // compound split query
7638 var compound = newQuery();
7639 var left = selector[selector.length - 1];
7640 var right = newQuery();
7641 var subject = newQuery();
7642
7643 var _descendant = newQuery();
7644
7645 var _ancestor = newQuery(); // set up the root compound q
7646
7647
7648 compound.checks.push({
7649 type: Type.COMPOUND_SPLIT,
7650 left: left,
7651 right: right,
7652 subject: subject
7653 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7654
7655 subject.checks = query.checks; // take the checks from the left
7656
7657 query.checks = [{
7658 type: Type.TRUE
7659 }]; // checks under left refs the subject implicitly
7660 // set up the right q
7661
7662 _ancestor.checks.push({
7663 type: Type.TRUE
7664 }); // ancestor implicitly refs the subject
7665
7666
7667 right.checks.push({
7668 type: Type.ANCESTOR,
7669 // type is swapped on right side queries
7670 ancestor: _ancestor,
7671 descendant: _descendant // empty for now
7672
7673 });
7674 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7675
7676 selector.currentSubject = subject;
7677 selector.compoundCount++;
7678 return _descendant; // now populating the right side's descendant
7679 } else {
7680 // ancestor query
7681 // info for parent query
7682 var _ancestor2 = newQuery();
7683
7684 var _descendant2 = newQuery();
7685
7686 var adQChecks = [{
7687 type: Type.ANCESTOR,
7688 ancestor: _ancestor2,
7689 descendant: _descendant2
7690 }]; // the parent-child query takes the place of the query previously being populated
7691
7692 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7693
7694 query.checks = adQChecks; // pc query takes over
7695
7696 selector.compoundCount++;
7697 return _descendant2; // we're now populating the child
7698 }
7699 }
7700}, {
7701 name: 'subject',
7702 modifier: true,
7703 regex: tokens.subject,
7704 populate: function populate(selector, query) {
7705 if (selector.currentSubject != null && selector.currentSubject !== query) {
7706 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7707 return false;
7708 }
7709
7710 selector.currentSubject = query;
7711 var topQ = selector[selector.length - 1];
7712 var topChk = topQ.checks[0];
7713 var topType = topChk == null ? null : topChk.type;
7714
7715 if (topType === Type.DIRECTED_EDGE) {
7716 // directed edge with subject on the target
7717 // change to target node check
7718 topChk.type = Type.NODE_TARGET;
7719 } else if (topType === Type.UNDIRECTED_EDGE) {
7720 // undirected edge with subject on the second node
7721 // change to neighbor check
7722 topChk.type = Type.NODE_NEIGHBOR;
7723 topChk.node = topChk.nodes[1]; // second node is subject
7724
7725 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7726
7727 topChk.nodes = null;
7728 }
7729 }
7730}];
7731exprs.forEach(function (e) {
7732 return e.regexObj = new RegExp('^' + e.regex);
7733});
7734
7735/**
7736 * Of all the expressions, find the first match in the remaining text.
7737 * @param {string} remaining The remaining text to parse
7738 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7739 */
7740
7741var consumeExpr = function consumeExpr(remaining) {
7742 var expr;
7743 var match;
7744 var name;
7745
7746 for (var j = 0; j < exprs.length; j++) {
7747 var e = exprs[j];
7748 var n = e.name;
7749 var m = remaining.match(e.regexObj);
7750
7751 if (m != null) {
7752 match = m;
7753 expr = e;
7754 name = n;
7755 var consumed = m[0];
7756 remaining = remaining.substring(consumed.length);
7757 break; // we've consumed one expr, so we can return now
7758 }
7759 }
7760
7761 return {
7762 expr: expr,
7763 match: match,
7764 name: name,
7765 remaining: remaining
7766 };
7767};
7768/**
7769 * Consume all the leading whitespace
7770 * @param {string} remaining The text to consume
7771 * @returns The text with the leading whitespace removed
7772 */
7773
7774
7775var consumeWhitespace = function consumeWhitespace(remaining) {
7776 var match = remaining.match(/^\s+/);
7777
7778 if (match) {
7779 var consumed = match[0];
7780 remaining = remaining.substring(consumed.length);
7781 }
7782
7783 return remaining;
7784};
7785/**
7786 * Parse the string and store the parsed representation in the Selector.
7787 * @param {string} selector The selector string
7788 * @returns `true` if the selector was successfully parsed, `false` otherwise
7789 */
7790
7791
7792var parse = function parse(selector) {
7793 var self = this;
7794 var remaining = self.inputText = selector;
7795 var currentQuery = self[0] = newQuery();
7796 self.length = 1;
7797 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7798
7799 for (;;) {
7800 var exprInfo = consumeExpr(remaining);
7801
7802 if (exprInfo.expr == null) {
7803 warn('The selector `' + selector + '`is invalid');
7804 return false;
7805 } else {
7806 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7807
7808 var ret = exprInfo.expr.populate(self, currentQuery, args);
7809
7810 if (ret === false) {
7811 return false; // exit if population failed
7812 } else if (ret != null) {
7813 currentQuery = ret; // change the current query to be filled if the expr specifies
7814 }
7815 }
7816
7817 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7818
7819 if (remaining.match(/^\s*$/)) {
7820 break;
7821 }
7822 }
7823
7824 var lastQ = self[self.length - 1];
7825
7826 if (self.currentSubject != null) {
7827 lastQ.subject = self.currentSubject;
7828 }
7829
7830 lastQ.edgeCount = self.edgeCount;
7831 lastQ.compoundCount = self.compoundCount;
7832
7833 for (var i = 0; i < self.length; i++) {
7834 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7835
7836 if (q.compoundCount > 0 && q.edgeCount > 0) {
7837 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7838 return false;
7839 }
7840
7841 if (q.edgeCount > 1) {
7842 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7843 return false;
7844 } else if (q.edgeCount === 1) {
7845 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.');
7846 }
7847 }
7848
7849 return true; // success
7850};
7851/**
7852 * Get the selector represented as a string. This value uses default formatting,
7853 * so things like spacing may differ from the input text passed to the constructor.
7854 * @returns {string} The selector string
7855 */
7856
7857
7858var toString = function toString() {
7859 if (this.toStringCache != null) {
7860 return this.toStringCache;
7861 }
7862
7863 var clean = function clean(obj) {
7864 if (obj == null) {
7865 return '';
7866 } else {
7867 return obj;
7868 }
7869 };
7870
7871 var cleanVal = function cleanVal(val) {
7872 if (string(val)) {
7873 return '"' + val + '"';
7874 } else {
7875 return clean(val);
7876 }
7877 };
7878
7879 var space = function space(val) {
7880 return ' ' + val + ' ';
7881 };
7882
7883 var checkToString = function checkToString(check, subject) {
7884 var type = check.type,
7885 value = check.value;
7886
7887 switch (type) {
7888 case Type.GROUP:
7889 {
7890 var group = clean(value);
7891 return group.substring(0, group.length - 1);
7892 }
7893
7894 case Type.DATA_COMPARE:
7895 {
7896 var field = check.field,
7897 operator = check.operator;
7898 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7899 }
7900
7901 case Type.DATA_BOOL:
7902 {
7903 var _operator = check.operator,
7904 _field = check.field;
7905 return '[' + clean(_operator) + _field + ']';
7906 }
7907
7908 case Type.DATA_EXIST:
7909 {
7910 var _field2 = check.field;
7911 return '[' + _field2 + ']';
7912 }
7913
7914 case Type.META_COMPARE:
7915 {
7916 var _operator2 = check.operator,
7917 _field3 = check.field;
7918 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7919 }
7920
7921 case Type.STATE:
7922 {
7923 return value;
7924 }
7925
7926 case Type.ID:
7927 {
7928 return '#' + value;
7929 }
7930
7931 case Type.CLASS:
7932 {
7933 return '.' + value;
7934 }
7935
7936 case Type.PARENT:
7937 case Type.CHILD:
7938 {
7939 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7940 }
7941
7942 case Type.ANCESTOR:
7943 case Type.DESCENDANT:
7944 {
7945 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7946 }
7947
7948 case Type.COMPOUND_SPLIT:
7949 {
7950 var lhs = queryToString(check.left, subject);
7951 var sub = queryToString(check.subject, subject);
7952 var rhs = queryToString(check.right, subject);
7953 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7954 }
7955
7956 case Type.TRUE:
7957 {
7958 return '';
7959 }
7960 }
7961 };
7962
7963 var queryToString = function queryToString(query, subject) {
7964 return query.checks.reduce(function (str, chk, i) {
7965 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7966 }, '');
7967 };
7968
7969 var str = '';
7970
7971 for (var i = 0; i < this.length; i++) {
7972 var query = this[i];
7973 str += queryToString(query, query.subject);
7974
7975 if (this.length > 1 && i < this.length - 1) {
7976 str += ', ';
7977 }
7978 }
7979
7980 this.toStringCache = str;
7981 return str;
7982};
7983var parse$1 = {
7984 parse: parse,
7985 toString: toString
7986};
7987
7988var valCmp = function valCmp(fieldVal, operator, value) {
7989 var matches;
7990 var isFieldStr = string(fieldVal);
7991 var isFieldNum = number(fieldVal);
7992 var isValStr = string(value);
7993 var fieldStr, valStr;
7994 var caseInsensitive = false;
7995 var notExpr = false;
7996 var isIneqCmp = false;
7997
7998 if (operator.indexOf('!') >= 0) {
7999 operator = operator.replace('!', '');
8000 notExpr = true;
8001 }
8002
8003 if (operator.indexOf('@') >= 0) {
8004 operator = operator.replace('@', '');
8005 caseInsensitive = true;
8006 }
8007
8008 if (isFieldStr || isValStr || caseInsensitive) {
8009 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8010 valStr = '' + value;
8011 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8012 // even if we're comparing numbers
8013
8014
8015 if (caseInsensitive) {
8016 fieldVal = fieldStr = fieldStr.toLowerCase();
8017 value = valStr = valStr.toLowerCase();
8018 }
8019
8020 switch (operator) {
8021 case '*=':
8022 matches = fieldStr.indexOf(valStr) >= 0;
8023 break;
8024
8025 case '$=':
8026 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8027 break;
8028
8029 case '^=':
8030 matches = fieldStr.indexOf(valStr) === 0;
8031 break;
8032
8033 case '=':
8034 matches = fieldVal === value;
8035 break;
8036
8037 case '>':
8038 isIneqCmp = true;
8039 matches = fieldVal > value;
8040 break;
8041
8042 case '>=':
8043 isIneqCmp = true;
8044 matches = fieldVal >= value;
8045 break;
8046
8047 case '<':
8048 isIneqCmp = true;
8049 matches = fieldVal < value;
8050 break;
8051
8052 case '<=':
8053 isIneqCmp = true;
8054 matches = fieldVal <= value;
8055 break;
8056
8057 default:
8058 matches = false;
8059 break;
8060 } // apply the not op, but null vals for inequalities should always stay non-matching
8061
8062
8063 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8064 matches = !matches;
8065 }
8066
8067 return matches;
8068};
8069var boolCmp = function boolCmp(fieldVal, operator) {
8070 switch (operator) {
8071 case '?':
8072 return fieldVal ? true : false;
8073
8074 case '!':
8075 return fieldVal ? false : true;
8076
8077 case '^':
8078 return fieldVal === undefined;
8079 }
8080};
8081var existCmp = function existCmp(fieldVal) {
8082 return fieldVal !== undefined;
8083};
8084var data = function data(ele, field) {
8085 return ele.data(field);
8086};
8087var meta = function meta(ele, field) {
8088 return ele[field]();
8089};
8090
8091/** A lookup of `match(check, ele)` functions by `Type` int */
8092
8093var match = [];
8094/**
8095 * Returns whether the query matches for the element
8096 * @param query The `{ type, value, ... }` query object
8097 * @param ele The element to compare against
8098*/
8099
8100var matches = function matches(query, ele) {
8101 return query.checks.every(function (chk) {
8102 return match[chk.type](chk, ele);
8103 });
8104};
8105
8106match[Type.GROUP] = function (check, ele) {
8107 var group = check.value;
8108 return group === '*' || group === ele.group();
8109};
8110
8111match[Type.STATE] = function (check, ele) {
8112 var stateSelector = check.value;
8113 return stateSelectorMatches(stateSelector, ele);
8114};
8115
8116match[Type.ID] = function (check, ele) {
8117 var id = check.value;
8118 return ele.id() === id;
8119};
8120
8121match[Type.CLASS] = function (check, ele) {
8122 var cls = check.value;
8123 return ele.hasClass(cls);
8124};
8125
8126match[Type.META_COMPARE] = function (check, ele) {
8127 var field = check.field,
8128 operator = check.operator,
8129 value = check.value;
8130 return valCmp(meta(ele, field), operator, value);
8131};
8132
8133match[Type.DATA_COMPARE] = function (check, ele) {
8134 var field = check.field,
8135 operator = check.operator,
8136 value = check.value;
8137 return valCmp(data(ele, field), operator, value);
8138};
8139
8140match[Type.DATA_BOOL] = function (check, ele) {
8141 var field = check.field,
8142 operator = check.operator;
8143 return boolCmp(data(ele, field), operator);
8144};
8145
8146match[Type.DATA_EXIST] = function (check, ele) {
8147 var field = check.field,
8148 operator = check.operator;
8149 return existCmp(data(ele, field));
8150};
8151
8152match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8153 var qA = check.nodes[0];
8154 var qB = check.nodes[1];
8155 var src = ele.source();
8156 var tgt = ele.target();
8157 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8158};
8159
8160match[Type.NODE_NEIGHBOR] = function (check, ele) {
8161 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8162 return n.isNode() && matches(check.neighbor, n);
8163 });
8164};
8165
8166match[Type.DIRECTED_EDGE] = function (check, ele) {
8167 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8168};
8169
8170match[Type.NODE_SOURCE] = function (check, ele) {
8171 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8172 return n.isNode() && matches(check.target, n);
8173 });
8174};
8175
8176match[Type.NODE_TARGET] = function (check, ele) {
8177 return matches(check.target, ele) && ele.incomers().some(function (n) {
8178 return n.isNode() && matches(check.source, n);
8179 });
8180};
8181
8182match[Type.CHILD] = function (check, ele) {
8183 return matches(check.child, ele) && matches(check.parent, ele.parent());
8184};
8185
8186match[Type.PARENT] = function (check, ele) {
8187 return matches(check.parent, ele) && ele.children().some(function (c) {
8188 return matches(check.child, c);
8189 });
8190};
8191
8192match[Type.DESCENDANT] = function (check, ele) {
8193 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8194 return matches(check.ancestor, a);
8195 });
8196};
8197
8198match[Type.ANCESTOR] = function (check, ele) {
8199 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8200 return matches(check.descendant, d);
8201 });
8202};
8203
8204match[Type.COMPOUND_SPLIT] = function (check, ele) {
8205 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8206};
8207
8208match[Type.TRUE] = function () {
8209 return true;
8210};
8211
8212match[Type.COLLECTION] = function (check, ele) {
8213 var collection = check.value;
8214 return collection.has(ele);
8215};
8216
8217match[Type.FILTER] = function (check, ele) {
8218 var filter = check.value;
8219 return filter(ele);
8220};
8221
8222var filter = function filter(collection) {
8223 var self = this; // for 1 id #foo queries, just get the element
8224
8225 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8226 return collection.getElementById(self[0].checks[0].value).collection();
8227 }
8228
8229 var selectorFunction = function selectorFunction(element) {
8230 for (var j = 0; j < self.length; j++) {
8231 var query = self[j];
8232
8233 if (matches(query, element)) {
8234 return true;
8235 }
8236 }
8237
8238 return false;
8239 };
8240
8241 if (self.text() == null) {
8242 selectorFunction = function selectorFunction() {
8243 return true;
8244 };
8245 }
8246
8247 return collection.filter(selectorFunction);
8248}; // filter
8249// does selector match a single element?
8250
8251
8252var matches$1 = function matches$1(ele) {
8253 var self = this;
8254
8255 for (var j = 0; j < self.length; j++) {
8256 var query = self[j];
8257
8258 if (matches(query, ele)) {
8259 return true;
8260 }
8261 }
8262
8263 return false;
8264}; // matches
8265
8266
8267var matching = {
8268 matches: matches$1,
8269 filter: filter
8270};
8271
8272var Selector = function Selector(selector) {
8273 this.inputText = selector;
8274 this.currentSubject = null;
8275 this.compoundCount = 0;
8276 this.edgeCount = 0;
8277 this.length = 0;
8278
8279 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8280 this.addQuery({
8281 checks: [{
8282 type: Type.COLLECTION,
8283 value: selector.collection()
8284 }]
8285 });
8286 } else if (fn(selector)) {
8287 this.addQuery({
8288 checks: [{
8289 type: Type.FILTER,
8290 value: selector
8291 }]
8292 });
8293 } else if (string(selector)) {
8294 if (!this.parse(selector)) {
8295 this.invalid = true;
8296 }
8297 } else {
8298 error('A selector must be created from a string; found ');
8299 }
8300};
8301
8302var selfn = Selector.prototype;
8303[parse$1, matching].forEach(function (p) {
8304 return extend(selfn, p);
8305});
8306
8307selfn.text = function () {
8308 return this.inputText;
8309};
8310
8311selfn.size = function () {
8312 return this.length;
8313};
8314
8315selfn.eq = function (i) {
8316 return this[i];
8317};
8318
8319selfn.sameText = function (otherSel) {
8320 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8321};
8322
8323selfn.addQuery = function (q) {
8324 this[this.length++] = q;
8325};
8326
8327selfn.selector = selfn.toString;
8328
8329var elesfn$f = {
8330 allAre: function allAre(selector) {
8331 var selObj = new Selector(selector);
8332 return this.every(function (ele) {
8333 return selObj.matches(ele);
8334 });
8335 },
8336 is: function is(selector) {
8337 var selObj = new Selector(selector);
8338 return this.some(function (ele) {
8339 return selObj.matches(ele);
8340 });
8341 },
8342 some: function some(fn, thisArg) {
8343 for (var i = 0; i < this.length; i++) {
8344 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8345
8346 if (ret) {
8347 return true;
8348 }
8349 }
8350
8351 return false;
8352 },
8353 every: function every(fn, thisArg) {
8354 for (var i = 0; i < this.length; i++) {
8355 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8356
8357 if (!ret) {
8358 return false;
8359 }
8360 }
8361
8362 return true;
8363 },
8364 same: function same(collection) {
8365 // cheap collection ref check
8366 if (this === collection) {
8367 return true;
8368 }
8369
8370 collection = this.cy().collection(collection);
8371 var thisLength = this.length;
8372 var collectionLength = collection.length; // cheap length check
8373
8374 if (thisLength !== collectionLength) {
8375 return false;
8376 } // cheap element ref check
8377
8378
8379 if (thisLength === 1) {
8380 return this[0] === collection[0];
8381 }
8382
8383 return this.every(function (ele) {
8384 return collection.hasElementWithId(ele.id());
8385 });
8386 },
8387 anySame: function anySame(collection) {
8388 collection = this.cy().collection(collection);
8389 return this.some(function (ele) {
8390 return collection.hasElementWithId(ele.id());
8391 });
8392 },
8393 allAreNeighbors: function allAreNeighbors(collection) {
8394 collection = this.cy().collection(collection);
8395 var nhood = this.neighborhood();
8396 return collection.every(function (ele) {
8397 return nhood.hasElementWithId(ele.id());
8398 });
8399 },
8400 contains: function contains(collection) {
8401 collection = this.cy().collection(collection);
8402 var self = this;
8403 return collection.every(function (ele) {
8404 return self.hasElementWithId(ele.id());
8405 });
8406 }
8407};
8408elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8409elesfn$f.has = elesfn$f.contains;
8410elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8411
8412var cache = function cache(fn, name) {
8413 return function traversalCache(arg1, arg2, arg3, arg4) {
8414 var selectorOrEles = arg1;
8415 var eles = this;
8416 var key;
8417
8418 if (selectorOrEles == null) {
8419 key = '';
8420 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8421 key = selectorOrEles.id();
8422 }
8423
8424 if (eles.length === 1 && key) {
8425 var _p = eles[0]._private;
8426 var tch = _p.traversalCache = _p.traversalCache || {};
8427 var ch = tch[name] = tch[name] || [];
8428 var hash = hashString(key);
8429 var cacheHit = ch[hash];
8430
8431 if (cacheHit) {
8432 return cacheHit;
8433 } else {
8434 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8435 }
8436 } else {
8437 return fn.call(eles, arg1, arg2, arg3, arg4);
8438 }
8439 };
8440};
8441
8442var elesfn$g = {
8443 parent: function parent(selector) {
8444 var parents = []; // optimisation for single ele call
8445
8446 if (this.length === 1) {
8447 var parent = this[0]._private.parent;
8448
8449 if (parent) {
8450 return parent;
8451 }
8452 }
8453
8454 for (var i = 0; i < this.length; i++) {
8455 var ele = this[i];
8456 var _parent = ele._private.parent;
8457
8458 if (_parent) {
8459 parents.push(_parent);
8460 }
8461 }
8462
8463 return this.spawn(parents, true).filter(selector);
8464 },
8465 parents: function parents(selector) {
8466 var parents = [];
8467 var eles = this.parent();
8468
8469 while (eles.nonempty()) {
8470 for (var i = 0; i < eles.length; i++) {
8471 var ele = eles[i];
8472 parents.push(ele);
8473 }
8474
8475 eles = eles.parent();
8476 }
8477
8478 return this.spawn(parents, true).filter(selector);
8479 },
8480 commonAncestors: function commonAncestors(selector) {
8481 var ancestors;
8482
8483 for (var i = 0; i < this.length; i++) {
8484 var ele = this[i];
8485 var parents = ele.parents();
8486 ancestors = ancestors || parents;
8487 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8488 }
8489
8490 return ancestors.filter(selector);
8491 },
8492 orphans: function orphans(selector) {
8493 return this.stdFilter(function (ele) {
8494 return ele.isOrphan();
8495 }).filter(selector);
8496 },
8497 nonorphans: function nonorphans(selector) {
8498 return this.stdFilter(function (ele) {
8499 return ele.isChild();
8500 }).filter(selector);
8501 },
8502 children: cache(function (selector) {
8503 var children = [];
8504
8505 for (var i = 0; i < this.length; i++) {
8506 var ele = this[i];
8507 var eleChildren = ele._private.children;
8508
8509 for (var j = 0; j < eleChildren.length; j++) {
8510 children.push(eleChildren[j]);
8511 }
8512 }
8513
8514 return this.spawn(children, true).filter(selector);
8515 }, 'children'),
8516 siblings: function siblings(selector) {
8517 return this.parent().children().not(this).filter(selector);
8518 },
8519 isParent: function isParent() {
8520 var ele = this[0];
8521
8522 if (ele) {
8523 return ele.isNode() && ele._private.children.length !== 0;
8524 }
8525 },
8526 isChildless: function isChildless() {
8527 var ele = this[0];
8528
8529 if (ele) {
8530 return ele.isNode() && ele._private.children.length === 0;
8531 }
8532 },
8533 isChild: function isChild() {
8534 var ele = this[0];
8535
8536 if (ele) {
8537 return ele.isNode() && ele._private.parent != null;
8538 }
8539 },
8540 isOrphan: function isOrphan() {
8541 var ele = this[0];
8542
8543 if (ele) {
8544 return ele.isNode() && ele._private.parent == null;
8545 }
8546 },
8547 descendants: function descendants(selector) {
8548 var elements = [];
8549
8550 function add(eles) {
8551 for (var i = 0; i < eles.length; i++) {
8552 var ele = eles[i];
8553 elements.push(ele);
8554
8555 if (ele.children().nonempty()) {
8556 add(ele.children());
8557 }
8558 }
8559 }
8560
8561 add(this.children());
8562 return this.spawn(elements, true).filter(selector);
8563 }
8564};
8565
8566function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8567 var q = [];
8568 var did = new Set$1();
8569 var cy = eles.cy();
8570 var hasCompounds = cy.hasCompoundNodes();
8571
8572 for (var i = 0; i < eles.length; i++) {
8573 var ele = eles[i];
8574
8575 if (includeSelf) {
8576 q.push(ele);
8577 } else if (hasCompounds) {
8578 recursiveStep(q, did, ele);
8579 }
8580 }
8581
8582 while (q.length > 0) {
8583 var _ele = q.shift();
8584
8585 fn(_ele);
8586 did.add(_ele.id());
8587
8588 if (hasCompounds) {
8589 recursiveStep(q, did, _ele);
8590 }
8591 }
8592
8593 return eles;
8594}
8595
8596function addChildren(q, did, ele) {
8597 if (ele.isParent()) {
8598 var children = ele._private.children;
8599
8600 for (var i = 0; i < children.length; i++) {
8601 var child = children[i];
8602
8603 if (!did.has(child.id())) {
8604 q.push(child);
8605 }
8606 }
8607 }
8608} // very efficient version of eles.add( eles.descendants() ).forEach()
8609// for internal use
8610
8611
8612elesfn$g.forEachDown = function (fn) {
8613 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8614 return forEachCompound(this, fn, includeSelf, addChildren);
8615};
8616
8617function addParent(q, did, ele) {
8618 if (ele.isChild()) {
8619 var parent = ele._private.parent;
8620
8621 if (!did.has(parent.id())) {
8622 q.push(parent);
8623 }
8624 }
8625}
8626
8627elesfn$g.forEachUp = function (fn) {
8628 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8629 return forEachCompound(this, fn, includeSelf, addParent);
8630};
8631
8632function addParentAndChildren(q, did, ele) {
8633 addParent(q, did, ele);
8634 addChildren(q, did, ele);
8635}
8636
8637elesfn$g.forEachUpAndDown = function (fn) {
8638 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8639 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8640}; // aliases
8641
8642
8643elesfn$g.ancestors = elesfn$g.parents;
8644
8645var fn$1, elesfn$h;
8646fn$1 = elesfn$h = {
8647 data: define$3.data({
8648 field: 'data',
8649 bindingEvent: 'data',
8650 allowBinding: true,
8651 allowSetting: true,
8652 settingEvent: 'data',
8653 settingTriggersEvent: true,
8654 triggerFnName: 'trigger',
8655 allowGetting: true,
8656 immutableKeys: {
8657 'id': true,
8658 'source': true,
8659 'target': true,
8660 'parent': true
8661 },
8662 updateStyle: true
8663 }),
8664 removeData: define$3.removeData({
8665 field: 'data',
8666 event: 'data',
8667 triggerFnName: 'trigger',
8668 triggerEvent: true,
8669 immutableKeys: {
8670 'id': true,
8671 'source': true,
8672 'target': true,
8673 'parent': true
8674 },
8675 updateStyle: true
8676 }),
8677 scratch: define$3.data({
8678 field: 'scratch',
8679 bindingEvent: 'scratch',
8680 allowBinding: true,
8681 allowSetting: true,
8682 settingEvent: 'scratch',
8683 settingTriggersEvent: true,
8684 triggerFnName: 'trigger',
8685 allowGetting: true,
8686 updateStyle: true
8687 }),
8688 removeScratch: define$3.removeData({
8689 field: 'scratch',
8690 event: 'scratch',
8691 triggerFnName: 'trigger',
8692 triggerEvent: true,
8693 updateStyle: true
8694 }),
8695 rscratch: define$3.data({
8696 field: 'rscratch',
8697 allowBinding: false,
8698 allowSetting: true,
8699 settingTriggersEvent: false,
8700 allowGetting: true
8701 }),
8702 removeRscratch: define$3.removeData({
8703 field: 'rscratch',
8704 triggerEvent: false
8705 }),
8706 id: function id() {
8707 var ele = this[0];
8708
8709 if (ele) {
8710 return ele._private.data.id;
8711 }
8712 }
8713}; // aliases
8714
8715fn$1.attr = fn$1.data;
8716fn$1.removeAttr = fn$1.removeData;
8717var data$1 = elesfn$h;
8718
8719var elesfn$i = {};
8720
8721function defineDegreeFunction(callback) {
8722 return function (includeLoops) {
8723 var self = this;
8724
8725 if (includeLoops === undefined) {
8726 includeLoops = true;
8727 }
8728
8729 if (self.length === 0) {
8730 return;
8731 }
8732
8733 if (self.isNode() && !self.removed()) {
8734 var degree = 0;
8735 var node = self[0];
8736 var connectedEdges = node._private.edges;
8737
8738 for (var i = 0; i < connectedEdges.length; i++) {
8739 var edge = connectedEdges[i];
8740
8741 if (!includeLoops && edge.isLoop()) {
8742 continue;
8743 }
8744
8745 degree += callback(node, edge);
8746 }
8747
8748 return degree;
8749 } else {
8750 return;
8751 }
8752 };
8753}
8754
8755extend(elesfn$i, {
8756 degree: defineDegreeFunction(function (node, edge) {
8757 if (edge.source().same(edge.target())) {
8758 return 2;
8759 } else {
8760 return 1;
8761 }
8762 }),
8763 indegree: defineDegreeFunction(function (node, edge) {
8764 if (edge.target().same(node)) {
8765 return 1;
8766 } else {
8767 return 0;
8768 }
8769 }),
8770 outdegree: defineDegreeFunction(function (node, edge) {
8771 if (edge.source().same(node)) {
8772 return 1;
8773 } else {
8774 return 0;
8775 }
8776 })
8777});
8778
8779function defineDegreeBoundsFunction(degreeFn, callback) {
8780 return function (includeLoops) {
8781 var ret;
8782 var nodes = this.nodes();
8783
8784 for (var i = 0; i < nodes.length; i++) {
8785 var ele = nodes[i];
8786 var degree = ele[degreeFn](includeLoops);
8787
8788 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8789 ret = degree;
8790 }
8791 }
8792
8793 return ret;
8794 };
8795}
8796
8797extend(elesfn$i, {
8798 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8799 return degree < min;
8800 }),
8801 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8802 return degree > max;
8803 }),
8804 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8805 return degree < min;
8806 }),
8807 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8808 return degree > max;
8809 }),
8810 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8811 return degree < min;
8812 }),
8813 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8814 return degree > max;
8815 })
8816});
8817extend(elesfn$i, {
8818 totalDegree: function totalDegree(includeLoops) {
8819 var total = 0;
8820 var nodes = this.nodes();
8821
8822 for (var i = 0; i < nodes.length; i++) {
8823 total += nodes[i].degree(includeLoops);
8824 }
8825
8826 return total;
8827 }
8828});
8829
8830var fn$2, elesfn$j;
8831
8832var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8833 for (var i = 0; i < eles.length; i++) {
8834 var ele = eles[i];
8835
8836 if (!ele.locked()) {
8837 var oldPos = ele._private.position;
8838 var delta = {
8839 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8840 y: newPos.y != null ? newPos.y - oldPos.y : 0
8841 };
8842
8843 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8844 ele.children().shift(delta, silent);
8845 }
8846
8847 ele.dirtyBoundingBoxCache();
8848 }
8849 }
8850};
8851
8852var positionDef = {
8853 field: 'position',
8854 bindingEvent: 'position',
8855 allowBinding: true,
8856 allowSetting: true,
8857 settingEvent: 'position',
8858 settingTriggersEvent: true,
8859 triggerFnName: 'emitAndNotify',
8860 allowGetting: true,
8861 validKeys: ['x', 'y'],
8862 beforeGet: function beforeGet(ele) {
8863 ele.updateCompoundBounds();
8864 },
8865 beforeSet: function beforeSet(eles, newPos) {
8866 beforePositionSet(eles, newPos, false);
8867 },
8868 onSet: function onSet(eles) {
8869 eles.dirtyCompoundBoundsCache();
8870 },
8871 canSet: function canSet(ele) {
8872 return !ele.locked();
8873 }
8874};
8875fn$2 = elesfn$j = {
8876 position: define$3.data(positionDef),
8877 // position but no notification to renderer
8878 silentPosition: define$3.data(extend({}, positionDef, {
8879 allowBinding: false,
8880 allowSetting: true,
8881 settingTriggersEvent: false,
8882 allowGetting: false,
8883 beforeSet: function beforeSet(eles, newPos) {
8884 beforePositionSet(eles, newPos, true);
8885 },
8886 onSet: function onSet(eles) {
8887 eles.dirtyCompoundBoundsCache();
8888 }
8889 })),
8890 positions: function positions(pos, silent) {
8891 if (plainObject(pos)) {
8892 if (silent) {
8893 this.silentPosition(pos);
8894 } else {
8895 this.position(pos);
8896 }
8897 } else if (fn(pos)) {
8898 var _fn = pos;
8899 var cy = this.cy();
8900 cy.startBatch();
8901
8902 for (var i = 0; i < this.length; i++) {
8903 var ele = this[i];
8904
8905 var _pos = void 0;
8906
8907 if (_pos = _fn(ele, i)) {
8908 if (silent) {
8909 ele.silentPosition(_pos);
8910 } else {
8911 ele.position(_pos);
8912 }
8913 }
8914 }
8915
8916 cy.endBatch();
8917 }
8918
8919 return this; // chaining
8920 },
8921 silentPositions: function silentPositions(pos) {
8922 return this.positions(pos, true);
8923 },
8924 shift: function shift(dim, val, silent) {
8925 var delta;
8926
8927 if (plainObject(dim)) {
8928 delta = {
8929 x: number(dim.x) ? dim.x : 0,
8930 y: number(dim.y) ? dim.y : 0
8931 };
8932 silent = val;
8933 } else if (string(dim) && number(val)) {
8934 delta = {
8935 x: 0,
8936 y: 0
8937 };
8938 delta[dim] = val;
8939 }
8940
8941 if (delta != null) {
8942 var cy = this.cy();
8943 cy.startBatch();
8944
8945 for (var i = 0; i < this.length; i++) {
8946 var ele = this[i]; // exclude any node that is a descendant of the calling collection
8947
8948 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
8949 continue;
8950 }
8951
8952 var pos = ele.position();
8953 var newPos = {
8954 x: pos.x + delta.x,
8955 y: pos.y + delta.y
8956 };
8957
8958 if (silent) {
8959 ele.silentPosition(newPos);
8960 } else {
8961 ele.position(newPos);
8962 }
8963 }
8964
8965 cy.endBatch();
8966 }
8967
8968 return this;
8969 },
8970 silentShift: function silentShift(dim, val) {
8971 if (plainObject(dim)) {
8972 this.shift(dim, true);
8973 } else if (string(dim) && number(val)) {
8974 this.shift(dim, val, true);
8975 }
8976
8977 return this;
8978 },
8979 // get/set the rendered (i.e. on screen) positon of the element
8980 renderedPosition: function renderedPosition(dim, val) {
8981 var ele = this[0];
8982 var cy = this.cy();
8983 var zoom = cy.zoom();
8984 var pan = cy.pan();
8985 var rpos = plainObject(dim) ? dim : undefined;
8986 var setting = rpos !== undefined || val !== undefined && string(dim);
8987
8988 if (ele && ele.isNode()) {
8989 // must have an element and must be a node to return position
8990 if (setting) {
8991 for (var i = 0; i < this.length; i++) {
8992 var _ele = this[i];
8993
8994 if (val !== undefined) {
8995 // set one dimension
8996 _ele.position(dim, (val - pan[dim]) / zoom);
8997 } else if (rpos !== undefined) {
8998 // set whole position
8999 _ele.position(renderedToModelPosition(rpos, zoom, pan));
9000 }
9001 }
9002 } else {
9003 // getting
9004 var pos = ele.position();
9005 rpos = modelToRenderedPosition(pos, zoom, pan);
9006
9007 if (dim === undefined) {
9008 // then return the whole rendered position
9009 return rpos;
9010 } else {
9011 // then return the specified dimension
9012 return rpos[dim];
9013 }
9014 }
9015 } else if (!setting) {
9016 return undefined; // for empty collection case
9017 }
9018
9019 return this; // chaining
9020 },
9021 // get/set the position relative to the parent
9022 relativePosition: function relativePosition(dim, val) {
9023 var ele = this[0];
9024 var cy = this.cy();
9025 var ppos = plainObject(dim) ? dim : undefined;
9026 var setting = ppos !== undefined || val !== undefined && string(dim);
9027 var hasCompoundNodes = cy.hasCompoundNodes();
9028
9029 if (ele && ele.isNode()) {
9030 // must have an element and must be a node to return position
9031 if (setting) {
9032 for (var i = 0; i < this.length; i++) {
9033 var _ele2 = this[i];
9034 var parent = hasCompoundNodes ? _ele2.parent() : null;
9035 var hasParent = parent && parent.length > 0;
9036 var relativeToParent = hasParent;
9037
9038 if (hasParent) {
9039 parent = parent[0];
9040 }
9041
9042 var origin = relativeToParent ? parent.position() : {
9043 x: 0,
9044 y: 0
9045 };
9046
9047 if (val !== undefined) {
9048 // set one dimension
9049 _ele2.position(dim, val + origin[dim]);
9050 } else if (ppos !== undefined) {
9051 // set whole position
9052 _ele2.position({
9053 x: ppos.x + origin.x,
9054 y: ppos.y + origin.y
9055 });
9056 }
9057 }
9058 } else {
9059 // getting
9060 var pos = ele.position();
9061
9062 var _parent = hasCompoundNodes ? ele.parent() : null;
9063
9064 var _hasParent = _parent && _parent.length > 0;
9065
9066 var _relativeToParent = _hasParent;
9067
9068 if (_hasParent) {
9069 _parent = _parent[0];
9070 }
9071
9072 var _origin = _relativeToParent ? _parent.position() : {
9073 x: 0,
9074 y: 0
9075 };
9076
9077 ppos = {
9078 x: pos.x - _origin.x,
9079 y: pos.y - _origin.y
9080 };
9081
9082 if (dim === undefined) {
9083 // then return the whole rendered position
9084 return ppos;
9085 } else {
9086 // then return the specified dimension
9087 return ppos[dim];
9088 }
9089 }
9090 } else if (!setting) {
9091 return undefined; // for empty collection case
9092 }
9093
9094 return this; // chaining
9095 }
9096}; // aliases
9097
9098fn$2.modelPosition = fn$2.point = fn$2.position;
9099fn$2.modelPositions = fn$2.points = fn$2.positions;
9100fn$2.renderedPoint = fn$2.renderedPosition;
9101fn$2.relativePoint = fn$2.relativePosition;
9102var position = elesfn$j;
9103
9104var fn$3, elesfn$k;
9105fn$3 = elesfn$k = {};
9106
9107elesfn$k.renderedBoundingBox = function (options) {
9108 var bb = this.boundingBox(options);
9109 var cy = this.cy();
9110 var zoom = cy.zoom();
9111 var pan = cy.pan();
9112 var x1 = bb.x1 * zoom + pan.x;
9113 var x2 = bb.x2 * zoom + pan.x;
9114 var y1 = bb.y1 * zoom + pan.y;
9115 var y2 = bb.y2 * zoom + pan.y;
9116 return {
9117 x1: x1,
9118 x2: x2,
9119 y1: y1,
9120 y2: y2,
9121 w: x2 - x1,
9122 h: y2 - y1
9123 };
9124};
9125
9126elesfn$k.dirtyCompoundBoundsCache = function () {
9127 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9128 var cy = this.cy();
9129
9130 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9131 return this;
9132 }
9133
9134 this.forEachUp(function (ele) {
9135 if (ele.isParent()) {
9136 var _p = ele._private;
9137 _p.compoundBoundsClean = false;
9138 _p.bbCache = null;
9139
9140 if (!silent) {
9141 ele.emitAndNotify('bounds');
9142 }
9143 }
9144 });
9145 return this;
9146};
9147
9148elesfn$k.updateCompoundBounds = function () {
9149 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9150 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9151
9152 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9153 return this;
9154 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9155
9156
9157 if (!force && cy.batching()) {
9158 return this;
9159 }
9160
9161 function update(parent) {
9162 if (!parent.isParent()) {
9163 return;
9164 }
9165
9166 var _p = parent._private;
9167 var children = parent.children();
9168 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9169 var min = {
9170 width: {
9171 val: parent.pstyle('min-width').pfValue,
9172 left: parent.pstyle('min-width-bias-left'),
9173 right: parent.pstyle('min-width-bias-right')
9174 },
9175 height: {
9176 val: parent.pstyle('min-height').pfValue,
9177 top: parent.pstyle('min-height-bias-top'),
9178 bottom: parent.pstyle('min-height-bias-bottom')
9179 }
9180 };
9181 var bb = children.boundingBox({
9182 includeLabels: includeLabels,
9183 includeOverlays: false,
9184 // updating the compound bounds happens outside of the regular
9185 // cache cycle (i.e. before fired events)
9186 useCache: false
9187 });
9188 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9189
9190 if (bb.w === 0 || bb.h === 0) {
9191 bb = {
9192 w: parent.pstyle('width').pfValue,
9193 h: parent.pstyle('height').pfValue
9194 };
9195 bb.x1 = pos.x - bb.w / 2;
9196 bb.x2 = pos.x + bb.w / 2;
9197 bb.y1 = pos.y - bb.h / 2;
9198 bb.y2 = pos.y + bb.h / 2;
9199 }
9200
9201 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9202 var biasDiff = 0;
9203 var biasComplementDiff = 0;
9204 var biasTotal = propBias + propBiasComplement;
9205
9206 if (propDiff > 0 && biasTotal > 0) {
9207 biasDiff = propBias / biasTotal * propDiff;
9208 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9209 }
9210
9211 return {
9212 biasDiff: biasDiff,
9213 biasComplementDiff: biasComplementDiff
9214 };
9215 }
9216
9217 function computePaddingValues(width, height, paddingObject, relativeTo) {
9218 // Assuming percentage is number from 0 to 1
9219 if (paddingObject.units === '%') {
9220 switch (relativeTo) {
9221 case 'width':
9222 return width > 0 ? paddingObject.pfValue * width : 0;
9223
9224 case 'height':
9225 return height > 0 ? paddingObject.pfValue * height : 0;
9226
9227 case 'average':
9228 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9229
9230 case 'min':
9231 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9232
9233 case 'max':
9234 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9235
9236 default:
9237 return 0;
9238 }
9239 } else if (paddingObject.units === 'px') {
9240 return paddingObject.pfValue;
9241 } else {
9242 return 0;
9243 }
9244 }
9245
9246 var leftVal = min.width.left.value;
9247
9248 if (min.width.left.units === 'px' && min.width.val > 0) {
9249 leftVal = leftVal * 100 / min.width.val;
9250 }
9251
9252 var rightVal = min.width.right.value;
9253
9254 if (min.width.right.units === 'px' && min.width.val > 0) {
9255 rightVal = rightVal * 100 / min.width.val;
9256 }
9257
9258 var topVal = min.height.top.value;
9259
9260 if (min.height.top.units === 'px' && min.height.val > 0) {
9261 topVal = topVal * 100 / min.height.val;
9262 }
9263
9264 var bottomVal = min.height.bottom.value;
9265
9266 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9267 bottomVal = bottomVal * 100 / min.height.val;
9268 }
9269
9270 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9271 var diffLeft = widthBiasDiffs.biasDiff;
9272 var diffRight = widthBiasDiffs.biasComplementDiff;
9273 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9274 var diffTop = heightBiasDiffs.biasDiff;
9275 var diffBottom = heightBiasDiffs.biasComplementDiff;
9276 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9277 _p.autoWidth = Math.max(bb.w, min.width.val);
9278 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9279 _p.autoHeight = Math.max(bb.h, min.height.val);
9280 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9281 }
9282
9283 for (var i = 0; i < this.length; i++) {
9284 var ele = this[i];
9285 var _p = ele._private;
9286
9287 if (!_p.compoundBoundsClean || force) {
9288 update(ele);
9289
9290 if (!cy.batching()) {
9291 _p.compoundBoundsClean = true;
9292 }
9293 }
9294 }
9295
9296 return this;
9297};
9298
9299var noninf = function noninf(x) {
9300 if (x === Infinity || x === -Infinity) {
9301 return 0;
9302 }
9303
9304 return x;
9305};
9306
9307var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9308 // don't update with zero area boxes
9309 if (x2 - x1 === 0 || y2 - y1 === 0) {
9310 return;
9311 } // don't update with null dim
9312
9313
9314 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9315 return;
9316 }
9317
9318 b.x1 = x1 < b.x1 ? x1 : b.x1;
9319 b.x2 = x2 > b.x2 ? x2 : b.x2;
9320 b.y1 = y1 < b.y1 ? y1 : b.y1;
9321 b.y2 = y2 > b.y2 ? y2 : b.y2;
9322 b.w = b.x2 - b.x1;
9323 b.h = b.y2 - b.y1;
9324};
9325
9326var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9327 if (b2 == null) {
9328 return b;
9329 }
9330
9331 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9332};
9333
9334var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9335 return getPrefixedProperty(obj, field, prefix);
9336};
9337
9338var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9339 if (ele.cy().headless()) {
9340 return;
9341 }
9342
9343 var _p = ele._private;
9344 var rstyle = _p.rstyle;
9345 var halfArW = rstyle.arrowWidth / 2;
9346 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9347 var x;
9348 var y;
9349
9350 if (arrowType !== 'none') {
9351 if (prefix === 'source') {
9352 x = rstyle.srcX;
9353 y = rstyle.srcY;
9354 } else if (prefix === 'target') {
9355 x = rstyle.tgtX;
9356 y = rstyle.tgtY;
9357 } else {
9358 x = rstyle.midX;
9359 y = rstyle.midY;
9360 } // always store the individual arrow bounds
9361
9362
9363 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9364 var bb = bbs[prefix] = bbs[prefix] || {};
9365 bb.x1 = x - halfArW;
9366 bb.y1 = y - halfArW;
9367 bb.x2 = x + halfArW;
9368 bb.y2 = y + halfArW;
9369 bb.w = bb.x2 - bb.x1;
9370 bb.h = bb.y2 - bb.y1;
9371 expandBoundingBox(bb, 1);
9372 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9373 }
9374};
9375
9376var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9377 if (ele.cy().headless()) {
9378 return;
9379 }
9380
9381 var prefixDash;
9382
9383 if (prefix) {
9384 prefixDash = prefix + '-';
9385 } else {
9386 prefixDash = '';
9387 }
9388
9389 var _p = ele._private;
9390 var rstyle = _p.rstyle;
9391 var label = ele.pstyle(prefixDash + 'label').strValue;
9392
9393 if (label) {
9394 var halign = ele.pstyle('text-halign');
9395 var valign = ele.pstyle('text-valign');
9396 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9397 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9398 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9399 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9400 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9401 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9402 var isEdge = ele.isEdge();
9403 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9404 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9405 var borderWidth = ele.pstyle('text-border-width').pfValue;
9406 var halfBorderWidth = borderWidth / 2;
9407 var padding = ele.pstyle('text-background-padding').pfValue;
9408 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9409
9410 var lh = labelHeight;
9411 var lw = labelWidth;
9412 var lw_2 = lw / 2;
9413 var lh_2 = lh / 2;
9414 var lx1, lx2, ly1, ly2;
9415
9416 if (isEdge) {
9417 lx1 = labelX - lw_2;
9418 lx2 = labelX + lw_2;
9419 ly1 = labelY - lh_2;
9420 ly2 = labelY + lh_2;
9421 } else {
9422 switch (halign.value) {
9423 case 'left':
9424 lx1 = labelX - lw;
9425 lx2 = labelX;
9426 break;
9427
9428 case 'center':
9429 lx1 = labelX - lw_2;
9430 lx2 = labelX + lw_2;
9431 break;
9432
9433 case 'right':
9434 lx1 = labelX;
9435 lx2 = labelX + lw;
9436 break;
9437 }
9438
9439 switch (valign.value) {
9440 case 'top':
9441 ly1 = labelY - lh;
9442 ly2 = labelY;
9443 break;
9444
9445 case 'center':
9446 ly1 = labelY - lh_2;
9447 ly2 = labelY + lh_2;
9448 break;
9449
9450 case 'bottom':
9451 ly1 = labelY;
9452 ly2 = labelY + lh;
9453 break;
9454 }
9455 } // shift by margin and expand by outline and border
9456
9457
9458 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9459 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9460 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9461 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9462
9463 var bbPrefix = prefix || 'main';
9464 var bbs = _p.labelBounds;
9465 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9466 bb.x1 = lx1;
9467 bb.y1 = ly1;
9468 bb.x2 = lx2;
9469 bb.y2 = ly2;
9470 bb.w = lx2 - lx1;
9471 bb.h = ly2 - ly1;
9472 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9473 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9474
9475 if (isAutorotate || isPfValue) {
9476 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9477 var cos = Math.cos(theta);
9478 var sin = Math.sin(theta); // rotation point (default value for center-center)
9479
9480 var xo = (lx1 + lx2) / 2;
9481 var yo = (ly1 + ly2) / 2;
9482
9483 if (!isEdge) {
9484 switch (halign.value) {
9485 case 'left':
9486 xo = lx2;
9487 break;
9488
9489 case 'right':
9490 xo = lx1;
9491 break;
9492 }
9493
9494 switch (valign.value) {
9495 case 'top':
9496 yo = ly2;
9497 break;
9498
9499 case 'bottom':
9500 yo = ly1;
9501 break;
9502 }
9503 }
9504
9505 var rotate = function rotate(x, y) {
9506 x = x - xo;
9507 y = y - yo;
9508 return {
9509 x: x * cos - y * sin + xo,
9510 y: x * sin + y * cos + yo
9511 };
9512 };
9513
9514 var px1y1 = rotate(lx1, ly1);
9515 var px1y2 = rotate(lx1, ly2);
9516 var px2y1 = rotate(lx2, ly1);
9517 var px2y2 = rotate(lx2, ly2);
9518 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9519 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9520 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9521 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9522 }
9523
9524 var bbPrefixRot = bbPrefix + 'Rot';
9525 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9526 bbRot.x1 = lx1;
9527 bbRot.y1 = ly1;
9528 bbRot.x2 = lx2;
9529 bbRot.y2 = ly2;
9530 bbRot.w = lx2 - lx1;
9531 bbRot.h = ly2 - ly1;
9532 updateBounds(bounds, lx1, ly1, lx2, ly2);
9533 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9534 }
9535
9536 return bounds;
9537}; // get the bounding box of the elements (in raw model position)
9538
9539
9540var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9541 var cy = ele._private.cy;
9542 var styleEnabled = cy.styleEnabled();
9543 var headless = cy.headless();
9544 var bounds = makeBoundingBox();
9545 var _p = ele._private;
9546 var isNode = ele.isNode();
9547 var isEdge = ele.isEdge();
9548 var ex1, ex2, ey1, ey2; // extrema of body / lines
9549
9550 var x, y; // node pos
9551
9552 var rstyle = _p.rstyle;
9553 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9554 // (other factors like width values will be considered later in this function anyway)
9555
9556 var isDisplayed = function isDisplayed(ele) {
9557 return ele.pstyle('display').value !== 'none';
9558 };
9559
9560 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9561 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9562
9563 if (displayed) {
9564 // displayed suffices, since we will find zero area eles anyway
9565 var overlayOpacity = 0;
9566 var overlayPadding = 0;
9567
9568 if (styleEnabled && options.includeOverlays) {
9569 overlayOpacity = ele.pstyle('overlay-opacity').value;
9570
9571 if (overlayOpacity !== 0) {
9572 overlayPadding = ele.pstyle('overlay-padding').value;
9573 }
9574 }
9575
9576 var underlayOpacity = 0;
9577 var underlayPadding = 0;
9578
9579 if (styleEnabled && options.includeUnderlays) {
9580 underlayOpacity = ele.pstyle('underlay-opacity').value;
9581
9582 if (underlayOpacity !== 0) {
9583 underlayPadding = ele.pstyle('underlay-padding').value;
9584 }
9585 }
9586
9587 var padding = Math.max(overlayPadding, underlayPadding);
9588 var w = 0;
9589 var wHalf = 0;
9590
9591 if (styleEnabled) {
9592 w = ele.pstyle('width').pfValue;
9593 wHalf = w / 2;
9594 }
9595
9596 if (isNode && options.includeNodes) {
9597 var pos = ele.position();
9598 x = pos.x;
9599 y = pos.y;
9600
9601 var _w = ele.outerWidth();
9602
9603 var halfW = _w / 2;
9604 var h = ele.outerHeight();
9605 var halfH = h / 2; // handle node dimensions
9606 /////////////////////////
9607
9608 ex1 = x - halfW;
9609 ex2 = x + halfW;
9610 ey1 = y - halfH;
9611 ey2 = y + halfH;
9612 updateBounds(bounds, ex1, ey1, ex2, ey2);
9613 } else if (isEdge && options.includeEdges) {
9614 if (styleEnabled && !headless) {
9615 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9616 //////////////////////////////////////////////
9617
9618 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9619 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9620 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9621 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9622
9623 ex1 -= wHalf;
9624 ex2 += wHalf;
9625 ey1 -= wHalf;
9626 ey2 += wHalf;
9627 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9628 ////////////////
9629
9630 if (curveStyle === 'haystack') {
9631 var hpts = rstyle.haystackPts;
9632
9633 if (hpts && hpts.length === 2) {
9634 ex1 = hpts[0].x;
9635 ey1 = hpts[0].y;
9636 ex2 = hpts[1].x;
9637 ey2 = hpts[1].y;
9638
9639 if (ex1 > ex2) {
9640 var temp = ex1;
9641 ex1 = ex2;
9642 ex2 = temp;
9643 }
9644
9645 if (ey1 > ey2) {
9646 var _temp = ey1;
9647 ey1 = ey2;
9648 ey2 = _temp;
9649 }
9650
9651 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9652 }
9653 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9654 var pts;
9655
9656 switch (curveStyle) {
9657 case 'bezier':
9658 case 'unbundled-bezier':
9659 pts = rstyle.bezierPts;
9660 break;
9661
9662 case 'segments':
9663 case 'taxi':
9664 pts = rstyle.linePts;
9665 break;
9666 }
9667
9668 if (pts != null) {
9669 for (var j = 0; j < pts.length; j++) {
9670 var pt = pts[j];
9671 ex1 = pt.x - wHalf;
9672 ex2 = pt.x + wHalf;
9673 ey1 = pt.y - wHalf;
9674 ey2 = pt.y + wHalf;
9675 updateBounds(bounds, ex1, ey1, ex2, ey2);
9676 }
9677 }
9678 } // bezier-like or segment-like edge
9679
9680 } else {
9681 // headless or style disabled
9682 // fallback on source and target positions
9683 //////////////////////////////////////////
9684 var n1 = ele.source();
9685 var n1pos = n1.position();
9686 var n2 = ele.target();
9687 var n2pos = n2.position();
9688 ex1 = n1pos.x;
9689 ex2 = n2pos.x;
9690 ey1 = n1pos.y;
9691 ey2 = n2pos.y;
9692
9693 if (ex1 > ex2) {
9694 var _temp2 = ex1;
9695 ex1 = ex2;
9696 ex2 = _temp2;
9697 }
9698
9699 if (ey1 > ey2) {
9700 var _temp3 = ey1;
9701 ey1 = ey2;
9702 ey2 = _temp3;
9703 } // take into account edge width
9704
9705
9706 ex1 -= wHalf;
9707 ex2 += wHalf;
9708 ey1 -= wHalf;
9709 ey2 += wHalf;
9710 updateBounds(bounds, ex1, ey1, ex2, ey2);
9711 } // headless or style disabled
9712
9713 } // edges
9714 // handle edge arrow size
9715 /////////////////////////
9716
9717
9718 if (styleEnabled && options.includeEdges && isEdge) {
9719 updateBoundsFromArrow(bounds, ele, 'mid-source');
9720 updateBoundsFromArrow(bounds, ele, 'mid-target');
9721 updateBoundsFromArrow(bounds, ele, 'source');
9722 updateBoundsFromArrow(bounds, ele, 'target');
9723 } // ghost
9724 ////////
9725
9726
9727 if (styleEnabled) {
9728 var ghost = ele.pstyle('ghost').value === 'yes';
9729
9730 if (ghost) {
9731 var gx = ele.pstyle('ghost-offset-x').pfValue;
9732 var gy = ele.pstyle('ghost-offset-y').pfValue;
9733 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9734 }
9735 } // always store the body bounds separately from the labels
9736
9737
9738 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9739 assignBoundingBox(bbBody, bounds);
9740 expandBoundingBoxSides(bbBody, manualExpansion);
9741 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9742 // overlay
9743 //////////
9744
9745 if (styleEnabled) {
9746 ex1 = bounds.x1;
9747 ex2 = bounds.x2;
9748 ey1 = bounds.y1;
9749 ey2 = bounds.y2;
9750 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
9751 } // always store the body bounds separately from the labels
9752
9753
9754 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9755 assignBoundingBox(bbOverlay, bounds);
9756 expandBoundingBoxSides(bbOverlay, manualExpansion);
9757 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9758 // handle label dimensions
9759 //////////////////////////
9760
9761 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9762
9763 if (bbLabels.all != null) {
9764 clearBoundingBox(bbLabels.all);
9765 } else {
9766 bbLabels.all = makeBoundingBox();
9767 }
9768
9769 if (styleEnabled && options.includeLabels) {
9770 if (options.includeMainLabels) {
9771 updateBoundsFromLabel(bounds, ele, null);
9772 }
9773
9774 if (isEdge) {
9775 if (options.includeSourceLabels) {
9776 updateBoundsFromLabel(bounds, ele, 'source');
9777 }
9778
9779 if (options.includeTargetLabels) {
9780 updateBoundsFromLabel(bounds, ele, 'target');
9781 }
9782 }
9783 } // style enabled for labels
9784
9785 } // if displayed
9786
9787
9788 bounds.x1 = noninf(bounds.x1);
9789 bounds.y1 = noninf(bounds.y1);
9790 bounds.x2 = noninf(bounds.x2);
9791 bounds.y2 = noninf(bounds.y2);
9792 bounds.w = noninf(bounds.x2 - bounds.x1);
9793 bounds.h = noninf(bounds.y2 - bounds.y1);
9794
9795 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9796 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9797
9798 expandBoundingBox(bounds, 1);
9799 }
9800
9801 return bounds;
9802};
9803
9804var getKey = function getKey(opts) {
9805 var i = 0;
9806
9807 var tf = function tf(val) {
9808 return (val ? 1 : 0) << i++;
9809 };
9810
9811 var key = 0;
9812 key += tf(opts.incudeNodes);
9813 key += tf(opts.includeEdges);
9814 key += tf(opts.includeLabels);
9815 key += tf(opts.includeMainLabels);
9816 key += tf(opts.includeSourceLabels);
9817 key += tf(opts.includeTargetLabels);
9818 key += tf(opts.includeOverlays);
9819 return key;
9820};
9821
9822var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9823 if (ele.isEdge()) {
9824 var p1 = ele.source().position();
9825 var p2 = ele.target().position();
9826
9827 var r = function r(x) {
9828 return Math.round(x);
9829 };
9830
9831 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9832 } else {
9833 return 0;
9834 }
9835};
9836
9837var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9838 var _p = ele._private;
9839 var bb;
9840 var isEdge = ele.isEdge();
9841 var key = opts == null ? defBbOptsKey : getKey(opts);
9842 var usingDefOpts = key === defBbOptsKey;
9843 var currPosKey = getBoundingBoxPosKey(ele);
9844 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9845 var useCache = opts.useCache && isPosKeySame;
9846
9847 var isDirty = function isDirty(ele) {
9848 return ele._private.bbCache == null || ele._private.styleDirty;
9849 };
9850
9851 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9852
9853 if (needRecalc) {
9854 if (!isPosKeySame) {
9855 ele.recalculateRenderedStyle(useCache);
9856 }
9857
9858 bb = boundingBoxImpl(ele, defBbOpts);
9859 _p.bbCache = bb;
9860 _p.bbCachePosKey = currPosKey;
9861 } else {
9862 bb = _p.bbCache;
9863 } // not using def opts => need to build up bb from combination of sub bbs
9864
9865
9866 if (!usingDefOpts) {
9867 var isNode = ele.isNode();
9868 bb = makeBoundingBox();
9869
9870 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9871 if (opts.includeOverlays) {
9872 updateBoundsFromBox(bb, _p.overlayBounds);
9873 } else {
9874 updateBoundsFromBox(bb, _p.bodyBounds);
9875 }
9876 }
9877
9878 if (opts.includeLabels) {
9879 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9880 updateBoundsFromBox(bb, _p.labelBounds.all);
9881 } else {
9882 if (opts.includeMainLabels) {
9883 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9884 }
9885
9886 if (opts.includeSourceLabels) {
9887 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9888 }
9889
9890 if (opts.includeTargetLabels) {
9891 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9892 }
9893 }
9894 }
9895
9896 bb.w = bb.x2 - bb.x1;
9897 bb.h = bb.y2 - bb.y1;
9898 }
9899
9900 return bb;
9901};
9902
9903var defBbOpts = {
9904 includeNodes: true,
9905 includeEdges: true,
9906 includeLabels: true,
9907 includeMainLabels: true,
9908 includeSourceLabels: true,
9909 includeTargetLabels: true,
9910 includeOverlays: true,
9911 includeUnderlays: true,
9912 useCache: true
9913};
9914var defBbOptsKey = getKey(defBbOpts);
9915var filledBbOpts = defaults(defBbOpts);
9916
9917elesfn$k.boundingBox = function (options) {
9918 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9919 // specified s.t. the cache is used, so check for this case to make it faster by
9920 // avoiding the overhead of the rest of the function
9921
9922 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9923 if (options === undefined) {
9924 options = defBbOpts;
9925 } else {
9926 options = filledBbOpts(options);
9927 }
9928
9929 bounds = cachedBoundingBoxImpl(this[0], options);
9930 } else {
9931 bounds = makeBoundingBox();
9932 options = options || defBbOpts;
9933 var opts = filledBbOpts(options);
9934 var eles = this;
9935 var cy = eles.cy();
9936 var styleEnabled = cy.styleEnabled();
9937
9938 if (styleEnabled) {
9939 for (var i = 0; i < eles.length; i++) {
9940 var ele = eles[i];
9941 var _p = ele._private;
9942 var currPosKey = getBoundingBoxPosKey(ele);
9943 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9944 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9945 ele.recalculateRenderedStyle(useCache);
9946 }
9947 }
9948
9949 this.updateCompoundBounds(!options.useCache);
9950
9951 for (var _i = 0; _i < eles.length; _i++) {
9952 var _ele = eles[_i];
9953 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9954 }
9955 }
9956
9957 bounds.x1 = noninf(bounds.x1);
9958 bounds.y1 = noninf(bounds.y1);
9959 bounds.x2 = noninf(bounds.x2);
9960 bounds.y2 = noninf(bounds.y2);
9961 bounds.w = noninf(bounds.x2 - bounds.x1);
9962 bounds.h = noninf(bounds.y2 - bounds.y1);
9963 return bounds;
9964};
9965
9966elesfn$k.dirtyBoundingBoxCache = function () {
9967 for (var i = 0; i < this.length; i++) {
9968 var _p = this[i]._private;
9969 _p.bbCache = null;
9970 _p.bbCachePosKey = null;
9971 _p.bodyBounds = null;
9972 _p.overlayBounds = null;
9973 _p.labelBounds.all = null;
9974 _p.labelBounds.source = null;
9975 _p.labelBounds.target = null;
9976 _p.labelBounds.main = null;
9977 _p.labelBounds.sourceRot = null;
9978 _p.labelBounds.targetRot = null;
9979 _p.labelBounds.mainRot = null;
9980 _p.arrowBounds.source = null;
9981 _p.arrowBounds.target = null;
9982 _p.arrowBounds['mid-source'] = null;
9983 _p.arrowBounds['mid-target'] = null;
9984 }
9985
9986 this.emitAndNotify('bounds');
9987 return this;
9988}; // private helper to get bounding box for custom node positions
9989// - good for perf in certain cases but currently requires dirtying the rendered style
9990// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9991// - try to use for only things like discrete layouts where the node position would change anyway
9992
9993
9994elesfn$k.boundingBoxAt = function (fn) {
9995 var nodes = this.nodes();
9996 var cy = this.cy();
9997 var hasCompoundNodes = cy.hasCompoundNodes();
9998 var parents = cy.collection();
9999
10000 if (hasCompoundNodes) {
10001 parents = nodes.filter(function (node) {
10002 return node.isParent();
10003 });
10004 nodes = nodes.not(parents);
10005 }
10006
10007 if (plainObject(fn)) {
10008 var obj = fn;
10009
10010 fn = function fn() {
10011 return obj;
10012 };
10013 }
10014
10015 var storeOldPos = function storeOldPos(node, i) {
10016 return node._private.bbAtOldPos = fn(node, i);
10017 };
10018
10019 var getOldPos = function getOldPos(node) {
10020 return node._private.bbAtOldPos;
10021 };
10022
10023 cy.startBatch();
10024 nodes.forEach(storeOldPos).silentPositions(fn);
10025
10026 if (hasCompoundNodes) {
10027 parents.dirtyCompoundBoundsCache();
10028 parents.dirtyBoundingBoxCache();
10029 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10030 }
10031
10032 var bb = copyBoundingBox(this.boundingBox({
10033 useCache: false
10034 }));
10035 nodes.silentPositions(getOldPos);
10036
10037 if (hasCompoundNodes) {
10038 parents.dirtyCompoundBoundsCache();
10039 parents.dirtyBoundingBoxCache();
10040 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10041 }
10042
10043 cy.endBatch();
10044 return bb;
10045};
10046
10047fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10048fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10049var bounds = elesfn$k;
10050
10051var fn$4, elesfn$l;
10052fn$4 = elesfn$l = {};
10053
10054var defineDimFns = function defineDimFns(opts) {
10055 opts.uppercaseName = capitalize(opts.name);
10056 opts.autoName = 'auto' + opts.uppercaseName;
10057 opts.labelName = 'label' + opts.uppercaseName;
10058 opts.outerName = 'outer' + opts.uppercaseName;
10059 opts.uppercaseOuterName = capitalize(opts.outerName);
10060
10061 fn$4[opts.name] = function dimImpl() {
10062 var ele = this[0];
10063 var _p = ele._private;
10064 var cy = _p.cy;
10065 var styleEnabled = cy._private.styleEnabled;
10066
10067 if (ele) {
10068 if (styleEnabled) {
10069 if (ele.isParent()) {
10070 ele.updateCompoundBounds();
10071 return _p[opts.autoName] || 0;
10072 }
10073
10074 var d = ele.pstyle(opts.name);
10075
10076 switch (d.strValue) {
10077 case 'label':
10078 ele.recalculateRenderedStyle();
10079 return _p.rstyle[opts.labelName] || 0;
10080
10081 default:
10082 return d.pfValue;
10083 }
10084 } else {
10085 return 1;
10086 }
10087 }
10088 };
10089
10090 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10091 var ele = this[0];
10092 var _p = ele._private;
10093 var cy = _p.cy;
10094 var styleEnabled = cy._private.styleEnabled;
10095
10096 if (ele) {
10097 if (styleEnabled) {
10098 var dim = ele[opts.name]();
10099 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10100
10101 var padding = 2 * ele.padding();
10102 return dim + border + padding;
10103 } else {
10104 return 1;
10105 }
10106 }
10107 };
10108
10109 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10110 var ele = this[0];
10111
10112 if (ele) {
10113 var d = ele[opts.name]();
10114 return d * this.cy().zoom();
10115 }
10116 };
10117
10118 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10119 var ele = this[0];
10120
10121 if (ele) {
10122 var od = ele[opts.outerName]();
10123 return od * this.cy().zoom();
10124 }
10125 };
10126};
10127
10128defineDimFns({
10129 name: 'width'
10130});
10131defineDimFns({
10132 name: 'height'
10133});
10134
10135elesfn$l.padding = function () {
10136 var ele = this[0];
10137 var _p = ele._private;
10138
10139 if (ele.isParent()) {
10140 ele.updateCompoundBounds();
10141
10142 if (_p.autoPadding !== undefined) {
10143 return _p.autoPadding;
10144 } else {
10145 return ele.pstyle('padding').pfValue;
10146 }
10147 } else {
10148 return ele.pstyle('padding').pfValue;
10149 }
10150};
10151
10152elesfn$l.paddedHeight = function () {
10153 var ele = this[0];
10154 return ele.height() + 2 * ele.padding();
10155};
10156
10157elesfn$l.paddedWidth = function () {
10158 var ele = this[0];
10159 return ele.width() + 2 * ele.padding();
10160};
10161
10162var widthHeight = elesfn$l;
10163
10164var ifEdge = function ifEdge(ele, getValue) {
10165 if (ele.isEdge()) {
10166 return getValue(ele);
10167 }
10168};
10169
10170var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10171 if (ele.isEdge()) {
10172 var cy = ele.cy();
10173 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10174 }
10175};
10176
10177var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10178 if (ele.isEdge()) {
10179 var cy = ele.cy();
10180 var pan = cy.pan();
10181 var zoom = cy.zoom();
10182 return getPoints(ele).map(function (p) {
10183 return modelToRenderedPosition(p, zoom, pan);
10184 });
10185 }
10186};
10187
10188var controlPoints = function controlPoints(ele) {
10189 return ele.renderer().getControlPoints(ele);
10190};
10191
10192var segmentPoints = function segmentPoints(ele) {
10193 return ele.renderer().getSegmentPoints(ele);
10194};
10195
10196var sourceEndpoint = function sourceEndpoint(ele) {
10197 return ele.renderer().getSourceEndpoint(ele);
10198};
10199
10200var targetEndpoint = function targetEndpoint(ele) {
10201 return ele.renderer().getTargetEndpoint(ele);
10202};
10203
10204var midpoint = function midpoint(ele) {
10205 return ele.renderer().getEdgeMidpoint(ele);
10206};
10207
10208var pts = {
10209 controlPoints: {
10210 get: controlPoints,
10211 mult: true
10212 },
10213 segmentPoints: {
10214 get: segmentPoints,
10215 mult: true
10216 },
10217 sourceEndpoint: {
10218 get: sourceEndpoint
10219 },
10220 targetEndpoint: {
10221 get: targetEndpoint
10222 },
10223 midpoint: {
10224 get: midpoint
10225 }
10226};
10227
10228var renderedName = function renderedName(name) {
10229 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10230};
10231
10232var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10233 var spec = pts[name];
10234 var rName = renderedName(name);
10235
10236 obj[name] = function () {
10237 return ifEdge(this, spec.get);
10238 };
10239
10240 if (spec.mult) {
10241 obj[rName] = function () {
10242 return ifEdgeRenderedPositions(this, spec.get);
10243 };
10244 } else {
10245 obj[rName] = function () {
10246 return ifEdgeRenderedPosition(this, spec.get);
10247 };
10248 }
10249
10250 return obj;
10251}, {});
10252
10253var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10254
10255/*!
10256Event object based on jQuery events, MIT license
10257
10258https://jquery.org/license/
10259https://tldrlegal.com/license/mit-license
10260https://github.com/jquery/jquery/blob/master/src/event.js
10261*/
10262var Event = function Event(src, props) {
10263 this.recycle(src, props);
10264};
10265
10266function returnFalse() {
10267 return false;
10268}
10269
10270function returnTrue() {
10271 return true;
10272} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10273
10274
10275Event.prototype = {
10276 instanceString: function instanceString() {
10277 return 'event';
10278 },
10279 recycle: function recycle(src, props) {
10280 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10281
10282 if (src != null && src.preventDefault) {
10283 // Browser Event object
10284 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10285 // by a handler lower down the tree; reflect the correct value.
10286
10287 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10288 } else if (src != null && src.type) {
10289 // Plain object containing all event details
10290 props = src;
10291 } else {
10292 // Event string
10293 this.type = src;
10294 } // Put explicitly provided properties onto the event object
10295
10296
10297 if (props != null) {
10298 // more efficient to manually copy fields we use
10299 this.originalEvent = props.originalEvent;
10300 this.type = props.type != null ? props.type : this.type;
10301 this.cy = props.cy;
10302 this.target = props.target;
10303 this.position = props.position;
10304 this.renderedPosition = props.renderedPosition;
10305 this.namespace = props.namespace;
10306 this.layout = props.layout;
10307 }
10308
10309 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10310 // create a rendered position based on the passed position
10311 var pos = this.position;
10312 var zoom = this.cy.zoom();
10313 var pan = this.cy.pan();
10314 this.renderedPosition = {
10315 x: pos.x * zoom + pan.x,
10316 y: pos.y * zoom + pan.y
10317 };
10318 } // Create a timestamp if incoming event doesn't have one
10319
10320
10321 this.timeStamp = src && src.timeStamp || Date.now();
10322 },
10323 preventDefault: function preventDefault() {
10324 this.isDefaultPrevented = returnTrue;
10325 var e = this.originalEvent;
10326
10327 if (!e) {
10328 return;
10329 } // if preventDefault exists run it on the original event
10330
10331
10332 if (e.preventDefault) {
10333 e.preventDefault();
10334 }
10335 },
10336 stopPropagation: function stopPropagation() {
10337 this.isPropagationStopped = returnTrue;
10338 var e = this.originalEvent;
10339
10340 if (!e) {
10341 return;
10342 } // if stopPropagation exists run it on the original event
10343
10344
10345 if (e.stopPropagation) {
10346 e.stopPropagation();
10347 }
10348 },
10349 stopImmediatePropagation: function stopImmediatePropagation() {
10350 this.isImmediatePropagationStopped = returnTrue;
10351 this.stopPropagation();
10352 },
10353 isDefaultPrevented: returnFalse,
10354 isPropagationStopped: returnFalse,
10355 isImmediatePropagationStopped: returnFalse
10356};
10357
10358var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10359
10360var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10361
10362var defaults$8 = {
10363 qualifierCompare: function qualifierCompare(q1, q2) {
10364 return q1 === q2;
10365 },
10366 eventMatches: function eventMatches()
10367 /*context, listener, eventObj*/
10368 {
10369 return true;
10370 },
10371 addEventFields: function addEventFields()
10372 /*context, evt*/
10373 {},
10374 callbackContext: function callbackContext(context
10375 /*, listener, eventObj*/
10376 ) {
10377 return context;
10378 },
10379 beforeEmit: function beforeEmit()
10380 /* context, listener, eventObj */
10381 {},
10382 afterEmit: function afterEmit()
10383 /* context, listener, eventObj */
10384 {},
10385 bubble: function bubble()
10386 /*context*/
10387 {
10388 return false;
10389 },
10390 parent: function parent()
10391 /*context*/
10392 {
10393 return null;
10394 },
10395 context: null
10396};
10397var defaultsKeys = Object.keys(defaults$8);
10398var emptyOpts = {};
10399
10400function Emitter() {
10401 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10402 var context = arguments.length > 1 ? arguments[1] : undefined;
10403
10404 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10405 for (var i = 0; i < defaultsKeys.length; i++) {
10406 var key = defaultsKeys[i];
10407 this[key] = opts[key] || defaults$8[key];
10408 }
10409
10410 this.context = context || this.context;
10411 this.listeners = [];
10412 this.emitting = 0;
10413}
10414
10415var p = Emitter.prototype;
10416
10417var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10418 if (fn(qualifier)) {
10419 callback = qualifier;
10420 qualifier = null;
10421 }
10422
10423 if (confOverrides) {
10424 if (conf == null) {
10425 conf = confOverrides;
10426 } else {
10427 conf = extend({}, conf, confOverrides);
10428 }
10429 }
10430
10431 var eventList = array(events) ? events : events.split(/\s+/);
10432
10433 for (var i = 0; i < eventList.length; i++) {
10434 var evt = eventList[i];
10435
10436 if (emptyString(evt)) {
10437 continue;
10438 }
10439
10440 var match = evt.match(eventRegex); // type[.namespace]
10441
10442 if (match) {
10443 var type = match[1];
10444 var namespace = match[2] ? match[2] : null;
10445 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10446
10447 if (ret === false) {
10448 break;
10449 } // allow exiting early
10450
10451 }
10452 }
10453};
10454
10455var makeEventObj = function makeEventObj(self, obj) {
10456 self.addEventFields(self.context, obj);
10457 return new Event(obj.type, obj);
10458};
10459
10460var forEachEventObj = function forEachEventObj(self, handler, events) {
10461 if (event(events)) {
10462 handler(self, events);
10463 return;
10464 } else if (plainObject(events)) {
10465 handler(self, makeEventObj(self, events));
10466 return;
10467 }
10468
10469 var eventList = array(events) ? events : events.split(/\s+/);
10470
10471 for (var i = 0; i < eventList.length; i++) {
10472 var evt = eventList[i];
10473
10474 if (emptyString(evt)) {
10475 continue;
10476 }
10477
10478 var match = evt.match(eventRegex); // type[.namespace]
10479
10480 if (match) {
10481 var type = match[1];
10482 var namespace = match[2] ? match[2] : null;
10483 var eventObj = makeEventObj(self, {
10484 type: type,
10485 namespace: namespace,
10486 target: self.context
10487 });
10488 handler(self, eventObj);
10489 }
10490 }
10491};
10492
10493p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10494 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10495 if (fn(callback)) {
10496 self.listeners.push({
10497 event: event,
10498 // full event string
10499 callback: callback,
10500 // callback to run
10501 type: type,
10502 // the event type (e.g. 'click')
10503 namespace: namespace,
10504 // the event namespace (e.g. ".foo")
10505 qualifier: qualifier,
10506 // a restriction on whether to match this emitter
10507 conf: conf // additional configuration
10508
10509 });
10510 }
10511 }, events, qualifier, callback, conf, confOverrides);
10512 return this;
10513};
10514
10515p.one = function (events, qualifier, callback, conf) {
10516 return this.on(events, qualifier, callback, conf, {
10517 one: true
10518 });
10519};
10520
10521p.removeListener = p.off = function (events, qualifier, callback, conf) {
10522 var _this = this;
10523
10524 if (this.emitting !== 0) {
10525 this.listeners = copyArray(this.listeners);
10526 }
10527
10528 var listeners = this.listeners;
10529
10530 var _loop = function _loop(i) {
10531 var listener = listeners[i];
10532 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10533 /*, conf*/
10534 ) {
10535 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10536 listeners.splice(i, 1);
10537 return false;
10538 }
10539 }, events, qualifier, callback, conf);
10540 };
10541
10542 for (var i = listeners.length - 1; i >= 0; i--) {
10543 _loop(i);
10544 }
10545
10546 return this;
10547};
10548
10549p.removeAllListeners = function () {
10550 return this.removeListener('*');
10551};
10552
10553p.emit = p.trigger = function (events, extraParams, manualCallback) {
10554 var listeners = this.listeners;
10555 var numListenersBeforeEmit = listeners.length;
10556 this.emitting++;
10557
10558 if (!array(extraParams)) {
10559 extraParams = [extraParams];
10560 }
10561
10562 forEachEventObj(this, function (self, eventObj) {
10563 if (manualCallback != null) {
10564 listeners = [{
10565 event: eventObj.event,
10566 type: eventObj.type,
10567 namespace: eventObj.namespace,
10568 callback: manualCallback
10569 }];
10570 numListenersBeforeEmit = listeners.length;
10571 }
10572
10573 var _loop2 = function _loop2(i) {
10574 var listener = listeners[i];
10575
10576 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10577 var args = [eventObj];
10578
10579 if (extraParams != null) {
10580 push(args, extraParams);
10581 }
10582
10583 self.beforeEmit(self.context, listener, eventObj);
10584
10585 if (listener.conf && listener.conf.one) {
10586 self.listeners = self.listeners.filter(function (l) {
10587 return l !== listener;
10588 });
10589 }
10590
10591 var context = self.callbackContext(self.context, listener, eventObj);
10592 var ret = listener.callback.apply(context, args);
10593 self.afterEmit(self.context, listener, eventObj);
10594
10595 if (ret === false) {
10596 eventObj.stopPropagation();
10597 eventObj.preventDefault();
10598 }
10599 } // if listener matches
10600
10601 };
10602
10603 for (var i = 0; i < numListenersBeforeEmit; i++) {
10604 _loop2(i);
10605 } // for listener
10606
10607
10608 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10609 self.parent(self.context).emit(eventObj, extraParams);
10610 }
10611 }, events);
10612 this.emitting--;
10613 return this;
10614};
10615
10616var emitterOptions = {
10617 qualifierCompare: function qualifierCompare(selector1, selector2) {
10618 if (selector1 == null || selector2 == null) {
10619 return selector1 == null && selector2 == null;
10620 } else {
10621 return selector1.sameText(selector2);
10622 }
10623 },
10624 eventMatches: function eventMatches(ele, listener, eventObj) {
10625 var selector = listener.qualifier;
10626
10627 if (selector != null) {
10628 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10629 }
10630
10631 return true;
10632 },
10633 addEventFields: function addEventFields(ele, evt) {
10634 evt.cy = ele.cy();
10635 evt.target = ele;
10636 },
10637 callbackContext: function callbackContext(ele, listener, eventObj) {
10638 return listener.qualifier != null ? eventObj.target : ele;
10639 },
10640 beforeEmit: function beforeEmit(context, listener
10641 /*, eventObj*/
10642 ) {
10643 if (listener.conf && listener.conf.once) {
10644 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10645 }
10646 },
10647 bubble: function bubble() {
10648 return true;
10649 },
10650 parent: function parent(ele) {
10651 return ele.isChild() ? ele.parent() : ele.cy();
10652 }
10653};
10654
10655var argSelector = function argSelector(arg) {
10656 if (string(arg)) {
10657 return new Selector(arg);
10658 } else {
10659 return arg;
10660 }
10661};
10662
10663var elesfn$m = {
10664 createEmitter: function createEmitter() {
10665 for (var i = 0; i < this.length; i++) {
10666 var ele = this[i];
10667 var _p = ele._private;
10668
10669 if (!_p.emitter) {
10670 _p.emitter = new Emitter(emitterOptions, ele);
10671 }
10672 }
10673
10674 return this;
10675 },
10676 emitter: function emitter() {
10677 return this._private.emitter;
10678 },
10679 on: function on(events, selector, callback) {
10680 var argSel = argSelector(selector);
10681
10682 for (var i = 0; i < this.length; i++) {
10683 var ele = this[i];
10684 ele.emitter().on(events, argSel, callback);
10685 }
10686
10687 return this;
10688 },
10689 removeListener: function removeListener(events, selector, callback) {
10690 var argSel = argSelector(selector);
10691
10692 for (var i = 0; i < this.length; i++) {
10693 var ele = this[i];
10694 ele.emitter().removeListener(events, argSel, callback);
10695 }
10696
10697 return this;
10698 },
10699 removeAllListeners: function removeAllListeners() {
10700 for (var i = 0; i < this.length; i++) {
10701 var ele = this[i];
10702 ele.emitter().removeAllListeners();
10703 }
10704
10705 return this;
10706 },
10707 one: function one(events, selector, callback) {
10708 var argSel = argSelector(selector);
10709
10710 for (var i = 0; i < this.length; i++) {
10711 var ele = this[i];
10712 ele.emitter().one(events, argSel, callback);
10713 }
10714
10715 return this;
10716 },
10717 once: function once(events, selector, callback) {
10718 var argSel = argSelector(selector);
10719
10720 for (var i = 0; i < this.length; i++) {
10721 var ele = this[i];
10722 ele.emitter().on(events, argSel, callback, {
10723 once: true,
10724 onceCollection: this
10725 });
10726 }
10727 },
10728 emit: function emit(events, extraParams) {
10729 for (var i = 0; i < this.length; i++) {
10730 var ele = this[i];
10731 ele.emitter().emit(events, extraParams);
10732 }
10733
10734 return this;
10735 },
10736 emitAndNotify: function emitAndNotify(event, extraParams) {
10737 // for internal use only
10738 if (this.length === 0) {
10739 return;
10740 } // empty collections don't need to notify anything
10741 // notify renderer
10742
10743
10744 this.cy().notify(event, this);
10745 this.emit(event, extraParams);
10746 return this;
10747 }
10748};
10749define$3.eventAliasesOn(elesfn$m);
10750
10751var elesfn$n = {
10752 nodes: function nodes(selector) {
10753 return this.filter(function (ele) {
10754 return ele.isNode();
10755 }).filter(selector);
10756 },
10757 edges: function edges(selector) {
10758 return this.filter(function (ele) {
10759 return ele.isEdge();
10760 }).filter(selector);
10761 },
10762 // internal helper to get nodes and edges as separate collections with single iteration over elements
10763 byGroup: function byGroup() {
10764 var nodes = this.spawn();
10765 var edges = this.spawn();
10766
10767 for (var i = 0; i < this.length; i++) {
10768 var ele = this[i];
10769
10770 if (ele.isNode()) {
10771 nodes.push(ele);
10772 } else {
10773 edges.push(ele);
10774 }
10775 }
10776
10777 return {
10778 nodes: nodes,
10779 edges: edges
10780 };
10781 },
10782 filter: function filter(_filter, thisArg) {
10783 if (_filter === undefined) {
10784 // check this first b/c it's the most common/performant case
10785 return this;
10786 } else if (string(_filter) || elementOrCollection(_filter)) {
10787 return new Selector(_filter).filter(this);
10788 } else if (fn(_filter)) {
10789 var filterEles = this.spawn();
10790 var eles = this;
10791
10792 for (var i = 0; i < eles.length; i++) {
10793 var ele = eles[i];
10794 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10795
10796 if (include) {
10797 filterEles.push(ele);
10798 }
10799 }
10800
10801 return filterEles;
10802 }
10803
10804 return this.spawn(); // if not handled by above, give 'em an empty collection
10805 },
10806 not: function not(toRemove) {
10807 if (!toRemove) {
10808 return this;
10809 } else {
10810 if (string(toRemove)) {
10811 toRemove = this.filter(toRemove);
10812 }
10813
10814 var elements = this.spawn();
10815
10816 for (var i = 0; i < this.length; i++) {
10817 var element = this[i];
10818 var remove = toRemove.has(element);
10819
10820 if (!remove) {
10821 elements.push(element);
10822 }
10823 }
10824
10825 return elements;
10826 }
10827 },
10828 absoluteComplement: function absoluteComplement() {
10829 var cy = this.cy();
10830 return cy.mutableElements().not(this);
10831 },
10832 intersect: function intersect(other) {
10833 // if a selector is specified, then filter by it instead
10834 if (string(other)) {
10835 var selector = other;
10836 return this.filter(selector);
10837 }
10838
10839 var elements = this.spawn();
10840 var col1 = this;
10841 var col2 = other;
10842 var col1Smaller = this.length < other.length;
10843 var colS = col1Smaller ? col1 : col2;
10844 var colL = col1Smaller ? col2 : col1;
10845
10846 for (var i = 0; i < colS.length; i++) {
10847 var ele = colS[i];
10848
10849 if (colL.has(ele)) {
10850 elements.push(ele);
10851 }
10852 }
10853
10854 return elements;
10855 },
10856 xor: function xor(other) {
10857 var cy = this._private.cy;
10858
10859 if (string(other)) {
10860 other = cy.$(other);
10861 }
10862
10863 var elements = this.spawn();
10864 var col1 = this;
10865 var col2 = other;
10866
10867 var add = function add(col, other) {
10868 for (var i = 0; i < col.length; i++) {
10869 var ele = col[i];
10870 var id = ele._private.data.id;
10871 var inOther = other.hasElementWithId(id);
10872
10873 if (!inOther) {
10874 elements.push(ele);
10875 }
10876 }
10877 };
10878
10879 add(col1, col2);
10880 add(col2, col1);
10881 return elements;
10882 },
10883 diff: function diff(other) {
10884 var cy = this._private.cy;
10885
10886 if (string(other)) {
10887 other = cy.$(other);
10888 }
10889
10890 var left = this.spawn();
10891 var right = this.spawn();
10892 var both = this.spawn();
10893 var col1 = this;
10894 var col2 = other;
10895
10896 var add = function add(col, other, retEles) {
10897 for (var i = 0; i < col.length; i++) {
10898 var ele = col[i];
10899 var id = ele._private.data.id;
10900 var inOther = other.hasElementWithId(id);
10901
10902 if (inOther) {
10903 both.merge(ele);
10904 } else {
10905 retEles.push(ele);
10906 }
10907 }
10908 };
10909
10910 add(col1, col2, left);
10911 add(col2, col1, right);
10912 return {
10913 left: left,
10914 right: right,
10915 both: both
10916 };
10917 },
10918 add: function add(toAdd) {
10919 var cy = this._private.cy;
10920
10921 if (!toAdd) {
10922 return this;
10923 }
10924
10925 if (string(toAdd)) {
10926 var selector = toAdd;
10927 toAdd = cy.mutableElements().filter(selector);
10928 }
10929
10930 var elements = this.spawnSelf();
10931
10932 for (var i = 0; i < toAdd.length; i++) {
10933 var ele = toAdd[i];
10934 var add = !this.has(ele);
10935
10936 if (add) {
10937 elements.push(ele);
10938 }
10939 }
10940
10941 return elements;
10942 },
10943 // in place merge on calling collection
10944 merge: function merge(toAdd) {
10945 var _p = this._private;
10946 var cy = _p.cy;
10947
10948 if (!toAdd) {
10949 return this;
10950 }
10951
10952 if (toAdd && string(toAdd)) {
10953 var selector = toAdd;
10954 toAdd = cy.mutableElements().filter(selector);
10955 }
10956
10957 var map = _p.map;
10958
10959 for (var i = 0; i < toAdd.length; i++) {
10960 var toAddEle = toAdd[i];
10961 var id = toAddEle._private.data.id;
10962 var add = !map.has(id);
10963
10964 if (add) {
10965 var index = this.length++;
10966 this[index] = toAddEle;
10967 map.set(id, {
10968 ele: toAddEle,
10969 index: index
10970 });
10971 }
10972 }
10973
10974 return this; // chaining
10975 },
10976 unmergeAt: function unmergeAt(i) {
10977 var ele = this[i];
10978 var id = ele.id();
10979 var _p = this._private;
10980 var map = _p.map; // remove ele
10981
10982 this[i] = undefined;
10983 map["delete"](id);
10984 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10985
10986 if (this.length > 1 && !unmergedLastEle) {
10987 var lastEleI = this.length - 1;
10988 var lastEle = this[lastEleI];
10989 var lastEleId = lastEle._private.data.id;
10990 this[lastEleI] = undefined;
10991 this[i] = lastEle;
10992 map.set(lastEleId, {
10993 ele: lastEle,
10994 index: i
10995 });
10996 } // the collection is now 1 ele smaller
10997
10998
10999 this.length--;
11000 return this;
11001 },
11002 // remove single ele in place in calling collection
11003 unmergeOne: function unmergeOne(ele) {
11004 ele = ele[0];
11005 var _p = this._private;
11006 var id = ele._private.data.id;
11007 var map = _p.map;
11008 var entry = map.get(id);
11009
11010 if (!entry) {
11011 return this; // no need to remove
11012 }
11013
11014 var i = entry.index;
11015 this.unmergeAt(i);
11016 return this;
11017 },
11018 // remove eles in place on calling collection
11019 unmerge: function unmerge(toRemove) {
11020 var cy = this._private.cy;
11021
11022 if (!toRemove) {
11023 return this;
11024 }
11025
11026 if (toRemove && string(toRemove)) {
11027 var selector = toRemove;
11028 toRemove = cy.mutableElements().filter(selector);
11029 }
11030
11031 for (var i = 0; i < toRemove.length; i++) {
11032 this.unmergeOne(toRemove[i]);
11033 }
11034
11035 return this; // chaining
11036 },
11037 unmergeBy: function unmergeBy(toRmFn) {
11038 for (var i = this.length - 1; i >= 0; i--) {
11039 var ele = this[i];
11040
11041 if (toRmFn(ele)) {
11042 this.unmergeAt(i);
11043 }
11044 }
11045
11046 return this;
11047 },
11048 map: function map(mapFn, thisArg) {
11049 var arr = [];
11050 var eles = this;
11051
11052 for (var i = 0; i < eles.length; i++) {
11053 var ele = eles[i];
11054 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11055 arr.push(ret);
11056 }
11057
11058 return arr;
11059 },
11060 reduce: function reduce(fn, initialValue) {
11061 var val = initialValue;
11062 var eles = this;
11063
11064 for (var i = 0; i < eles.length; i++) {
11065 val = fn(val, eles[i], i, eles);
11066 }
11067
11068 return val;
11069 },
11070 max: function max(valFn, thisArg) {
11071 var max = -Infinity;
11072 var maxEle;
11073 var eles = this;
11074
11075 for (var i = 0; i < eles.length; i++) {
11076 var ele = eles[i];
11077 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11078
11079 if (val > max) {
11080 max = val;
11081 maxEle = ele;
11082 }
11083 }
11084
11085 return {
11086 value: max,
11087 ele: maxEle
11088 };
11089 },
11090 min: function min(valFn, thisArg) {
11091 var min = Infinity;
11092 var minEle;
11093 var eles = this;
11094
11095 for (var i = 0; i < eles.length; i++) {
11096 var ele = eles[i];
11097 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11098
11099 if (val < min) {
11100 min = val;
11101 minEle = ele;
11102 }
11103 }
11104
11105 return {
11106 value: min,
11107 ele: minEle
11108 };
11109 }
11110}; // aliases
11111
11112var fn$5 = elesfn$n;
11113fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11114fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11115fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11116fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11117fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11118fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11119
11120var elesfn$o = {
11121 isNode: function isNode() {
11122 return this.group() === 'nodes';
11123 },
11124 isEdge: function isEdge() {
11125 return this.group() === 'edges';
11126 },
11127 isLoop: function isLoop() {
11128 return this.isEdge() && this.source()[0] === this.target()[0];
11129 },
11130 isSimple: function isSimple() {
11131 return this.isEdge() && this.source()[0] !== this.target()[0];
11132 },
11133 group: function group() {
11134 var ele = this[0];
11135
11136 if (ele) {
11137 return ele._private.group;
11138 }
11139 }
11140};
11141
11142/**
11143 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11144 * and z-index (low to high). These styles affect how this applies:
11145 *
11146 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11147 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11148 * root to leaves of the compound graph. The last drawn is `top`.
11149 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11150 * `manual` ignores this convention and draws based on the `z-index` value setting.
11151 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11152 * `z-index` will be drawn on top of an element with a lower `z-index`.
11153 */
11154
11155var zIndexSort = function zIndexSort(a, b) {
11156 var cy = a.cy();
11157 var hasCompoundNodes = cy.hasCompoundNodes();
11158
11159 function getDepth(ele) {
11160 var style = ele.pstyle('z-compound-depth');
11161
11162 if (style.value === 'auto') {
11163 return hasCompoundNodes ? ele.zDepth() : 0;
11164 } else if (style.value === 'bottom') {
11165 return -1;
11166 } else if (style.value === 'top') {
11167 return MAX_INT;
11168 } // 'orphan'
11169
11170
11171 return 0;
11172 }
11173
11174 var depthDiff = getDepth(a) - getDepth(b);
11175
11176 if (depthDiff !== 0) {
11177 return depthDiff;
11178 }
11179
11180 function getEleDepth(ele) {
11181 var style = ele.pstyle('z-index-compare');
11182
11183 if (style.value === 'auto') {
11184 return ele.isNode() ? 1 : 0;
11185 } // 'manual'
11186
11187
11188 return 0;
11189 }
11190
11191 var eleDiff = getEleDepth(a) - getEleDepth(b);
11192
11193 if (eleDiff !== 0) {
11194 return eleDiff;
11195 }
11196
11197 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11198
11199 if (zDiff !== 0) {
11200 return zDiff;
11201 } // compare indices in the core (order added to graph w/ last on top)
11202
11203
11204 return a.poolIndex() - b.poolIndex();
11205};
11206
11207var elesfn$p = {
11208 forEach: function forEach(fn$1, thisArg) {
11209 if (fn(fn$1)) {
11210 var N = this.length;
11211
11212 for (var i = 0; i < N; i++) {
11213 var ele = this[i];
11214 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11215
11216 if (ret === false) {
11217 break;
11218 } // exit each early on return false
11219
11220 }
11221 }
11222
11223 return this;
11224 },
11225 toArray: function toArray() {
11226 var array = [];
11227
11228 for (var i = 0; i < this.length; i++) {
11229 array.push(this[i]);
11230 }
11231
11232 return array;
11233 },
11234 slice: function slice(start, end) {
11235 var array = [];
11236 var thisSize = this.length;
11237
11238 if (end == null) {
11239 end = thisSize;
11240 }
11241
11242 if (start == null) {
11243 start = 0;
11244 }
11245
11246 if (start < 0) {
11247 start = thisSize + start;
11248 }
11249
11250 if (end < 0) {
11251 end = thisSize + end;
11252 }
11253
11254 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11255 array.push(this[i]);
11256 }
11257
11258 return this.spawn(array);
11259 },
11260 size: function size() {
11261 return this.length;
11262 },
11263 eq: function eq(i) {
11264 return this[i] || this.spawn();
11265 },
11266 first: function first() {
11267 return this[0] || this.spawn();
11268 },
11269 last: function last() {
11270 return this[this.length - 1] || this.spawn();
11271 },
11272 empty: function empty() {
11273 return this.length === 0;
11274 },
11275 nonempty: function nonempty() {
11276 return !this.empty();
11277 },
11278 sort: function sort(sortFn) {
11279 if (!fn(sortFn)) {
11280 return this;
11281 }
11282
11283 var sorted = this.toArray().sort(sortFn);
11284 return this.spawn(sorted);
11285 },
11286 sortByZIndex: function sortByZIndex() {
11287 return this.sort(zIndexSort);
11288 },
11289 zDepth: function zDepth() {
11290 var ele = this[0];
11291
11292 if (!ele) {
11293 return undefined;
11294 } // let cy = ele.cy();
11295
11296
11297 var _p = ele._private;
11298 var group = _p.group;
11299
11300 if (group === 'nodes') {
11301 var depth = _p.data.parent ? ele.parents().size() : 0;
11302
11303 if (!ele.isParent()) {
11304 return MAX_INT - 1; // childless nodes always on top
11305 }
11306
11307 return depth;
11308 } else {
11309 var src = _p.source;
11310 var tgt = _p.target;
11311 var srcDepth = src.zDepth();
11312 var tgtDepth = tgt.zDepth();
11313 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11314 }
11315 }
11316};
11317elesfn$p.each = elesfn$p.forEach;
11318
11319var defineSymbolIterator = function defineSymbolIterator() {
11320 var typeofUndef = "undefined" ;
11321 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11322
11323 if (isIteratorSupported) {
11324 elesfn$p[Symbol.iterator] = function () {
11325 var _this = this;
11326
11327 // eslint-disable-line no-undef
11328 var entry = {
11329 value: undefined,
11330 done: false
11331 };
11332 var i = 0;
11333 var length = this.length;
11334 return _defineProperty({
11335 next: function next() {
11336 if (i < length) {
11337 entry.value = _this[i++];
11338 } else {
11339 entry.value = undefined;
11340 entry.done = true;
11341 }
11342
11343 return entry;
11344 }
11345 }, Symbol.iterator, function () {
11346 // eslint-disable-line no-undef
11347 return this;
11348 });
11349 };
11350 }
11351};
11352
11353defineSymbolIterator();
11354
11355var getLayoutDimensionOptions = defaults({
11356 nodeDimensionsIncludeLabels: false
11357});
11358var elesfn$q = {
11359 // Calculates and returns node dimensions { x, y } based on options given
11360 layoutDimensions: function layoutDimensions(options) {
11361 options = getLayoutDimensionOptions(options);
11362 var dims;
11363
11364 if (!this.takesUpSpace()) {
11365 dims = {
11366 w: 0,
11367 h: 0
11368 };
11369 } else if (options.nodeDimensionsIncludeLabels) {
11370 var bbDim = this.boundingBox();
11371 dims = {
11372 w: bbDim.w,
11373 h: bbDim.h
11374 };
11375 } else {
11376 dims = {
11377 w: this.outerWidth(),
11378 h: this.outerHeight()
11379 };
11380 } // sanitise the dimensions for external layouts (avoid division by zero)
11381
11382
11383 if (dims.w === 0 || dims.h === 0) {
11384 dims.w = dims.h = 1;
11385 }
11386
11387 return dims;
11388 },
11389 // using standard layout options, apply position function (w/ or w/o animation)
11390 layoutPositions: function layoutPositions(layout, options, fn) {
11391 var nodes = this.nodes().filter(function (n) {
11392 return !n.isParent();
11393 });
11394 var cy = this.cy();
11395 var layoutEles = options.eles; // nodes & edges
11396
11397 var getMemoizeKey = function getMemoizeKey(node) {
11398 return node.id();
11399 };
11400
11401 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11402
11403 layout.emit({
11404 type: 'layoutstart',
11405 layout: layout
11406 });
11407 layout.animations = [];
11408
11409 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11410 var center = {
11411 x: nodesBb.x1 + nodesBb.w / 2,
11412 y: nodesBb.y1 + nodesBb.h / 2
11413 };
11414 var spacingVector = {
11415 // scale from center of bounding box (not necessarily 0,0)
11416 x: (pos.x - center.x) * spacing,
11417 y: (pos.y - center.y) * spacing
11418 };
11419 return {
11420 x: center.x + spacingVector.x,
11421 y: center.y + spacingVector.y
11422 };
11423 };
11424
11425 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11426
11427 var spacingBb = function spacingBb() {
11428 if (!useSpacingFactor) {
11429 return null;
11430 }
11431
11432 var bb = makeBoundingBox();
11433
11434 for (var i = 0; i < nodes.length; i++) {
11435 var node = nodes[i];
11436 var pos = fnMem(node, i);
11437 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11438 }
11439
11440 return bb;
11441 };
11442
11443 var bb = spacingBb();
11444 var getFinalPos = memoize(function (node, i) {
11445 var newPos = fnMem(node, i);
11446
11447 if (useSpacingFactor) {
11448 var spacing = Math.abs(options.spacingFactor);
11449 newPos = calculateSpacing(spacing, bb, newPos);
11450 }
11451
11452 if (options.transform != null) {
11453 newPos = options.transform(node, newPos);
11454 }
11455
11456 return newPos;
11457 }, getMemoizeKey);
11458
11459 if (options.animate) {
11460 for (var i = 0; i < nodes.length; i++) {
11461 var node = nodes[i];
11462 var newPos = getFinalPos(node, i);
11463 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11464
11465 if (animateNode) {
11466 var ani = node.animation({
11467 position: newPos,
11468 duration: options.animationDuration,
11469 easing: options.animationEasing
11470 });
11471 layout.animations.push(ani);
11472 } else {
11473 node.position(newPos);
11474 }
11475 }
11476
11477 if (options.fit) {
11478 var fitAni = cy.animation({
11479 fit: {
11480 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11481 padding: options.padding
11482 },
11483 duration: options.animationDuration,
11484 easing: options.animationEasing
11485 });
11486 layout.animations.push(fitAni);
11487 } else if (options.zoom !== undefined && options.pan !== undefined) {
11488 var zoomPanAni = cy.animation({
11489 zoom: options.zoom,
11490 pan: options.pan,
11491 duration: options.animationDuration,
11492 easing: options.animationEasing
11493 });
11494 layout.animations.push(zoomPanAni);
11495 }
11496
11497 layout.animations.forEach(function (ani) {
11498 return ani.play();
11499 });
11500 layout.one('layoutready', options.ready);
11501 layout.emit({
11502 type: 'layoutready',
11503 layout: layout
11504 });
11505 Promise$1.all(layout.animations.map(function (ani) {
11506 return ani.promise();
11507 })).then(function () {
11508 layout.one('layoutstop', options.stop);
11509 layout.emit({
11510 type: 'layoutstop',
11511 layout: layout
11512 });
11513 });
11514 } else {
11515 nodes.positions(getFinalPos);
11516
11517 if (options.fit) {
11518 cy.fit(options.eles, options.padding);
11519 }
11520
11521 if (options.zoom != null) {
11522 cy.zoom(options.zoom);
11523 }
11524
11525 if (options.pan) {
11526 cy.pan(options.pan);
11527 }
11528
11529 layout.one('layoutready', options.ready);
11530 layout.emit({
11531 type: 'layoutready',
11532 layout: layout
11533 });
11534 layout.one('layoutstop', options.stop);
11535 layout.emit({
11536 type: 'layoutstop',
11537 layout: layout
11538 });
11539 }
11540
11541 return this; // chaining
11542 },
11543 layout: function layout(options) {
11544 var cy = this.cy();
11545 return cy.makeLayout(extend({}, options, {
11546 eles: this
11547 }));
11548 }
11549}; // aliases:
11550
11551elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11552
11553function styleCache(key, fn, ele) {
11554 var _p = ele._private;
11555 var cache = _p.styleCache = _p.styleCache || [];
11556 var val;
11557
11558 if ((val = cache[key]) != null) {
11559 return val;
11560 } else {
11561 val = cache[key] = fn(ele);
11562 return val;
11563 }
11564}
11565
11566function cacheStyleFunction(key, fn) {
11567 key = hashString(key);
11568 return function cachedStyleFunction(ele) {
11569 return styleCache(key, fn, ele);
11570 };
11571}
11572
11573function cachePrototypeStyleFunction(key, fn) {
11574 key = hashString(key);
11575
11576 var selfFn = function selfFn(ele) {
11577 return fn.call(ele);
11578 };
11579
11580 return function cachedPrototypeStyleFunction() {
11581 var ele = this[0];
11582
11583 if (ele) {
11584 return styleCache(key, selfFn, ele);
11585 }
11586 };
11587}
11588
11589var elesfn$r = {
11590 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11591 var cy = this.cy();
11592 var renderer = cy.renderer();
11593 var styleEnabled = cy.styleEnabled();
11594
11595 if (renderer && styleEnabled) {
11596 renderer.recalculateRenderedStyle(this, useCache);
11597 }
11598
11599 return this;
11600 },
11601 dirtyStyleCache: function dirtyStyleCache() {
11602 var cy = this.cy();
11603
11604 var dirty = function dirty(ele) {
11605 return ele._private.styleCache = null;
11606 };
11607
11608 if (cy.hasCompoundNodes()) {
11609 var eles;
11610 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11611 eles.merge(eles.connectedEdges());
11612 eles.forEach(dirty);
11613 } else {
11614 this.forEach(function (ele) {
11615 dirty(ele);
11616 ele.connectedEdges().forEach(dirty);
11617 });
11618 }
11619
11620 return this;
11621 },
11622 // fully updates (recalculates) the style for the elements
11623 updateStyle: function updateStyle(notifyRenderer) {
11624 var cy = this._private.cy;
11625
11626 if (!cy.styleEnabled()) {
11627 return this;
11628 }
11629
11630 if (cy.batching()) {
11631 var bEles = cy._private.batchStyleEles;
11632 bEles.merge(this);
11633 return this; // chaining and exit early when batching
11634 }
11635
11636 var hasCompounds = cy.hasCompoundNodes();
11637 var updatedEles = this;
11638 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11639
11640 if (hasCompounds) {
11641 // then add everything up and down for compound selector checks
11642 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11643 } // let changedEles = style.apply( updatedEles );
11644
11645
11646 var changedEles = updatedEles;
11647
11648 if (notifyRenderer) {
11649 changedEles.emitAndNotify('style'); // let renderer know we changed style
11650 } else {
11651 changedEles.emit('style'); // just fire the event
11652 }
11653
11654 updatedEles.forEach(function (ele) {
11655 return ele._private.styleDirty = true;
11656 });
11657 return this; // chaining
11658 },
11659 // private: clears dirty flag and recalculates style
11660 cleanStyle: function cleanStyle() {
11661 var cy = this.cy();
11662
11663 if (!cy.styleEnabled()) {
11664 return;
11665 }
11666
11667 for (var i = 0; i < this.length; i++) {
11668 var ele = this[i];
11669
11670 if (ele._private.styleDirty) {
11671 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11672 ele._private.styleDirty = false;
11673 cy.style().apply(ele);
11674 }
11675 }
11676 },
11677 // get the internal parsed style object for the specified property
11678 parsedStyle: function parsedStyle(property) {
11679 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11680 var ele = this[0];
11681 var cy = ele.cy();
11682
11683 if (!cy.styleEnabled()) {
11684 return;
11685 }
11686
11687 if (ele) {
11688 this.cleanStyle();
11689 var overriddenStyle = ele._private.style[property];
11690
11691 if (overriddenStyle != null) {
11692 return overriddenStyle;
11693 } else if (includeNonDefault) {
11694 return cy.style().getDefaultProperty(property);
11695 } else {
11696 return null;
11697 }
11698 }
11699 },
11700 numericStyle: function numericStyle(property) {
11701 var ele = this[0];
11702
11703 if (!ele.cy().styleEnabled()) {
11704 return;
11705 }
11706
11707 if (ele) {
11708 var pstyle = ele.pstyle(property);
11709 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11710 }
11711 },
11712 numericStyleUnits: function numericStyleUnits(property) {
11713 var ele = this[0];
11714
11715 if (!ele.cy().styleEnabled()) {
11716 return;
11717 }
11718
11719 if (ele) {
11720 return ele.pstyle(property).units;
11721 }
11722 },
11723 // get the specified css property as a rendered value (i.e. on-screen value)
11724 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11725 renderedStyle: function renderedStyle(property) {
11726 var cy = this.cy();
11727
11728 if (!cy.styleEnabled()) {
11729 return this;
11730 }
11731
11732 var ele = this[0];
11733
11734 if (ele) {
11735 return cy.style().getRenderedStyle(ele, property);
11736 }
11737 },
11738 // read the calculated css style of the element or override the style (via a bypass)
11739 style: function style(name, value) {
11740 var cy = this.cy();
11741
11742 if (!cy.styleEnabled()) {
11743 return this;
11744 }
11745
11746 var updateTransitions = false;
11747 var style = cy.style();
11748
11749 if (plainObject(name)) {
11750 // then extend the bypass
11751 var props = name;
11752 style.applyBypass(this, props, updateTransitions);
11753 this.emitAndNotify('style'); // let the renderer know we've updated style
11754 } else if (string(name)) {
11755 if (value === undefined) {
11756 // then get the property from the style
11757 var ele = this[0];
11758
11759 if (ele) {
11760 return style.getStylePropertyValue(ele, name);
11761 } else {
11762 // empty collection => can't get any value
11763 return;
11764 }
11765 } else {
11766 // then set the bypass with the property value
11767 style.applyBypass(this, name, value, updateTransitions);
11768 this.emitAndNotify('style'); // let the renderer know we've updated style
11769 }
11770 } else if (name === undefined) {
11771 var _ele = this[0];
11772
11773 if (_ele) {
11774 return style.getRawStyle(_ele);
11775 } else {
11776 // empty collection => can't get any value
11777 return;
11778 }
11779 }
11780
11781 return this; // chaining
11782 },
11783 removeStyle: function removeStyle(names) {
11784 var cy = this.cy();
11785
11786 if (!cy.styleEnabled()) {
11787 return this;
11788 }
11789
11790 var updateTransitions = false;
11791 var style = cy.style();
11792 var eles = this;
11793
11794 if (names === undefined) {
11795 for (var i = 0; i < eles.length; i++) {
11796 var ele = eles[i];
11797 style.removeAllBypasses(ele, updateTransitions);
11798 }
11799 } else {
11800 names = names.split(/\s+/);
11801
11802 for (var _i = 0; _i < eles.length; _i++) {
11803 var _ele2 = eles[_i];
11804 style.removeBypasses(_ele2, names, updateTransitions);
11805 }
11806 }
11807
11808 this.emitAndNotify('style'); // let the renderer know we've updated style
11809
11810 return this; // chaining
11811 },
11812 show: function show() {
11813 this.css('display', 'element');
11814 return this; // chaining
11815 },
11816 hide: function hide() {
11817 this.css('display', 'none');
11818 return this; // chaining
11819 },
11820 effectiveOpacity: function effectiveOpacity() {
11821 var cy = this.cy();
11822
11823 if (!cy.styleEnabled()) {
11824 return 1;
11825 }
11826
11827 var hasCompoundNodes = cy.hasCompoundNodes();
11828 var ele = this[0];
11829
11830 if (ele) {
11831 var _p = ele._private;
11832 var parentOpacity = ele.pstyle('opacity').value;
11833
11834 if (!hasCompoundNodes) {
11835 return parentOpacity;
11836 }
11837
11838 var parents = !_p.data.parent ? null : ele.parents();
11839
11840 if (parents) {
11841 for (var i = 0; i < parents.length; i++) {
11842 var parent = parents[i];
11843 var opacity = parent.pstyle('opacity').value;
11844 parentOpacity = opacity * parentOpacity;
11845 }
11846 }
11847
11848 return parentOpacity;
11849 }
11850 },
11851 transparent: function transparent() {
11852 var cy = this.cy();
11853
11854 if (!cy.styleEnabled()) {
11855 return false;
11856 }
11857
11858 var ele = this[0];
11859 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11860
11861 if (ele) {
11862 if (!hasCompoundNodes) {
11863 return ele.pstyle('opacity').value === 0;
11864 } else {
11865 return ele.effectiveOpacity() === 0;
11866 }
11867 }
11868 },
11869 backgrounding: function backgrounding() {
11870 var cy = this.cy();
11871
11872 if (!cy.styleEnabled()) {
11873 return false;
11874 }
11875
11876 var ele = this[0];
11877 return ele._private.backgrounding ? true : false;
11878 }
11879};
11880
11881function checkCompound(ele, parentOk) {
11882 var _p = ele._private;
11883 var parents = _p.data.parent ? ele.parents() : null;
11884
11885 if (parents) {
11886 for (var i = 0; i < parents.length; i++) {
11887 var parent = parents[i];
11888
11889 if (!parentOk(parent)) {
11890 return false;
11891 }
11892 }
11893 }
11894
11895 return true;
11896}
11897
11898function defineDerivedStateFunction(specs) {
11899 var ok = specs.ok;
11900 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11901 var parentOk = specs.parentOk || specs.ok;
11902 return function () {
11903 var cy = this.cy();
11904
11905 if (!cy.styleEnabled()) {
11906 return true;
11907 }
11908
11909 var ele = this[0];
11910 var hasCompoundNodes = cy.hasCompoundNodes();
11911
11912 if (ele) {
11913 var _p = ele._private;
11914
11915 if (!ok(ele)) {
11916 return false;
11917 }
11918
11919 if (ele.isNode()) {
11920 return !hasCompoundNodes || checkCompound(ele, parentOk);
11921 } else {
11922 var src = _p.source;
11923 var tgt = _p.target;
11924 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11925 }
11926 }
11927 };
11928}
11929
11930var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11931 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11932});
11933elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11934 ok: eleTakesUpSpace
11935}));
11936var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11937 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11938});
11939var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11940 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11941});
11942elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11943 ok: eleInteractive,
11944 parentOk: parentInteractive,
11945 edgeOkViaNode: eleTakesUpSpace
11946}));
11947
11948elesfn$r.noninteractive = function () {
11949 var ele = this[0];
11950
11951 if (ele) {
11952 return !ele.interactive();
11953 }
11954};
11955
11956var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11957 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11958});
11959var edgeVisibleViaNode = eleTakesUpSpace;
11960elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11961 ok: eleVisible,
11962 edgeOkViaNode: edgeVisibleViaNode
11963}));
11964
11965elesfn$r.hidden = function () {
11966 var ele = this[0];
11967
11968 if (ele) {
11969 return !ele.visible();
11970 }
11971};
11972
11973elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11974 if (!this.cy().styleEnabled()) {
11975 return false;
11976 }
11977
11978 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11979});
11980elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11981elesfn$r.renderedCss = elesfn$r.renderedStyle;
11982elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11983elesfn$r.pstyle = elesfn$r.parsedStyle;
11984
11985var elesfn$s = {};
11986
11987function defineSwitchFunction(params) {
11988 return function () {
11989 var args = arguments;
11990 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11991
11992 if (args.length === 2) {
11993 var data = args[0];
11994 var handler = args[1];
11995 this.on(params.event, data, handler);
11996 } // e.g. cy.nodes().select( handler )
11997 else if (args.length === 1 && fn(args[0])) {
11998 var _handler = args[0];
11999 this.on(params.event, _handler);
12000 } // e.g. cy.nodes().select()
12001 // e.g. (private) cy.nodes().select(['tapselect'])
12002 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12003 var addlEvents = args.length === 1 ? args[0] : null;
12004
12005 for (var i = 0; i < this.length; i++) {
12006 var ele = this[i];
12007 var able = !params.ableField || ele._private[params.ableField];
12008 var changed = ele._private[params.field] != params.value;
12009
12010 if (params.overrideAble) {
12011 var overrideAble = params.overrideAble(ele);
12012
12013 if (overrideAble !== undefined) {
12014 able = overrideAble;
12015
12016 if (!overrideAble) {
12017 return this;
12018 } // to save cycles assume not able for all on override
12019
12020 }
12021 }
12022
12023 if (able) {
12024 ele._private[params.field] = params.value;
12025
12026 if (changed) {
12027 changedEles.push(ele);
12028 }
12029 }
12030 }
12031
12032 var changedColl = this.spawn(changedEles);
12033 changedColl.updateStyle(); // change of state => possible change of style
12034
12035 changedColl.emit(params.event);
12036
12037 if (addlEvents) {
12038 changedColl.emit(addlEvents);
12039 }
12040 }
12041
12042 return this;
12043 };
12044}
12045
12046function defineSwitchSet(params) {
12047 elesfn$s[params.field] = function () {
12048 var ele = this[0];
12049
12050 if (ele) {
12051 if (params.overrideField) {
12052 var val = params.overrideField(ele);
12053
12054 if (val !== undefined) {
12055 return val;
12056 }
12057 }
12058
12059 return ele._private[params.field];
12060 }
12061 };
12062
12063 elesfn$s[params.on] = defineSwitchFunction({
12064 event: params.on,
12065 field: params.field,
12066 ableField: params.ableField,
12067 overrideAble: params.overrideAble,
12068 value: true
12069 });
12070 elesfn$s[params.off] = defineSwitchFunction({
12071 event: params.off,
12072 field: params.field,
12073 ableField: params.ableField,
12074 overrideAble: params.overrideAble,
12075 value: false
12076 });
12077}
12078
12079defineSwitchSet({
12080 field: 'locked',
12081 overrideField: function overrideField(ele) {
12082 return ele.cy().autolock() ? true : undefined;
12083 },
12084 on: 'lock',
12085 off: 'unlock'
12086});
12087defineSwitchSet({
12088 field: 'grabbable',
12089 overrideField: function overrideField(ele) {
12090 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12091 },
12092 on: 'grabify',
12093 off: 'ungrabify'
12094});
12095defineSwitchSet({
12096 field: 'selected',
12097 ableField: 'selectable',
12098 overrideAble: function overrideAble(ele) {
12099 return ele.cy().autounselectify() ? false : undefined;
12100 },
12101 on: 'select',
12102 off: 'unselect'
12103});
12104defineSwitchSet({
12105 field: 'selectable',
12106 overrideField: function overrideField(ele) {
12107 return ele.cy().autounselectify() ? false : undefined;
12108 },
12109 on: 'selectify',
12110 off: 'unselectify'
12111});
12112elesfn$s.deselect = elesfn$s.unselect;
12113
12114elesfn$s.grabbed = function () {
12115 var ele = this[0];
12116
12117 if (ele) {
12118 return ele._private.grabbed;
12119 }
12120};
12121
12122defineSwitchSet({
12123 field: 'active',
12124 on: 'activate',
12125 off: 'unactivate'
12126});
12127defineSwitchSet({
12128 field: 'pannable',
12129 on: 'panify',
12130 off: 'unpanify'
12131});
12132
12133elesfn$s.inactive = function () {
12134 var ele = this[0];
12135
12136 if (ele) {
12137 return !ele._private.active;
12138 }
12139};
12140
12141var elesfn$t = {}; // DAG functions
12142////////////////
12143
12144var defineDagExtremity = function defineDagExtremity(params) {
12145 return function dagExtremityImpl(selector) {
12146 var eles = this;
12147 var ret = [];
12148
12149 for (var i = 0; i < eles.length; i++) {
12150 var ele = eles[i];
12151
12152 if (!ele.isNode()) {
12153 continue;
12154 }
12155
12156 var disqualified = false;
12157 var edges = ele.connectedEdges();
12158
12159 for (var j = 0; j < edges.length; j++) {
12160 var edge = edges[j];
12161 var src = edge.source();
12162 var tgt = edge.target();
12163
12164 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12165 disqualified = true;
12166 break;
12167 }
12168 }
12169
12170 if (!disqualified) {
12171 ret.push(ele);
12172 }
12173 }
12174
12175 return this.spawn(ret, true).filter(selector);
12176 };
12177};
12178
12179var defineDagOneHop = function defineDagOneHop(params) {
12180 return function (selector) {
12181 var eles = this;
12182 var oEles = [];
12183
12184 for (var i = 0; i < eles.length; i++) {
12185 var ele = eles[i];
12186
12187 if (!ele.isNode()) {
12188 continue;
12189 }
12190
12191 var edges = ele.connectedEdges();
12192
12193 for (var j = 0; j < edges.length; j++) {
12194 var edge = edges[j];
12195 var src = edge.source();
12196 var tgt = edge.target();
12197
12198 if (params.outgoing && src === ele) {
12199 oEles.push(edge);
12200 oEles.push(tgt);
12201 } else if (params.incoming && tgt === ele) {
12202 oEles.push(edge);
12203 oEles.push(src);
12204 }
12205 }
12206 }
12207
12208 return this.spawn(oEles, true).filter(selector);
12209 };
12210};
12211
12212var defineDagAllHops = function defineDagAllHops(params) {
12213 return function (selector) {
12214 var eles = this;
12215 var sEles = [];
12216 var sElesIds = {};
12217
12218 for (;;) {
12219 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12220
12221 if (next.length === 0) {
12222 break;
12223 } // done if none left
12224
12225
12226 var newNext = false;
12227
12228 for (var i = 0; i < next.length; i++) {
12229 var n = next[i];
12230 var nid = n.id();
12231
12232 if (!sElesIds[nid]) {
12233 sElesIds[nid] = true;
12234 sEles.push(n);
12235 newNext = true;
12236 }
12237 }
12238
12239 if (!newNext) {
12240 break;
12241 } // done if touched all outgoers already
12242
12243
12244 eles = next;
12245 }
12246
12247 return this.spawn(sEles, true).filter(selector);
12248 };
12249};
12250
12251elesfn$t.clearTraversalCache = function () {
12252 for (var i = 0; i < this.length; i++) {
12253 this[i]._private.traversalCache = null;
12254 }
12255};
12256
12257extend(elesfn$t, {
12258 // get the root nodes in the DAG
12259 roots: defineDagExtremity({
12260 noIncomingEdges: true
12261 }),
12262 // get the leaf nodes in the DAG
12263 leaves: defineDagExtremity({
12264 noOutgoingEdges: true
12265 }),
12266 // normally called children in graph theory
12267 // these nodes =edges=> outgoing nodes
12268 outgoers: cache(defineDagOneHop({
12269 outgoing: true
12270 }), 'outgoers'),
12271 // aka DAG descendants
12272 successors: defineDagAllHops({
12273 outgoing: true
12274 }),
12275 // normally called parents in graph theory
12276 // these nodes <=edges= incoming nodes
12277 incomers: cache(defineDagOneHop({
12278 incoming: true
12279 }), 'incomers'),
12280 // aka DAG ancestors
12281 predecessors: defineDagAllHops({
12282 incoming: true
12283 })
12284}); // Neighbourhood functions
12285//////////////////////////
12286
12287extend(elesfn$t, {
12288 neighborhood: cache(function (selector) {
12289 var elements = [];
12290 var nodes = this.nodes();
12291
12292 for (var i = 0; i < nodes.length; i++) {
12293 // for all nodes
12294 var node = nodes[i];
12295 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12296
12297 for (var j = 0; j < connectedEdges.length; j++) {
12298 var edge = connectedEdges[j];
12299 var src = edge.source();
12300 var tgt = edge.target();
12301 var otherNode = node === src ? tgt : src; // need check in case of loop
12302
12303 if (otherNode.length > 0) {
12304 elements.push(otherNode[0]); // add node 1 hop away
12305 } // add connected edge
12306
12307
12308 elements.push(edge[0]);
12309 }
12310 }
12311
12312 return this.spawn(elements, true).filter(selector);
12313 }, 'neighborhood'),
12314 closedNeighborhood: function closedNeighborhood(selector) {
12315 return this.neighborhood().add(this).filter(selector);
12316 },
12317 openNeighborhood: function openNeighborhood(selector) {
12318 return this.neighborhood(selector);
12319 }
12320}); // aliases
12321
12322elesfn$t.neighbourhood = elesfn$t.neighborhood;
12323elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12324elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12325/////////////////
12326
12327extend(elesfn$t, {
12328 source: cache(function sourceImpl(selector) {
12329 var ele = this[0];
12330 var src;
12331
12332 if (ele) {
12333 src = ele._private.source || ele.cy().collection();
12334 }
12335
12336 return src && selector ? src.filter(selector) : src;
12337 }, 'source'),
12338 target: cache(function targetImpl(selector) {
12339 var ele = this[0];
12340 var tgt;
12341
12342 if (ele) {
12343 tgt = ele._private.target || ele.cy().collection();
12344 }
12345
12346 return tgt && selector ? tgt.filter(selector) : tgt;
12347 }, 'target'),
12348 sources: defineSourceFunction({
12349 attr: 'source'
12350 }),
12351 targets: defineSourceFunction({
12352 attr: 'target'
12353 })
12354});
12355
12356function defineSourceFunction(params) {
12357 return function sourceImpl(selector) {
12358 var sources = [];
12359
12360 for (var i = 0; i < this.length; i++) {
12361 var ele = this[i];
12362 var src = ele._private[params.attr];
12363
12364 if (src) {
12365 sources.push(src);
12366 }
12367 }
12368
12369 return this.spawn(sources, true).filter(selector);
12370 };
12371}
12372
12373extend(elesfn$t, {
12374 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12375 edgesTo: cache(defineEdgesWithFunction({
12376 thisIsSrc: true
12377 }), 'edgesTo')
12378});
12379
12380function defineEdgesWithFunction(params) {
12381 return function edgesWithImpl(otherNodes) {
12382 var elements = [];
12383 var cy = this._private.cy;
12384 var p = params || {}; // get elements if a selector is specified
12385
12386 if (string(otherNodes)) {
12387 otherNodes = cy.$(otherNodes);
12388 }
12389
12390 for (var h = 0; h < otherNodes.length; h++) {
12391 var edges = otherNodes[h]._private.edges;
12392
12393 for (var i = 0; i < edges.length; i++) {
12394 var edge = edges[i];
12395 var edgeData = edge._private.data;
12396 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12397 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12398 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12399
12400 if (!edgeConnectsThisAndOther) {
12401 continue;
12402 }
12403
12404 if (p.thisIsSrc || p.thisIsTgt) {
12405 if (p.thisIsSrc && !thisToOther) {
12406 continue;
12407 }
12408
12409 if (p.thisIsTgt && !otherToThis) {
12410 continue;
12411 }
12412 }
12413
12414 elements.push(edge);
12415 }
12416 }
12417
12418 return this.spawn(elements, true);
12419 };
12420}
12421
12422extend(elesfn$t, {
12423 connectedEdges: cache(function (selector) {
12424 var retEles = [];
12425 var eles = this;
12426
12427 for (var i = 0; i < eles.length; i++) {
12428 var node = eles[i];
12429
12430 if (!node.isNode()) {
12431 continue;
12432 }
12433
12434 var edges = node._private.edges;
12435
12436 for (var j = 0; j < edges.length; j++) {
12437 var edge = edges[j];
12438 retEles.push(edge);
12439 }
12440 }
12441
12442 return this.spawn(retEles, true).filter(selector);
12443 }, 'connectedEdges'),
12444 connectedNodes: cache(function (selector) {
12445 var retEles = [];
12446 var eles = this;
12447
12448 for (var i = 0; i < eles.length; i++) {
12449 var edge = eles[i];
12450
12451 if (!edge.isEdge()) {
12452 continue;
12453 }
12454
12455 retEles.push(edge.source()[0]);
12456 retEles.push(edge.target()[0]);
12457 }
12458
12459 return this.spawn(retEles, true).filter(selector);
12460 }, 'connectedNodes'),
12461 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12462 codirectedEdges: cache(defineParallelEdgesFunction({
12463 codirected: true
12464 }), 'codirectedEdges')
12465});
12466
12467function defineParallelEdgesFunction(params) {
12468 var defaults = {
12469 codirected: false
12470 };
12471 params = extend({}, defaults, params);
12472 return function parallelEdgesImpl(selector) {
12473 // micro-optimised for renderer
12474 var elements = [];
12475 var edges = this.edges();
12476 var p = params; // look at all the edges in the collection
12477
12478 for (var i = 0; i < edges.length; i++) {
12479 var edge1 = edges[i];
12480 var edge1_p = edge1._private;
12481 var src1 = edge1_p.source;
12482 var srcid1 = src1._private.data.id;
12483 var tgtid1 = edge1_p.data.target;
12484 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12485
12486 for (var j = 0; j < srcEdges1.length; j++) {
12487 var edge2 = srcEdges1[j];
12488 var edge2data = edge2._private.data;
12489 var tgtid2 = edge2data.target;
12490 var srcid2 = edge2data.source;
12491 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12492 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12493
12494 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12495 elements.push(edge2);
12496 }
12497 }
12498 }
12499
12500 return this.spawn(elements, true).filter(selector);
12501 };
12502} // Misc functions
12503/////////////////
12504
12505
12506extend(elesfn$t, {
12507 components: function components(root) {
12508 var self = this;
12509 var cy = self.cy();
12510 var visited = cy.collection();
12511 var unvisited = root == null ? self.nodes() : root.nodes();
12512 var components = [];
12513
12514 if (root != null && unvisited.empty()) {
12515 // root may contain only edges
12516 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12517 }
12518
12519 var visitInComponent = function visitInComponent(node, component) {
12520 visited.merge(node);
12521 unvisited.unmerge(node);
12522 component.merge(node);
12523 };
12524
12525 if (unvisited.empty()) {
12526 return self.spawn();
12527 }
12528
12529 var _loop = function _loop() {
12530 // each iteration yields a component
12531 var cmpt = cy.collection();
12532 components.push(cmpt);
12533 var root = unvisited[0];
12534 visitInComponent(root, cmpt);
12535 self.bfs({
12536 directed: false,
12537 roots: root,
12538 visit: function visit(v) {
12539 return visitInComponent(v, cmpt);
12540 }
12541 });
12542 cmpt.forEach(function (node) {
12543 node.connectedEdges().forEach(function (e) {
12544 // connectedEdges() usually cached
12545 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12546 // has() is cheap
12547 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12548 }
12549 });
12550 });
12551 };
12552
12553 do {
12554 _loop();
12555 } while (unvisited.length > 0);
12556
12557 return components;
12558 },
12559 component: function component() {
12560 var ele = this[0];
12561 return ele.cy().mutableElements().components(ele)[0];
12562 }
12563});
12564elesfn$t.componentsOf = elesfn$t.components;
12565
12566var Collection = function Collection(cy, elements) {
12567 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12568
12569 if (cy === undefined) {
12570 error('A collection must have a reference to the core');
12571 return;
12572 }
12573
12574 var map = new Map$1();
12575 var createdElements = false;
12576
12577 if (!elements) {
12578 elements = [];
12579 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12580 createdElements = true; // make elements from json and restore all at once later
12581
12582 var eles = [];
12583 var elesIds = new Set$1();
12584
12585 for (var i = 0, l = elements.length; i < l; i++) {
12586 var json = elements[i];
12587
12588 if (json.data == null) {
12589 json.data = {};
12590 }
12591
12592 var _data = json.data; // make sure newly created elements have valid ids
12593
12594 if (_data.id == null) {
12595 _data.id = uuid();
12596 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12597 continue; // can't create element if prior id already exists
12598 }
12599
12600 var ele = new Element(cy, json, false);
12601 eles.push(ele);
12602 elesIds.add(_data.id);
12603 }
12604
12605 elements = eles;
12606 }
12607
12608 this.length = 0;
12609
12610 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12611 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12612
12613 if (element$1 == null) {
12614 continue;
12615 }
12616
12617 var id = element$1._private.data.id;
12618
12619 if (!unique || !map.has(id)) {
12620 if (unique) {
12621 map.set(id, {
12622 index: this.length,
12623 ele: element$1
12624 });
12625 }
12626
12627 this[this.length] = element$1;
12628 this.length++;
12629 }
12630 }
12631
12632 this._private = {
12633 eles: this,
12634 cy: cy,
12635
12636 get map() {
12637 if (this.lazyMap == null) {
12638 this.rebuildMap();
12639 }
12640
12641 return this.lazyMap;
12642 },
12643
12644 set map(m) {
12645 this.lazyMap = m;
12646 },
12647
12648 rebuildMap: function rebuildMap() {
12649 var m = this.lazyMap = new Map$1();
12650 var eles = this.eles;
12651
12652 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12653 var _ele = eles[_i2];
12654 m.set(_ele.id(), {
12655 index: _i2,
12656 ele: _ele
12657 });
12658 }
12659 }
12660 };
12661
12662 if (unique) {
12663 this._private.map = map;
12664 } // restore the elements if we created them from json
12665
12666
12667 if (createdElements) {
12668 this.restore();
12669 }
12670}; // Functions
12671////////////////////////////////////////////////////////////////////////////////////////////////////
12672// keep the prototypes in sync (an element has the same functions as a collection)
12673// and use elefn and elesfn as shorthands to the prototypes
12674
12675
12676var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12677
12678elesfn$u.instanceString = function () {
12679 return 'collection';
12680};
12681
12682elesfn$u.spawn = function (eles, unique) {
12683 return new Collection(this.cy(), eles, unique);
12684};
12685
12686elesfn$u.spawnSelf = function () {
12687 return this.spawn(this);
12688};
12689
12690elesfn$u.cy = function () {
12691 return this._private.cy;
12692};
12693
12694elesfn$u.renderer = function () {
12695 return this._private.cy.renderer();
12696};
12697
12698elesfn$u.element = function () {
12699 return this[0];
12700};
12701
12702elesfn$u.collection = function () {
12703 if (collection(this)) {
12704 return this;
12705 } else {
12706 // an element
12707 return new Collection(this._private.cy, [this]);
12708 }
12709};
12710
12711elesfn$u.unique = function () {
12712 return new Collection(this._private.cy, this, true);
12713};
12714
12715elesfn$u.hasElementWithId = function (id) {
12716 id = '' + id; // id must be string
12717
12718 return this._private.map.has(id);
12719};
12720
12721elesfn$u.getElementById = function (id) {
12722 id = '' + id; // id must be string
12723
12724 var cy = this._private.cy;
12725
12726 var entry = this._private.map.get(id);
12727
12728 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12729};
12730
12731elesfn$u.$id = elesfn$u.getElementById;
12732
12733elesfn$u.poolIndex = function () {
12734 var cy = this._private.cy;
12735 var eles = cy._private.elements;
12736 var id = this[0]._private.data.id;
12737 return eles._private.map.get(id).index;
12738};
12739
12740elesfn$u.indexOf = function (ele) {
12741 var id = ele[0]._private.data.id;
12742 return this._private.map.get(id).index;
12743};
12744
12745elesfn$u.indexOfId = function (id) {
12746 id = '' + id; // id must be string
12747
12748 return this._private.map.get(id).index;
12749};
12750
12751elesfn$u.json = function (obj) {
12752 var ele = this.element();
12753 var cy = this.cy();
12754
12755 if (ele == null && obj) {
12756 return this;
12757 } // can't set to no eles
12758
12759
12760 if (ele == null) {
12761 return undefined;
12762 } // can't get from no eles
12763
12764
12765 var p = ele._private;
12766
12767 if (plainObject(obj)) {
12768 // set
12769 cy.startBatch();
12770
12771 if (obj.data) {
12772 ele.data(obj.data);
12773 var _data2 = p.data;
12774
12775 if (ele.isEdge()) {
12776 // source and target are immutable via data()
12777 var move = false;
12778 var spec = {};
12779 var src = obj.data.source;
12780 var tgt = obj.data.target;
12781
12782 if (src != null && src != _data2.source) {
12783 spec.source = '' + src; // id must be string
12784
12785 move = true;
12786 }
12787
12788 if (tgt != null && tgt != _data2.target) {
12789 spec.target = '' + tgt; // id must be string
12790
12791 move = true;
12792 }
12793
12794 if (move) {
12795 ele = ele.move(spec);
12796 }
12797 } else {
12798 // parent is immutable via data()
12799 var newParentValSpecd = 'parent' in obj.data;
12800 var parent = obj.data.parent;
12801
12802 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12803 if (parent === undefined) {
12804 // can't set undefined imperatively, so use null
12805 parent = null;
12806 }
12807
12808 if (parent != null) {
12809 parent = '' + parent; // id must be string
12810 }
12811
12812 ele = ele.move({
12813 parent: parent
12814 });
12815 }
12816 }
12817 }
12818
12819 if (obj.position) {
12820 ele.position(obj.position);
12821 } // ignore group -- immutable
12822
12823
12824 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12825 var obj_k = obj[k];
12826
12827 if (obj_k != null && obj_k !== p[k]) {
12828 if (obj_k) {
12829 ele[trueFnName]();
12830 } else {
12831 ele[falseFnName]();
12832 }
12833 }
12834 };
12835
12836 checkSwitch('removed', 'remove', 'restore');
12837 checkSwitch('selected', 'select', 'unselect');
12838 checkSwitch('selectable', 'selectify', 'unselectify');
12839 checkSwitch('locked', 'lock', 'unlock');
12840 checkSwitch('grabbable', 'grabify', 'ungrabify');
12841 checkSwitch('pannable', 'panify', 'unpanify');
12842
12843 if (obj.classes != null) {
12844 ele.classes(obj.classes);
12845 }
12846
12847 cy.endBatch();
12848 return this;
12849 } else if (obj === undefined) {
12850 // get
12851 var json = {
12852 data: copy(p.data),
12853 position: copy(p.position),
12854 group: p.group,
12855 removed: p.removed,
12856 selected: p.selected,
12857 selectable: p.selectable,
12858 locked: p.locked,
12859 grabbable: p.grabbable,
12860 pannable: p.pannable,
12861 classes: null
12862 };
12863 json.classes = '';
12864 var i = 0;
12865 p.classes.forEach(function (cls) {
12866 return json.classes += i++ === 0 ? cls : ' ' + cls;
12867 });
12868 return json;
12869 }
12870};
12871
12872elesfn$u.jsons = function () {
12873 var jsons = [];
12874
12875 for (var i = 0; i < this.length; i++) {
12876 var ele = this[i];
12877 var json = ele.json();
12878 jsons.push(json);
12879 }
12880
12881 return jsons;
12882};
12883
12884elesfn$u.clone = function () {
12885 var cy = this.cy();
12886 var elesArr = [];
12887
12888 for (var i = 0; i < this.length; i++) {
12889 var ele = this[i];
12890 var json = ele.json();
12891 var clone = new Element(cy, json, false); // NB no restore
12892
12893 elesArr.push(clone);
12894 }
12895
12896 return new Collection(cy, elesArr);
12897};
12898
12899elesfn$u.copy = elesfn$u.clone;
12900
12901elesfn$u.restore = function () {
12902 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12903 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12904 var self = this;
12905 var cy = self.cy();
12906 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12907 // restore the nodes first
12908
12909 var nodes = [];
12910 var edges = [];
12911 var elements;
12912
12913 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12914 var ele = self[_i3];
12915
12916 if (addToPool && !ele.removed()) {
12917 // don't need to handle this ele
12918 continue;
12919 } // keep nodes first in the array and edges after
12920
12921
12922 if (ele.isNode()) {
12923 // put to front of array if node
12924 nodes.push(ele);
12925 } else {
12926 // put to end of array if edge
12927 edges.push(ele);
12928 }
12929 }
12930
12931 elements = nodes.concat(edges);
12932 var i;
12933
12934 var removeFromElements = function removeFromElements() {
12935 elements.splice(i, 1);
12936 i--;
12937 }; // now, restore each element
12938
12939
12940 for (i = 0; i < elements.length; i++) {
12941 var _ele2 = elements[i];
12942 var _private = _ele2._private;
12943 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12944
12945 _ele2.clearTraversalCache(); // set id and validate
12946
12947
12948 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12949 _data3.id = uuid();
12950 } else if (number(_data3.id)) {
12951 _data3.id = '' + _data3.id; // now it's a string
12952 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12953 error('Can not create element with invalid string ID `' + _data3.id + '`'); // can't create element if it has empty string as id or non-string id
12954
12955 removeFromElements();
12956 continue;
12957 } else if (cy.hasElementWithId(_data3.id)) {
12958 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12959
12960 removeFromElements();
12961 continue;
12962 }
12963
12964 var id = _data3.id; // id is finalised, now let's keep a ref
12965
12966 if (_ele2.isNode()) {
12967 // extra checks for nodes
12968 var pos = _private.position; // make sure the nodes have a defined position
12969
12970 if (pos.x == null) {
12971 pos.x = 0;
12972 }
12973
12974 if (pos.y == null) {
12975 pos.y = 0;
12976 }
12977 }
12978
12979 if (_ele2.isEdge()) {
12980 // extra checks for edges
12981 var edge = _ele2;
12982 var fields = ['source', 'target'];
12983 var fieldsLength = fields.length;
12984 var badSourceOrTarget = false;
12985
12986 for (var j = 0; j < fieldsLength; j++) {
12987 var field = fields[j];
12988 var val = _data3[field];
12989
12990 if (number(val)) {
12991 val = _data3[field] = '' + _data3[field]; // now string
12992 }
12993
12994 if (val == null || val === '') {
12995 // can't create if source or target is not defined properly
12996 error('Can not create edge `' + id + '` with unspecified ' + field);
12997 badSourceOrTarget = true;
12998 } else if (!cy.hasElementWithId(val)) {
12999 // can't create edge if one of its nodes doesn't exist
13000 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13001 badSourceOrTarget = true;
13002 }
13003 }
13004
13005 if (badSourceOrTarget) {
13006 removeFromElements();
13007 continue;
13008 } // can't create this
13009
13010
13011 var src = cy.getElementById(_data3.source);
13012 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13013
13014 if (src.same(tgt)) {
13015 src._private.edges.push(edge);
13016 } else {
13017 src._private.edges.push(edge);
13018
13019 tgt._private.edges.push(edge);
13020 }
13021
13022 edge._private.source = src;
13023 edge._private.target = tgt;
13024 } // if is edge
13025 // create mock ids / indexes maps for element so it can be used like collections
13026
13027
13028 _private.map = new Map$1();
13029
13030 _private.map.set(id, {
13031 ele: _ele2,
13032 index: 0
13033 });
13034
13035 _private.removed = false;
13036
13037 if (addToPool) {
13038 cy.addToPool(_ele2);
13039 }
13040 } // for each element
13041 // do compound node sanity checks
13042
13043
13044 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13045 // each node
13046 var node = nodes[_i4];
13047 var _data4 = node._private.data;
13048
13049 if (number(_data4.parent)) {
13050 // then automake string
13051 _data4.parent = '' + _data4.parent;
13052 }
13053
13054 var parentId = _data4.parent;
13055 var specifiedParent = parentId != null;
13056
13057 if (specifiedParent) {
13058 var parent = cy.getElementById(parentId);
13059
13060 if (parent.empty()) {
13061 // non-existant parent; just remove it
13062 _data4.parent = undefined;
13063 } else {
13064 var selfAsParent = false;
13065 var ancestor = parent;
13066
13067 while (!ancestor.empty()) {
13068 if (node.same(ancestor)) {
13069 // mark self as parent and remove from data
13070 selfAsParent = true;
13071 _data4.parent = undefined; // remove parent reference
13072 // exit or we loop forever
13073
13074 break;
13075 }
13076
13077 ancestor = ancestor.parent();
13078 }
13079
13080 if (!selfAsParent) {
13081 // connect with children
13082 parent[0]._private.children.push(node);
13083
13084 node._private.parent = parent[0]; // let the core know we have a compound graph
13085
13086 cy_p.hasCompoundNodes = true;
13087 }
13088 } // else
13089
13090 } // if specified parent
13091
13092 } // for each node
13093
13094
13095 if (elements.length > 0) {
13096 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13097
13098 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13099 var _ele3 = restored[_i5];
13100
13101 if (_ele3.isNode()) {
13102 continue;
13103 } // adding an edge invalidates the traversal caches for the parallel edges
13104
13105
13106 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13107
13108
13109 _ele3.source().clearTraversalCache();
13110
13111 _ele3.target().clearTraversalCache();
13112 }
13113
13114 var toUpdateStyle;
13115
13116 if (cy_p.hasCompoundNodes) {
13117 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13118 } else {
13119 toUpdateStyle = restored;
13120 }
13121
13122 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13123
13124 if (notifyRenderer) {
13125 restored.emitAndNotify('add');
13126 } else if (addToPool) {
13127 restored.emit('add');
13128 }
13129 }
13130
13131 return self; // chainability
13132};
13133
13134elesfn$u.removed = function () {
13135 var ele = this[0];
13136 return ele && ele._private.removed;
13137};
13138
13139elesfn$u.inside = function () {
13140 var ele = this[0];
13141 return ele && !ele._private.removed;
13142};
13143
13144elesfn$u.remove = function () {
13145 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13146 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13147 var self = this;
13148 var elesToRemove = [];
13149 var elesToRemoveIds = {};
13150 var cy = self._private.cy; // add connected edges
13151
13152 function addConnectedEdges(node) {
13153 var edges = node._private.edges;
13154
13155 for (var i = 0; i < edges.length; i++) {
13156 add(edges[i]);
13157 }
13158 } // add descendant nodes
13159
13160
13161 function addChildren(node) {
13162 var children = node._private.children;
13163
13164 for (var i = 0; i < children.length; i++) {
13165 add(children[i]);
13166 }
13167 }
13168
13169 function add(ele) {
13170 var alreadyAdded = elesToRemoveIds[ele.id()];
13171
13172 if (removeFromPool && ele.removed() || alreadyAdded) {
13173 return;
13174 } else {
13175 elesToRemoveIds[ele.id()] = true;
13176 }
13177
13178 if (ele.isNode()) {
13179 elesToRemove.push(ele); // nodes are removed last
13180
13181 addConnectedEdges(ele);
13182 addChildren(ele);
13183 } else {
13184 elesToRemove.unshift(ele); // edges are removed first
13185 }
13186 } // make the list of elements to remove
13187 // (may be removing more than specified due to connected edges etc)
13188
13189
13190 for (var i = 0, l = self.length; i < l; i++) {
13191 var ele = self[i];
13192 add(ele);
13193 }
13194
13195 function removeEdgeRef(node, edge) {
13196 var connectedEdges = node._private.edges;
13197 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13198
13199 node.clearTraversalCache();
13200 }
13201
13202 function removeParallelRef(pllEdge) {
13203 // removing an edge invalidates the traversal caches for the parallel edges
13204 pllEdge.clearTraversalCache();
13205 }
13206
13207 var alteredParents = [];
13208 alteredParents.ids = {};
13209
13210 function removeChildRef(parent, ele) {
13211 ele = ele[0];
13212 parent = parent[0];
13213 var children = parent._private.children;
13214 var pid = parent.id();
13215 removeFromArray(children, ele); // remove parent => child ref
13216
13217 ele._private.parent = null; // remove child => parent ref
13218
13219 if (!alteredParents.ids[pid]) {
13220 alteredParents.ids[pid] = true;
13221 alteredParents.push(parent);
13222 }
13223 }
13224
13225 self.dirtyCompoundBoundsCache();
13226
13227 if (removeFromPool) {
13228 cy.removeFromPool(elesToRemove); // remove from core pool
13229 }
13230
13231 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13232 var _ele4 = elesToRemove[_i6];
13233
13234 if (_ele4.isEdge()) {
13235 // remove references to this edge in its connected nodes
13236 var src = _ele4.source()[0];
13237
13238 var tgt = _ele4.target()[0];
13239
13240 removeEdgeRef(src, _ele4);
13241 removeEdgeRef(tgt, _ele4);
13242
13243 var pllEdges = _ele4.parallelEdges();
13244
13245 for (var j = 0; j < pllEdges.length; j++) {
13246 var pllEdge = pllEdges[j];
13247 removeParallelRef(pllEdge);
13248
13249 if (pllEdge.isBundledBezier()) {
13250 pllEdge.dirtyBoundingBoxCache();
13251 }
13252 }
13253 } else {
13254 // remove reference to parent
13255 var parent = _ele4.parent();
13256
13257 if (parent.length !== 0) {
13258 removeChildRef(parent, _ele4);
13259 }
13260 }
13261
13262 if (removeFromPool) {
13263 // mark as removed
13264 _ele4._private.removed = true;
13265 }
13266 } // check to see if we have a compound graph or not
13267
13268
13269 var elesStillInside = cy._private.elements;
13270 cy._private.hasCompoundNodes = false;
13271
13272 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13273 var _ele5 = elesStillInside[_i7];
13274
13275 if (_ele5.isParent()) {
13276 cy._private.hasCompoundNodes = true;
13277 break;
13278 }
13279 }
13280
13281 var removedElements = new Collection(this.cy(), elesToRemove);
13282
13283 if (removedElements.size() > 0) {
13284 // must manually notify since trigger won't do this automatically once removed
13285 if (notifyRenderer) {
13286 removedElements.emitAndNotify('remove');
13287 } else if (removeFromPool) {
13288 removedElements.emit('remove');
13289 }
13290 } // the parents who were modified by the removal need their style updated
13291
13292
13293 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13294 var _ele6 = alteredParents[_i8];
13295
13296 if (!removeFromPool || !_ele6.removed()) {
13297 _ele6.updateStyle();
13298 }
13299 }
13300
13301 return removedElements;
13302};
13303
13304elesfn$u.move = function (struct) {
13305 var cy = this._private.cy;
13306 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13307 // (our calls to remove/restore do not remove from the graph or make events)
13308
13309 var notifyRenderer = false;
13310 var modifyPool = false;
13311
13312 var toString = function toString(id) {
13313 return id == null ? id : '' + id;
13314 }; // id must be string
13315
13316
13317 if (struct.source !== undefined || struct.target !== undefined) {
13318 var srcId = toString(struct.source);
13319 var tgtId = toString(struct.target);
13320 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13321 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13322
13323 if (srcExists || tgtExists) {
13324 cy.batch(function () {
13325 // avoid duplicate style updates
13326 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13327
13328 eles.emitAndNotify('moveout');
13329
13330 for (var i = 0; i < eles.length; i++) {
13331 var ele = eles[i];
13332 var _data5 = ele._private.data;
13333
13334 if (ele.isEdge()) {
13335 if (srcExists) {
13336 _data5.source = srcId;
13337 }
13338
13339 if (tgtExists) {
13340 _data5.target = tgtId;
13341 }
13342 }
13343 }
13344
13345 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13346 });
13347 eles.emitAndNotify('move');
13348 }
13349 } else if (struct.parent !== undefined) {
13350 // move node to new parent
13351 var parentId = toString(struct.parent);
13352 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13353
13354 if (parentExists) {
13355 var pidToAssign = parentId === null ? undefined : parentId;
13356 cy.batch(function () {
13357 // avoid duplicate style updates
13358 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13359
13360 updated.emitAndNotify('moveout');
13361
13362 for (var i = 0; i < eles.length; i++) {
13363 var ele = eles[i];
13364 var _data6 = ele._private.data;
13365
13366 if (ele.isNode()) {
13367 _data6.parent = pidToAssign;
13368 }
13369 }
13370
13371 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13372 });
13373 eles.emitAndNotify('move');
13374 }
13375 }
13376
13377 return this;
13378};
13379
13380[elesfn$c, elesfn$d, elesfn$e, elesfn$f, elesfn$g, data$1, elesfn$i, dimensions, elesfn$m, elesfn$n, elesfn$o, elesfn$p, elesfn$q, elesfn$r, elesfn$s, elesfn$t].forEach(function (props) {
13381 extend(elesfn$u, props);
13382});
13383
13384var corefn = {
13385 add: function add(opts) {
13386 var elements;
13387 var cy = this; // add the elements
13388
13389 if (elementOrCollection(opts)) {
13390 var eles = opts;
13391
13392 if (eles._private.cy === cy) {
13393 // same instance => just restore
13394 elements = eles.restore();
13395 } else {
13396 // otherwise, copy from json
13397 var jsons = [];
13398
13399 for (var i = 0; i < eles.length; i++) {
13400 var ele = eles[i];
13401 jsons.push(ele.json());
13402 }
13403
13404 elements = new Collection(cy, jsons);
13405 }
13406 } // specify an array of options
13407 else if (array(opts)) {
13408 var _jsons = opts;
13409 elements = new Collection(cy, _jsons);
13410 } // specify via opts.nodes and opts.edges
13411 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13412 var elesByGroup = opts;
13413 var _jsons2 = [];
13414 var grs = ['nodes', 'edges'];
13415
13416 for (var _i = 0, il = grs.length; _i < il; _i++) {
13417 var group = grs[_i];
13418 var elesArray = elesByGroup[group];
13419
13420 if (array(elesArray)) {
13421 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13422 var json = extend({
13423 group: group
13424 }, elesArray[j]);
13425
13426 _jsons2.push(json);
13427 }
13428 }
13429 }
13430
13431 elements = new Collection(cy, _jsons2);
13432 } // specify options for one element
13433 else {
13434 var _json = opts;
13435 elements = new Element(cy, _json).collection();
13436 }
13437
13438 return elements;
13439 },
13440 remove: function remove(collection) {
13441 if (elementOrCollection(collection)) ; else if (string(collection)) {
13442 var selector = collection;
13443 collection = this.$(selector);
13444 }
13445
13446 return collection.remove();
13447 }
13448};
13449
13450/* global Float32Array */
13451
13452/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13453function generateCubicBezier(mX1, mY1, mX2, mY2) {
13454 var NEWTON_ITERATIONS = 4,
13455 NEWTON_MIN_SLOPE = 0.001,
13456 SUBDIVISION_PRECISION = 0.0000001,
13457 SUBDIVISION_MAX_ITERATIONS = 10,
13458 kSplineTableSize = 11,
13459 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13460 float32ArraySupported = typeof Float32Array !== 'undefined';
13461 /* Must contain four arguments. */
13462
13463 if (arguments.length !== 4) {
13464 return false;
13465 }
13466 /* Arguments must be numbers. */
13467
13468
13469 for (var i = 0; i < 4; ++i) {
13470 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13471 return false;
13472 }
13473 }
13474 /* X values must be in the [0, 1] range. */
13475
13476
13477 mX1 = Math.min(mX1, 1);
13478 mX2 = Math.min(mX2, 1);
13479 mX1 = Math.max(mX1, 0);
13480 mX2 = Math.max(mX2, 0);
13481 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13482
13483 function A(aA1, aA2) {
13484 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13485 }
13486
13487 function B(aA1, aA2) {
13488 return 3.0 * aA2 - 6.0 * aA1;
13489 }
13490
13491 function C(aA1) {
13492 return 3.0 * aA1;
13493 }
13494
13495 function calcBezier(aT, aA1, aA2) {
13496 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13497 }
13498
13499 function getSlope(aT, aA1, aA2) {
13500 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13501 }
13502
13503 function newtonRaphsonIterate(aX, aGuessT) {
13504 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13505 var currentSlope = getSlope(aGuessT, mX1, mX2);
13506
13507 if (currentSlope === 0.0) {
13508 return aGuessT;
13509 }
13510
13511 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13512 aGuessT -= currentX / currentSlope;
13513 }
13514
13515 return aGuessT;
13516 }
13517
13518 function calcSampleValues() {
13519 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13520 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13521 }
13522 }
13523
13524 function binarySubdivide(aX, aA, aB) {
13525 var currentX,
13526 currentT,
13527 i = 0;
13528
13529 do {
13530 currentT = aA + (aB - aA) / 2.0;
13531 currentX = calcBezier(currentT, mX1, mX2) - aX;
13532
13533 if (currentX > 0.0) {
13534 aB = currentT;
13535 } else {
13536 aA = currentT;
13537 }
13538 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13539
13540 return currentT;
13541 }
13542
13543 function getTForX(aX) {
13544 var intervalStart = 0.0,
13545 currentSample = 1,
13546 lastSample = kSplineTableSize - 1;
13547
13548 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13549 intervalStart += kSampleStepSize;
13550 }
13551
13552 --currentSample;
13553 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13554 guessForT = intervalStart + dist * kSampleStepSize,
13555 initialSlope = getSlope(guessForT, mX1, mX2);
13556
13557 if (initialSlope >= NEWTON_MIN_SLOPE) {
13558 return newtonRaphsonIterate(aX, guessForT);
13559 } else if (initialSlope === 0.0) {
13560 return guessForT;
13561 } else {
13562 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13563 }
13564 }
13565
13566 var _precomputed = false;
13567
13568 function precompute() {
13569 _precomputed = true;
13570
13571 if (mX1 !== mY1 || mX2 !== mY2) {
13572 calcSampleValues();
13573 }
13574 }
13575
13576 var f = function f(aX) {
13577 if (!_precomputed) {
13578 precompute();
13579 }
13580
13581 if (mX1 === mY1 && mX2 === mY2) {
13582 return aX;
13583 }
13584
13585 if (aX === 0) {
13586 return 0;
13587 }
13588
13589 if (aX === 1) {
13590 return 1;
13591 }
13592
13593 return calcBezier(getTForX(aX), mY1, mY2);
13594 };
13595
13596 f.getControlPoints = function () {
13597 return [{
13598 x: mX1,
13599 y: mY1
13600 }, {
13601 x: mX2,
13602 y: mY2
13603 }];
13604 };
13605
13606 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13607
13608 f.toString = function () {
13609 return str;
13610 };
13611
13612 return f;
13613}
13614
13615/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13616
13617/* 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
13618 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13619var generateSpringRK4 = function () {
13620 function springAccelerationForState(state) {
13621 return -state.tension * state.x - state.friction * state.v;
13622 }
13623
13624 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13625 var state = {
13626 x: initialState.x + derivative.dx * dt,
13627 v: initialState.v + derivative.dv * dt,
13628 tension: initialState.tension,
13629 friction: initialState.friction
13630 };
13631 return {
13632 dx: state.v,
13633 dv: springAccelerationForState(state)
13634 };
13635 }
13636
13637 function springIntegrateState(state, dt) {
13638 var a = {
13639 dx: state.v,
13640 dv: springAccelerationForState(state)
13641 },
13642 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13643 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13644 d = springEvaluateStateWithDerivative(state, dt, c),
13645 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13646 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13647 state.x = state.x + dxdt * dt;
13648 state.v = state.v + dvdt * dt;
13649 return state;
13650 }
13651
13652 return function springRK4Factory(tension, friction, duration) {
13653 var initState = {
13654 x: -1,
13655 v: 0,
13656 tension: null,
13657 friction: null
13658 },
13659 path = [0],
13660 time_lapsed = 0,
13661 tolerance = 1 / 10000,
13662 DT = 16 / 1000,
13663 have_duration,
13664 dt,
13665 last_state;
13666 tension = parseFloat(tension) || 500;
13667 friction = parseFloat(friction) || 20;
13668 duration = duration || null;
13669 initState.tension = tension;
13670 initState.friction = friction;
13671 have_duration = duration !== null;
13672 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13673
13674 if (have_duration) {
13675 /* Run the simulation without a duration. */
13676 time_lapsed = springRK4Factory(tension, friction);
13677 /* Compute the adjusted time delta. */
13678
13679 dt = time_lapsed / duration * DT;
13680 } else {
13681 dt = DT;
13682 }
13683
13684 for (;;) {
13685 /* Next/step function .*/
13686 last_state = springIntegrateState(last_state || initState, dt);
13687 /* Store the position. */
13688
13689 path.push(1 + last_state.x);
13690 time_lapsed += 16;
13691 /* If the change threshold is reached, break. */
13692
13693 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13694 break;
13695 }
13696 }
13697 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13698 computed path and returns a snapshot of the position according to a given percentComplete. */
13699
13700
13701 return !have_duration ? time_lapsed : function (percentComplete) {
13702 return path[percentComplete * (path.length - 1) | 0];
13703 };
13704 };
13705}();
13706
13707var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13708 var bezier = generateCubicBezier(t1, p1, t2, p2);
13709 return function (start, end, percent) {
13710 return start + (end - start) * bezier(percent);
13711 };
13712};
13713
13714var easings = {
13715 'linear': function linear(start, end, percent) {
13716 return start + (end - start) * percent;
13717 },
13718 // default easings
13719 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13720 'ease-in': cubicBezier(0.42, 0, 1, 1),
13721 'ease-out': cubicBezier(0, 0, 0.58, 1),
13722 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13723 // sine
13724 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13725 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13726 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13727 // quad
13728 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13729 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13730 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13731 // cubic
13732 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13733 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13734 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13735 // quart
13736 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13737 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13738 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13739 // quint
13740 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13741 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13742 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13743 // expo
13744 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13745 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13746 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13747 // circ
13748 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13749 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13750 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13751 // user param easings...
13752 'spring': function spring(tension, friction, duration) {
13753 if (duration === 0) {
13754 // can't get a spring w/ duration 0
13755 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13756 }
13757
13758 var spring = generateSpringRK4(tension, friction, duration);
13759 return function (start, end, percent) {
13760 return start + (end - start) * spring(percent);
13761 };
13762 },
13763 'cubic-bezier': cubicBezier
13764};
13765
13766function getEasedValue(type, start, end, percent, easingFn) {
13767 if (percent === 1) {
13768 return end;
13769 }
13770
13771 if (start === end) {
13772 return end;
13773 }
13774
13775 var val = easingFn(start, end, percent);
13776
13777 if (type == null) {
13778 return val;
13779 }
13780
13781 if (type.roundValue || type.color) {
13782 val = Math.round(val);
13783 }
13784
13785 if (type.min !== undefined) {
13786 val = Math.max(val, type.min);
13787 }
13788
13789 if (type.max !== undefined) {
13790 val = Math.min(val, type.max);
13791 }
13792
13793 return val;
13794}
13795
13796function getValue(prop, spec) {
13797 if (prop.pfValue != null || prop.value != null) {
13798 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13799 return prop.pfValue;
13800 } else {
13801 return prop.value;
13802 }
13803 } else {
13804 return prop;
13805 }
13806}
13807
13808function ease(startProp, endProp, percent, easingFn, propSpec) {
13809 var type = propSpec != null ? propSpec.type : null;
13810
13811 if (percent < 0) {
13812 percent = 0;
13813 } else if (percent > 1) {
13814 percent = 1;
13815 }
13816
13817 var start = getValue(startProp, propSpec);
13818 var end = getValue(endProp, propSpec);
13819
13820 if (number(start) && number(end)) {
13821 return getEasedValue(type, start, end, percent, easingFn);
13822 } else if (array(start) && array(end)) {
13823 var easedArr = [];
13824
13825 for (var i = 0; i < end.length; i++) {
13826 var si = start[i];
13827 var ei = end[i];
13828
13829 if (si != null && ei != null) {
13830 var val = getEasedValue(type, si, ei, percent, easingFn);
13831 easedArr.push(val);
13832 } else {
13833 easedArr.push(ei);
13834 }
13835 }
13836
13837 return easedArr;
13838 }
13839
13840 return undefined;
13841}
13842
13843function step(self, ani, now, isCore) {
13844 var isEles = !isCore;
13845 var _p = self._private;
13846 var ani_p = ani._private;
13847 var pEasing = ani_p.easing;
13848 var startTime = ani_p.startTime;
13849 var cy = isCore ? self : self.cy();
13850 var style = cy.style();
13851
13852 if (!ani_p.easingImpl) {
13853 if (pEasing == null) {
13854 // use default
13855 ani_p.easingImpl = easings['linear'];
13856 } else {
13857 // then define w/ name
13858 var easingVals;
13859
13860 if (string(pEasing)) {
13861 var easingProp = style.parse('transition-timing-function', pEasing);
13862 easingVals = easingProp.value;
13863 } else {
13864 // then assume preparsed array
13865 easingVals = pEasing;
13866 }
13867
13868 var name, args;
13869
13870 if (string(easingVals)) {
13871 name = easingVals;
13872 args = [];
13873 } else {
13874 name = easingVals[1];
13875 args = easingVals.slice(2).map(function (n) {
13876 return +n;
13877 });
13878 }
13879
13880 if (args.length > 0) {
13881 // create with args
13882 if (name === 'spring') {
13883 args.push(ani_p.duration); // need duration to generate spring
13884 }
13885
13886 ani_p.easingImpl = easings[name].apply(null, args);
13887 } else {
13888 // static impl by name
13889 ani_p.easingImpl = easings[name];
13890 }
13891 }
13892 }
13893
13894 var easing = ani_p.easingImpl;
13895 var percent;
13896
13897 if (ani_p.duration === 0) {
13898 percent = 1;
13899 } else {
13900 percent = (now - startTime) / ani_p.duration;
13901 }
13902
13903 if (ani_p.applying) {
13904 percent = ani_p.progress;
13905 }
13906
13907 if (percent < 0) {
13908 percent = 0;
13909 } else if (percent > 1) {
13910 percent = 1;
13911 }
13912
13913 if (ani_p.delay == null) {
13914 // then update
13915 var startPos = ani_p.startPosition;
13916 var endPos = ani_p.position;
13917
13918 if (endPos && isEles && !self.locked()) {
13919 var newPos = {};
13920
13921 if (valid(startPos.x, endPos.x)) {
13922 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13923 }
13924
13925 if (valid(startPos.y, endPos.y)) {
13926 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13927 }
13928
13929 self.position(newPos);
13930 }
13931
13932 var startPan = ani_p.startPan;
13933 var endPan = ani_p.pan;
13934 var pan = _p.pan;
13935 var animatingPan = endPan != null && isCore;
13936
13937 if (animatingPan) {
13938 if (valid(startPan.x, endPan.x)) {
13939 pan.x = ease(startPan.x, endPan.x, percent, easing);
13940 }
13941
13942 if (valid(startPan.y, endPan.y)) {
13943 pan.y = ease(startPan.y, endPan.y, percent, easing);
13944 }
13945
13946 self.emit('pan');
13947 }
13948
13949 var startZoom = ani_p.startZoom;
13950 var endZoom = ani_p.zoom;
13951 var animatingZoom = endZoom != null && isCore;
13952
13953 if (animatingZoom) {
13954 if (valid(startZoom, endZoom)) {
13955 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13956 }
13957
13958 self.emit('zoom');
13959 }
13960
13961 if (animatingPan || animatingZoom) {
13962 self.emit('viewport');
13963 }
13964
13965 var props = ani_p.style;
13966
13967 if (props && props.length > 0 && isEles) {
13968 for (var i = 0; i < props.length; i++) {
13969 var prop = props[i];
13970 var _name = prop.name;
13971 var end = prop;
13972 var start = ani_p.startStyle[_name];
13973 var propSpec = style.properties[start.name];
13974 var easedVal = ease(start, end, percent, easing, propSpec);
13975 style.overrideBypass(self, _name, easedVal);
13976 } // for props
13977
13978
13979 self.emit('style');
13980 } // if
13981
13982 }
13983
13984 ani_p.progress = percent;
13985 return percent;
13986}
13987
13988function valid(start, end) {
13989 if (start == null || end == null) {
13990 return false;
13991 }
13992
13993 if (number(start) && number(end)) {
13994 return true;
13995 } else if (start && end) {
13996 return true;
13997 }
13998
13999 return false;
14000}
14001
14002function startAnimation(self, ani, now, isCore) {
14003 var ani_p = ani._private;
14004 ani_p.started = true;
14005 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14006}
14007
14008function stepAll(now, cy) {
14009 var eles = cy._private.aniEles;
14010 var doneEles = [];
14011
14012 function stepOne(ele, isCore) {
14013 var _p = ele._private;
14014 var current = _p.animation.current;
14015 var queue = _p.animation.queue;
14016 var ranAnis = false; // if nothing currently animating, get something from the queue
14017
14018 if (current.length === 0) {
14019 var next = queue.shift();
14020
14021 if (next) {
14022 current.push(next);
14023 }
14024 }
14025
14026 var callbacks = function callbacks(_callbacks) {
14027 for (var j = _callbacks.length - 1; j >= 0; j--) {
14028 var cb = _callbacks[j];
14029 cb();
14030 }
14031
14032 _callbacks.splice(0, _callbacks.length);
14033 }; // step and remove if done
14034
14035
14036 for (var i = current.length - 1; i >= 0; i--) {
14037 var ani = current[i];
14038 var ani_p = ani._private;
14039
14040 if (ani_p.stopped) {
14041 current.splice(i, 1);
14042 ani_p.hooked = false;
14043 ani_p.playing = false;
14044 ani_p.started = false;
14045 callbacks(ani_p.frames);
14046 continue;
14047 }
14048
14049 if (!ani_p.playing && !ani_p.applying) {
14050 continue;
14051 } // an apply() while playing shouldn't do anything
14052
14053
14054 if (ani_p.playing && ani_p.applying) {
14055 ani_p.applying = false;
14056 }
14057
14058 if (!ani_p.started) {
14059 startAnimation(ele, ani, now);
14060 }
14061
14062 step(ele, ani, now, isCore);
14063
14064 if (ani_p.applying) {
14065 ani_p.applying = false;
14066 }
14067
14068 callbacks(ani_p.frames);
14069
14070 if (ani_p.step != null) {
14071 ani_p.step(now);
14072 }
14073
14074 if (ani.completed()) {
14075 current.splice(i, 1);
14076 ani_p.hooked = false;
14077 ani_p.playing = false;
14078 ani_p.started = false;
14079 callbacks(ani_p.completes);
14080 }
14081
14082 ranAnis = true;
14083 }
14084
14085 if (!isCore && current.length === 0 && queue.length === 0) {
14086 doneEles.push(ele);
14087 }
14088
14089 return ranAnis;
14090 } // stepElement
14091 // handle all eles
14092
14093
14094 var ranEleAni = false;
14095
14096 for (var e = 0; e < eles.length; e++) {
14097 var ele = eles[e];
14098 var handledThisEle = stepOne(ele);
14099 ranEleAni = ranEleAni || handledThisEle;
14100 } // each element
14101
14102
14103 var ranCoreAni = stepOne(cy, true); // notify renderer
14104
14105 if (ranEleAni || ranCoreAni) {
14106 if (eles.length > 0) {
14107 cy.notify('draw', eles);
14108 } else {
14109 cy.notify('draw');
14110 }
14111 } // remove elements from list of currently animating if its queues are empty
14112
14113
14114 eles.unmerge(doneEles);
14115 cy.emit('step');
14116} // stepAll
14117
14118var corefn$1 = {
14119 // pull in animation functions
14120 animate: define$3.animate(),
14121 animation: define$3.animation(),
14122 animated: define$3.animated(),
14123 clearQueue: define$3.clearQueue(),
14124 delay: define$3.delay(),
14125 delayAnimation: define$3.delayAnimation(),
14126 stop: define$3.stop(),
14127 addToAnimationPool: function addToAnimationPool(eles) {
14128 var cy = this;
14129
14130 if (!cy.styleEnabled()) {
14131 return;
14132 } // save cycles when no style used
14133
14134
14135 cy._private.aniEles.merge(eles);
14136 },
14137 stopAnimationLoop: function stopAnimationLoop() {
14138 this._private.animationsRunning = false;
14139 },
14140 startAnimationLoop: function startAnimationLoop() {
14141 var cy = this;
14142 cy._private.animationsRunning = true;
14143
14144 if (!cy.styleEnabled()) {
14145 return;
14146 } // save cycles when no style used
14147 // NB the animation loop will exec in headless environments if style enabled
14148 // and explicit cy.destroy() is necessary to stop the loop
14149
14150
14151 function headlessStep() {
14152 if (!cy._private.animationsRunning) {
14153 return;
14154 }
14155
14156 requestAnimationFrame(function animationStep(now) {
14157 stepAll(now, cy);
14158 headlessStep();
14159 });
14160 }
14161
14162 var renderer = cy.renderer();
14163
14164 if (renderer && renderer.beforeRender) {
14165 // let the renderer schedule animations
14166 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14167 stepAll(now, cy);
14168 }, renderer.beforeRenderPriorities.animations);
14169 } else {
14170 // manage the animation loop ourselves
14171 headlessStep(); // first call
14172 }
14173 }
14174};
14175
14176var emitterOptions$1 = {
14177 qualifierCompare: function qualifierCompare(selector1, selector2) {
14178 if (selector1 == null || selector2 == null) {
14179 return selector1 == null && selector2 == null;
14180 } else {
14181 return selector1.sameText(selector2);
14182 }
14183 },
14184 eventMatches: function eventMatches(cy, listener, eventObj) {
14185 var selector = listener.qualifier;
14186
14187 if (selector != null) {
14188 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14189 }
14190
14191 return true;
14192 },
14193 addEventFields: function addEventFields(cy, evt) {
14194 evt.cy = cy;
14195 evt.target = cy;
14196 },
14197 callbackContext: function callbackContext(cy, listener, eventObj) {
14198 return listener.qualifier != null ? eventObj.target : cy;
14199 }
14200};
14201
14202var argSelector$1 = function argSelector(arg) {
14203 if (string(arg)) {
14204 return new Selector(arg);
14205 } else {
14206 return arg;
14207 }
14208};
14209
14210var elesfn$v = {
14211 createEmitter: function createEmitter() {
14212 var _p = this._private;
14213
14214 if (!_p.emitter) {
14215 _p.emitter = new Emitter(emitterOptions$1, this);
14216 }
14217
14218 return this;
14219 },
14220 emitter: function emitter() {
14221 return this._private.emitter;
14222 },
14223 on: function on(events, selector, callback) {
14224 this.emitter().on(events, argSelector$1(selector), callback);
14225 return this;
14226 },
14227 removeListener: function removeListener(events, selector, callback) {
14228 this.emitter().removeListener(events, argSelector$1(selector), callback);
14229 return this;
14230 },
14231 removeAllListeners: function removeAllListeners() {
14232 this.emitter().removeAllListeners();
14233 return this;
14234 },
14235 one: function one(events, selector, callback) {
14236 this.emitter().one(events, argSelector$1(selector), callback);
14237 return this;
14238 },
14239 once: function once(events, selector, callback) {
14240 this.emitter().one(events, argSelector$1(selector), callback);
14241 return this;
14242 },
14243 emit: function emit(events, extraParams) {
14244 this.emitter().emit(events, extraParams);
14245 return this;
14246 },
14247 emitAndNotify: function emitAndNotify(event, eles) {
14248 this.emit(event);
14249 this.notify(event, eles);
14250 return this;
14251 }
14252};
14253define$3.eventAliasesOn(elesfn$v);
14254
14255var corefn$2 = {
14256 png: function png(options) {
14257 var renderer = this._private.renderer;
14258 options = options || {};
14259 return renderer.png(options);
14260 },
14261 jpg: function jpg(options) {
14262 var renderer = this._private.renderer;
14263 options = options || {};
14264 options.bg = options.bg || '#fff';
14265 return renderer.jpg(options);
14266 }
14267};
14268corefn$2.jpeg = corefn$2.jpg;
14269
14270var corefn$3 = {
14271 layout: function layout(options) {
14272 var cy = this;
14273
14274 if (options == null) {
14275 error('Layout options must be specified to make a layout');
14276 return;
14277 }
14278
14279 if (options.name == null) {
14280 error('A `name` must be specified to make a layout');
14281 return;
14282 }
14283
14284 var name = options.name;
14285 var Layout = cy.extension('layout', name);
14286
14287 if (Layout == null) {
14288 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14289 return;
14290 }
14291
14292 var eles;
14293
14294 if (string(options.eles)) {
14295 eles = cy.$(options.eles);
14296 } else {
14297 eles = options.eles != null ? options.eles : cy.$();
14298 }
14299
14300 var layout = new Layout(extend({}, options, {
14301 cy: cy,
14302 eles: eles
14303 }));
14304 return layout;
14305 }
14306};
14307corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14308
14309var corefn$4 = {
14310 notify: function notify(eventName, eventEles) {
14311 var _p = this._private;
14312
14313 if (this.batching()) {
14314 _p.batchNotifications = _p.batchNotifications || {};
14315 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14316
14317 if (eventEles != null) {
14318 eles.merge(eventEles);
14319 }
14320
14321 return; // notifications are disabled during batching
14322 }
14323
14324 if (!_p.notificationsEnabled) {
14325 return;
14326 } // exit on disabled
14327
14328
14329 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14330
14331 if (this.destroyed() || !renderer) {
14332 return;
14333 }
14334
14335 renderer.notify(eventName, eventEles);
14336 },
14337 notifications: function notifications(bool) {
14338 var p = this._private;
14339
14340 if (bool === undefined) {
14341 return p.notificationsEnabled;
14342 } else {
14343 p.notificationsEnabled = bool ? true : false;
14344 }
14345
14346 return this;
14347 },
14348 noNotifications: function noNotifications(callback) {
14349 this.notifications(false);
14350 callback();
14351 this.notifications(true);
14352 },
14353 batching: function batching() {
14354 return this._private.batchCount > 0;
14355 },
14356 startBatch: function startBatch() {
14357 var _p = this._private;
14358
14359 if (_p.batchCount == null) {
14360 _p.batchCount = 0;
14361 }
14362
14363 if (_p.batchCount === 0) {
14364 _p.batchStyleEles = this.collection();
14365 _p.batchNotifications = {};
14366 }
14367
14368 _p.batchCount++;
14369 return this;
14370 },
14371 endBatch: function endBatch() {
14372 var _p = this._private;
14373
14374 if (_p.batchCount === 0) {
14375 return this;
14376 }
14377
14378 _p.batchCount--;
14379
14380 if (_p.batchCount === 0) {
14381 // update style for dirty eles
14382 _p.batchStyleEles.updateStyle();
14383
14384 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14385
14386 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14387 var eles = _p.batchNotifications[eventName];
14388
14389 if (eles.empty()) {
14390 renderer.notify(eventName);
14391 } else {
14392 renderer.notify(eventName, eles);
14393 }
14394 });
14395 }
14396
14397 return this;
14398 },
14399 batch: function batch(callback) {
14400 this.startBatch();
14401 callback();
14402 this.endBatch();
14403 return this;
14404 },
14405 // for backwards compatibility
14406 batchData: function batchData(map) {
14407 var cy = this;
14408 return this.batch(function () {
14409 var ids = Object.keys(map);
14410
14411 for (var i = 0; i < ids.length; i++) {
14412 var id = ids[i];
14413 var data = map[id];
14414 var ele = cy.getElementById(id);
14415 ele.data(data);
14416 }
14417 });
14418 }
14419};
14420
14421var rendererDefaults = defaults({
14422 hideEdgesOnViewport: false,
14423 textureOnViewport: false,
14424 motionBlur: false,
14425 motionBlurOpacity: 0.05,
14426 pixelRatio: undefined,
14427 desktopTapThreshold: 4,
14428 touchTapThreshold: 8,
14429 wheelSensitivity: 1,
14430 debug: false,
14431 showFps: false
14432});
14433var corefn$5 = {
14434 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14435 var r = this._private.renderer;
14436 r.renderTo(context, zoom, pan, pxRatio);
14437 return this;
14438 },
14439 renderer: function renderer() {
14440 return this._private.renderer;
14441 },
14442 forceRender: function forceRender() {
14443 this.notify('draw');
14444 return this;
14445 },
14446 resize: function resize() {
14447 this.invalidateSize();
14448 this.emitAndNotify('resize');
14449 return this;
14450 },
14451 initRenderer: function initRenderer(options) {
14452 var cy = this;
14453 var RendererProto = cy.extension('renderer', options.name);
14454
14455 if (RendererProto == null) {
14456 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14457 return;
14458 }
14459
14460 if (options.wheelSensitivity !== undefined) {
14461 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.");
14462 }
14463
14464 var rOpts = rendererDefaults(options);
14465 rOpts.cy = cy;
14466 cy._private.renderer = new RendererProto(rOpts);
14467 this.notify('init');
14468 },
14469 destroyRenderer: function destroyRenderer() {
14470 var cy = this;
14471 cy.notify('destroy'); // destroy the renderer
14472
14473 var domEle = cy.container();
14474
14475 if (domEle) {
14476 domEle._cyreg = null;
14477
14478 while (domEle.childNodes.length > 0) {
14479 domEle.removeChild(domEle.childNodes[0]);
14480 }
14481 }
14482
14483 cy._private.renderer = null; // to be extra safe, remove the ref
14484
14485 cy.mutableElements().forEach(function (ele) {
14486 var _p = ele._private;
14487 _p.rscratch = {};
14488 _p.rstyle = {};
14489 _p.animation.current = [];
14490 _p.animation.queue = [];
14491 });
14492 },
14493 onRender: function onRender(fn) {
14494 return this.on('render', fn);
14495 },
14496 offRender: function offRender(fn) {
14497 return this.off('render', fn);
14498 }
14499};
14500corefn$5.invalidateDimensions = corefn$5.resize;
14501
14502var corefn$6 = {
14503 // get a collection
14504 // - empty collection on no args
14505 // - collection of elements in the graph on selector arg
14506 // - guarantee a returned collection when elements or collection specified
14507 collection: function collection(eles, opts) {
14508 if (string(eles)) {
14509 return this.$(eles);
14510 } else if (elementOrCollection(eles)) {
14511 return eles.collection();
14512 } else if (array(eles)) {
14513 return new Collection(this, eles, opts);
14514 }
14515
14516 return new Collection(this);
14517 },
14518 nodes: function nodes(selector) {
14519 var nodes = this.$(function (ele) {
14520 return ele.isNode();
14521 });
14522
14523 if (selector) {
14524 return nodes.filter(selector);
14525 }
14526
14527 return nodes;
14528 },
14529 edges: function edges(selector) {
14530 var edges = this.$(function (ele) {
14531 return ele.isEdge();
14532 });
14533
14534 if (selector) {
14535 return edges.filter(selector);
14536 }
14537
14538 return edges;
14539 },
14540 // search the graph like jQuery
14541 $: function $(selector) {
14542 var eles = this._private.elements;
14543
14544 if (selector) {
14545 return eles.filter(selector);
14546 } else {
14547 return eles.spawnSelf();
14548 }
14549 },
14550 mutableElements: function mutableElements() {
14551 return this._private.elements;
14552 }
14553}; // aliases
14554
14555corefn$6.elements = corefn$6.filter = corefn$6.$;
14556
14557var styfn = {}; // keys for style blocks, e.g. ttfftt
14558
14559var TRUE = 't';
14560var FALSE = 'f'; // (potentially expensive calculation)
14561// apply the style to the element based on
14562// - its bypass
14563// - what selectors match it
14564
14565styfn.apply = function (eles) {
14566 var self = this;
14567 var _p = self._private;
14568 var cy = _p.cy;
14569 var updatedEles = cy.collection();
14570
14571 for (var ie = 0; ie < eles.length; ie++) {
14572 var ele = eles[ie];
14573 var cxtMeta = self.getContextMeta(ele);
14574
14575 if (cxtMeta.empty) {
14576 continue;
14577 }
14578
14579 var cxtStyle = self.getContextStyle(cxtMeta);
14580 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14581
14582 if (ele._private.appliedInitStyle) {
14583 self.updateTransitions(ele, app.diffProps);
14584 } else {
14585 ele._private.appliedInitStyle = true;
14586 }
14587
14588 var hintsDiff = self.updateStyleHints(ele);
14589
14590 if (hintsDiff) {
14591 updatedEles.push(ele);
14592 }
14593 } // for elements
14594
14595
14596 return updatedEles;
14597};
14598
14599styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14600 var self = this;
14601 var cache = self._private.propDiffs = self._private.propDiffs || {};
14602 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14603 var cachedVal = cache[dualCxtKey];
14604
14605 if (cachedVal) {
14606 return cachedVal;
14607 }
14608
14609 var diffProps = [];
14610 var addedProp = {};
14611
14612 for (var i = 0; i < self.length; i++) {
14613 var cxt = self[i];
14614 var oldHasCxt = oldCxtKey[i] === TRUE;
14615 var newHasCxt = newCxtKey[i] === TRUE;
14616 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14617 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14618
14619 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14620 var props = void 0;
14621
14622 if (cxtHasDiffed && cxtHasMappedProps) {
14623 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14624 } else if (cxtHasDiffed) {
14625 props = cxt.properties; // need to check them all
14626 } else if (cxtHasMappedProps) {
14627 props = cxt.mappedProperties; // only need to check mapped
14628 }
14629
14630 for (var j = 0; j < props.length; j++) {
14631 var prop = props[j];
14632 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14633 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14634 // is cached)
14635
14636 var laterCxtOverrides = false;
14637
14638 for (var k = i + 1; k < self.length; k++) {
14639 var laterCxt = self[k];
14640 var hasLaterCxt = newCxtKey[k] === TRUE;
14641
14642 if (!hasLaterCxt) {
14643 continue;
14644 } // can't override unless the context is active
14645
14646
14647 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14648
14649 if (laterCxtOverrides) {
14650 break;
14651 } // exit early as long as one later context overrides
14652
14653 }
14654
14655 if (!addedProp[name] && !laterCxtOverrides) {
14656 addedProp[name] = true;
14657 diffProps.push(name);
14658 }
14659 } // for props
14660
14661 } // if
14662
14663 } // for contexts
14664
14665
14666 cache[dualCxtKey] = diffProps;
14667 return diffProps;
14668};
14669
14670styfn.getContextMeta = function (ele) {
14671 var self = this;
14672 var cxtKey = '';
14673 var diffProps;
14674 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14675
14676 for (var i = 0; i < self.length; i++) {
14677 var context = self[i];
14678 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14679
14680 if (contextSelectorMatches) {
14681 cxtKey += TRUE;
14682 } else {
14683 cxtKey += FALSE;
14684 }
14685 } // for context
14686
14687
14688 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14689 ele._private.styleCxtKey = cxtKey;
14690 return {
14691 key: cxtKey,
14692 diffPropNames: diffProps,
14693 empty: diffProps.length === 0
14694 };
14695}; // gets a computed ele style object based on matched contexts
14696
14697
14698styfn.getContextStyle = function (cxtMeta) {
14699 var cxtKey = cxtMeta.key;
14700 var self = this;
14701 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14702
14703 if (cxtStyles[cxtKey]) {
14704 return cxtStyles[cxtKey];
14705 }
14706
14707 var style = {
14708 _private: {
14709 key: cxtKey
14710 }
14711 };
14712
14713 for (var i = 0; i < self.length; i++) {
14714 var cxt = self[i];
14715 var hasCxt = cxtKey[i] === TRUE;
14716
14717 if (!hasCxt) {
14718 continue;
14719 }
14720
14721 for (var j = 0; j < cxt.properties.length; j++) {
14722 var prop = cxt.properties[j];
14723 style[prop.name] = prop;
14724 }
14725 }
14726
14727 cxtStyles[cxtKey] = style;
14728 return style;
14729};
14730
14731styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14732 var self = this;
14733 var diffProps = cxtMeta.diffPropNames;
14734 var retDiffProps = {};
14735 var types = self.types;
14736
14737 for (var i = 0; i < diffProps.length; i++) {
14738 var diffPropName = diffProps[i];
14739 var cxtProp = cxtStyle[diffPropName];
14740 var eleProp = ele.pstyle(diffPropName);
14741
14742 if (!cxtProp) {
14743 // no context prop means delete
14744 if (!eleProp) {
14745 continue; // no existing prop means nothing needs to be removed
14746 // nb affects initial application on mapped values like control-point-distances
14747 } else if (eleProp.bypass) {
14748 cxtProp = {
14749 name: diffPropName,
14750 deleteBypassed: true
14751 };
14752 } else {
14753 cxtProp = {
14754 name: diffPropName,
14755 "delete": true
14756 };
14757 }
14758 } // save cycles when the context prop doesn't need to be applied
14759
14760
14761 if (eleProp === cxtProp) {
14762 continue;
14763 } // save cycles when a mapped context prop doesn't need to be applied
14764
14765
14766 if (cxtProp.mapped === types.fn // context prop is function mapper
14767 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14768 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14769 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14770 ) {
14771 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14772 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14773
14774 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14775
14776 if (fnValue === mapping.prevFnValue) {
14777 continue;
14778 }
14779 }
14780
14781 var retDiffProp = retDiffProps[diffPropName] = {
14782 prev: eleProp
14783 };
14784 self.applyParsedProperty(ele, cxtProp);
14785 retDiffProp.next = ele.pstyle(diffPropName);
14786
14787 if (retDiffProp.next && retDiffProp.next.bypass) {
14788 retDiffProp.next = retDiffProp.next.bypassed;
14789 }
14790 }
14791
14792 return {
14793 diffProps: retDiffProps
14794 };
14795};
14796
14797styfn.updateStyleHints = function (ele) {
14798 var _p = ele._private;
14799 var self = this;
14800 var propNames = self.propertyGroupNames;
14801 var propGrKeys = self.propertyGroupKeys;
14802
14803 var propHash = function propHash(ele, propNames, seedKey) {
14804 return self.getPropertiesHash(ele, propNames, seedKey);
14805 };
14806
14807 var oldStyleKey = _p.styleKey;
14808
14809 if (ele.removed()) {
14810 return false;
14811 }
14812
14813 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14814 // but lazily -- only use non-default prop values to reduce the number of hashes
14815 //
14816
14817 var overriddenStyles = ele._private.style;
14818 propNames = Object.keys(overriddenStyles);
14819
14820 for (var i = 0; i < propGrKeys.length; i++) {
14821 var grKey = propGrKeys[i];
14822 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14823 }
14824
14825 var updateGrKey1 = function updateGrKey1(val, grKey) {
14826 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14827 };
14828
14829 var updateGrKey2 = function updateGrKey2(val, grKey) {
14830 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14831 };
14832
14833 var updateGrKey = function updateGrKey(val, grKey) {
14834 updateGrKey1(val, grKey);
14835 updateGrKey2(val, grKey);
14836 };
14837
14838 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14839 for (var j = 0; j < strVal.length; j++) {
14840 var ch = strVal.charCodeAt(j);
14841 updateGrKey1(ch, grKey);
14842 updateGrKey2(ch, grKey);
14843 }
14844 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14845 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14846 // - raise up small numbers so more significant digits are seen by hashing
14847 // - make small numbers larger than a normal value to avoid collisions
14848 // - works in practice and it's relatively cheap
14849
14850
14851 var N = 2000000000;
14852
14853 var cleanNum = function cleanNum(val) {
14854 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14855 };
14856
14857 for (var _i = 0; _i < propNames.length; _i++) {
14858 var name = propNames[_i];
14859 var parsedProp = overriddenStyles[name];
14860
14861 if (parsedProp == null) {
14862 continue;
14863 }
14864
14865 var propInfo = this.properties[name];
14866 var type = propInfo.type;
14867 var _grKey = propInfo.groupKey;
14868 var normalizedNumberVal = void 0;
14869
14870 if (propInfo.hashOverride != null) {
14871 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14872 } else if (parsedProp.pfValue != null) {
14873 normalizedNumberVal = parsedProp.pfValue;
14874 } // might not be a number if it allows enums
14875
14876
14877 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14878 var haveNormNum = normalizedNumberVal != null;
14879 var haveUnitedNum = numberVal != null;
14880 var haveNum = haveNormNum || haveUnitedNum;
14881 var units = parsedProp.units; // numbers are cheaper to hash than strings
14882 // 1 hash op vs n hash ops (for length n string)
14883
14884 if (type.number && haveNum && !type.multiple) {
14885 var v = haveNormNum ? normalizedNumberVal : numberVal;
14886 updateGrKey(cleanNum(v), _grKey);
14887
14888 if (!haveNormNum && units != null) {
14889 updateGrKeyWStr(units, _grKey);
14890 }
14891 } else {
14892 updateGrKeyWStr(parsedProp.strValue, _grKey);
14893 }
14894 } // overall style key
14895 //
14896
14897
14898 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14899
14900 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14901 var _grKey2 = propGrKeys[_i2];
14902 var grHash = _p.styleKeys[_grKey2];
14903 hash[0] = hashInt(grHash[0], hash[0]);
14904 hash[1] = hashIntAlt(grHash[1], hash[1]);
14905 }
14906
14907 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14908 //
14909
14910 var sk = _p.styleKeys;
14911 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14912 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14913 _p.labelKey = combineHashesArray(labelKeys);
14914 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14915
14916 if (!isNode) {
14917 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14918 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14919 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14920 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14921 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14922 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14923 } // node
14924 //
14925
14926
14927 if (isNode) {
14928 var _p$styleKeys = _p.styleKeys,
14929 nodeBody = _p$styleKeys.nodeBody,
14930 nodeBorder = _p$styleKeys.nodeBorder,
14931 backgroundImage = _p$styleKeys.backgroundImage,
14932 compound = _p$styleKeys.compound,
14933 pie = _p$styleKeys.pie;
14934 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14935 return k != null;
14936 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14937 _p.nodeKey = combineHashesArray(nodeKeys);
14938 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14939 }
14940
14941 return oldStyleKey !== _p.styleKey;
14942};
14943
14944styfn.clearStyleHints = function (ele) {
14945 var _p = ele._private;
14946 _p.styleCxtKey = '';
14947 _p.styleKeys = {};
14948 _p.styleKey = null;
14949 _p.labelKey = null;
14950 _p.labelStyleKey = null;
14951 _p.sourceLabelKey = null;
14952 _p.sourceLabelStyleKey = null;
14953 _p.targetLabelKey = null;
14954 _p.targetLabelStyleKey = null;
14955 _p.nodeKey = null;
14956 _p.hasPie = null;
14957}; // apply a property to the style (for internal use)
14958// returns whether application was successful
14959//
14960// now, this function flattens the property, and here's how:
14961//
14962// for parsedProp:{ bypass: true, deleteBypass: true }
14963// no property is generated, instead the bypass property in the
14964// element's style is replaced by what's pointed to by the `bypassed`
14965// field in the bypass property (i.e. restoring the property the
14966// bypass was overriding)
14967//
14968// for parsedProp:{ mapped: truthy }
14969// the generated flattenedProp:{ mapping: prop }
14970//
14971// for parsedProp:{ bypass: true }
14972// the generated flattenedProp:{ bypassed: parsedProp }
14973
14974
14975styfn.applyParsedProperty = function (ele, parsedProp) {
14976 var self = this;
14977 var prop = parsedProp;
14978 var style = ele._private.style;
14979 var flatProp;
14980 var types = self.types;
14981 var type = self.properties[prop.name].type;
14982 var propIsBypass = prop.bypass;
14983 var origProp = style[prop.name];
14984 var origPropIsBypass = origProp && origProp.bypass;
14985 var _p = ele._private;
14986 var flatPropMapping = 'mapping';
14987
14988 var getVal = function getVal(p) {
14989 if (p == null) {
14990 return null;
14991 } else if (p.pfValue != null) {
14992 return p.pfValue;
14993 } else {
14994 return p.value;
14995 }
14996 };
14997
14998 var checkTriggers = function checkTriggers() {
14999 var fromVal = getVal(origProp);
15000 var toVal = getVal(prop);
15001 self.checkTriggers(ele, prop.name, fromVal, toVal);
15002 };
15003
15004 if (prop && prop.name.substr(0, 3) === 'pie') {
15005 warn('The pie style properties are deprecated. Create charts using background images instead.');
15006 } // edge sanity checks to prevent the client from making serious mistakes
15007
15008
15009 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15010 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15011 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15012 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15013 }
15014
15015 if (prop["delete"]) {
15016 // delete the property and use the default value on falsey value
15017 style[prop.name] = undefined;
15018 checkTriggers();
15019 return true;
15020 }
15021
15022 if (prop.deleteBypassed) {
15023 // delete the property that the
15024 if (!origProp) {
15025 checkTriggers();
15026 return true; // can't delete if no prop
15027 } else if (origProp.bypass) {
15028 // delete bypassed
15029 origProp.bypassed = undefined;
15030 checkTriggers();
15031 return true;
15032 } else {
15033 return false; // we're unsuccessful deleting the bypassed
15034 }
15035 } // check if we need to delete the current bypass
15036
15037
15038 if (prop.deleteBypass) {
15039 // then this property is just here to indicate we need to delete
15040 if (!origProp) {
15041 checkTriggers();
15042 return true; // property is already not defined
15043 } else if (origProp.bypass) {
15044 // then replace the bypass property with the original
15045 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15046 style[prop.name] = origProp.bypassed;
15047 checkTriggers();
15048 return true;
15049 } else {
15050 return false; // we're unsuccessful deleting the bypass
15051 }
15052 }
15053
15054 var printMappingErr = function printMappingErr() {
15055 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');
15056 }; // put the property in the style objects
15057
15058
15059 switch (prop.mapped) {
15060 // flatten the property if mapped
15061 case types.mapData:
15062 {
15063 // flatten the field (e.g. data.foo.bar)
15064 var fields = prop.field.split('.');
15065 var fieldVal = _p.data;
15066
15067 for (var i = 0; i < fields.length && fieldVal; i++) {
15068 var field = fields[i];
15069 fieldVal = fieldVal[field];
15070 }
15071
15072 if (fieldVal == null) {
15073 printMappingErr();
15074 return false;
15075 }
15076
15077 var percent;
15078
15079 if (!number(fieldVal)) {
15080 // then don't apply and fall back on the existing style
15081 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15082 return false;
15083 } else {
15084 var fieldWidth = prop.fieldMax - prop.fieldMin;
15085
15086 if (fieldWidth === 0) {
15087 // safety check -- not strictly necessary as no props of zero range should be passed here
15088 percent = 0;
15089 } else {
15090 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15091 }
15092 } // make sure to bound percent value
15093
15094
15095 if (percent < 0) {
15096 percent = 0;
15097 } else if (percent > 1) {
15098 percent = 1;
15099 }
15100
15101 if (type.color) {
15102 var r1 = prop.valueMin[0];
15103 var r2 = prop.valueMax[0];
15104 var g1 = prop.valueMin[1];
15105 var g2 = prop.valueMax[1];
15106 var b1 = prop.valueMin[2];
15107 var b2 = prop.valueMax[2];
15108 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15109 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15110 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)];
15111 flatProp = {
15112 // colours are simple, so just create the flat property instead of expensive string parsing
15113 bypass: prop.bypass,
15114 // we're a bypass if the mapping property is a bypass
15115 name: prop.name,
15116 value: clr,
15117 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15118 };
15119 } else if (type.number) {
15120 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15121 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15122 } else {
15123 return false; // can only map to colours and numbers
15124 }
15125
15126 if (!flatProp) {
15127 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15128 printMappingErr();
15129 return false;
15130 }
15131
15132 flatProp.mapping = prop; // keep a reference to the mapping
15133
15134 prop = flatProp; // the flattened (mapped) property is the one we want
15135
15136 break;
15137 }
15138 // direct mapping
15139
15140 case types.data:
15141 {
15142 // flatten the field (e.g. data.foo.bar)
15143 var _fields = prop.field.split('.');
15144
15145 var _fieldVal = _p.data;
15146
15147 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15148 var _field = _fields[_i3];
15149 _fieldVal = _fieldVal[_field];
15150 }
15151
15152 if (_fieldVal != null) {
15153 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15154 }
15155
15156 if (!flatProp) {
15157 // if we can't flatten the property, then don't apply and fall back on the existing style
15158 printMappingErr();
15159 return false;
15160 }
15161
15162 flatProp.mapping = prop; // keep a reference to the mapping
15163
15164 prop = flatProp; // the flattened (mapped) property is the one we want
15165
15166 break;
15167 }
15168
15169 case types.fn:
15170 {
15171 var fn = prop.value;
15172 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15173
15174 prop.prevFnValue = fnRetVal;
15175
15176 if (fnRetVal == null) {
15177 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15178 return false;
15179 }
15180
15181 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15182
15183 if (!flatProp) {
15184 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15185 return false;
15186 }
15187
15188 flatProp.mapping = copy(prop); // keep a reference to the mapping
15189
15190 prop = flatProp; // the flattened (mapped) property is the one we want
15191
15192 break;
15193 }
15194
15195 case undefined:
15196 break;
15197 // just set the property
15198
15199 default:
15200 return false;
15201 // not a valid mapping
15202 } // if the property is a bypass property, then link the resultant property to the original one
15203
15204
15205 if (propIsBypass) {
15206 if (origPropIsBypass) {
15207 // then this bypass overrides the existing one
15208 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15209 } else {
15210 // then link the orig prop to the new bypass
15211 prop.bypassed = origProp;
15212 }
15213
15214 style[prop.name] = prop; // and set
15215 } else {
15216 // prop is not bypass
15217 if (origPropIsBypass) {
15218 // then keep the orig prop (since it's a bypass) and link to the new prop
15219 origProp.bypassed = prop;
15220 } else {
15221 // then just replace the old prop with the new one
15222 style[prop.name] = prop;
15223 }
15224 }
15225
15226 checkTriggers();
15227 return true;
15228};
15229
15230styfn.cleanElements = function (eles, keepBypasses) {
15231 for (var i = 0; i < eles.length; i++) {
15232 var ele = eles[i];
15233 this.clearStyleHints(ele);
15234 ele.dirtyCompoundBoundsCache();
15235 ele.dirtyBoundingBoxCache();
15236
15237 if (!keepBypasses) {
15238 ele._private.style = {};
15239 } else {
15240 var style = ele._private.style;
15241 var propNames = Object.keys(style);
15242
15243 for (var j = 0; j < propNames.length; j++) {
15244 var propName = propNames[j];
15245 var eleProp = style[propName];
15246
15247 if (eleProp != null) {
15248 if (eleProp.bypass) {
15249 eleProp.bypassed = null;
15250 } else {
15251 style[propName] = null;
15252 }
15253 }
15254 }
15255 }
15256 }
15257}; // updates the visual style for all elements (useful for manual style modification after init)
15258
15259
15260styfn.update = function () {
15261 var cy = this._private.cy;
15262 var eles = cy.mutableElements();
15263 eles.updateStyle();
15264}; // diffProps : { name => { prev, next } }
15265
15266
15267styfn.updateTransitions = function (ele, diffProps) {
15268 var self = this;
15269 var _p = ele._private;
15270 var props = ele.pstyle('transition-property').value;
15271 var duration = ele.pstyle('transition-duration').pfValue;
15272 var delay = ele.pstyle('transition-delay').pfValue;
15273
15274 if (props.length > 0 && duration > 0) {
15275 var style = {}; // build up the style to animate towards
15276
15277 var anyPrev = false;
15278
15279 for (var i = 0; i < props.length; i++) {
15280 var prop = props[i];
15281 var styProp = ele.pstyle(prop);
15282 var diffProp = diffProps[prop];
15283
15284 if (!diffProp) {
15285 continue;
15286 }
15287
15288 var prevProp = diffProp.prev;
15289 var fromProp = prevProp;
15290 var toProp = diffProp.next != null ? diffProp.next : styProp;
15291 var diff = false;
15292 var initVal = void 0;
15293 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15294
15295 if (!fromProp) {
15296 continue;
15297 } // consider px values
15298
15299
15300 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15301 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15302
15303 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15304 } else if (number(fromProp.value) && number(toProp.value)) {
15305 diff = toProp.value - fromProp.value; // nonzero is truthy
15306
15307 initVal = fromProp.value + initDt * diff; // consider colour values
15308 } else if (array(fromProp.value) && array(toProp.value)) {
15309 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15310 initVal = fromProp.strValue;
15311 } // the previous value is good for an animation only if it's different
15312
15313
15314 if (diff) {
15315 style[prop] = toProp.strValue; // to val
15316
15317 this.applyBypass(ele, prop, initVal); // from val
15318
15319 anyPrev = true;
15320 }
15321 } // end if props allow ani
15322 // can't transition if there's nothing previous to transition from
15323
15324
15325 if (!anyPrev) {
15326 return;
15327 }
15328
15329 _p.transitioning = true;
15330 new Promise$1(function (resolve) {
15331 if (delay > 0) {
15332 ele.delayAnimation(delay).play().promise().then(resolve);
15333 } else {
15334 resolve();
15335 }
15336 }).then(function () {
15337 return ele.animation({
15338 style: style,
15339 duration: duration,
15340 easing: ele.pstyle('transition-timing-function').value,
15341 queue: false
15342 }).play().promise();
15343 }).then(function () {
15344 // if( !isBypass ){
15345 self.removeBypasses(ele, props);
15346 ele.emitAndNotify('style'); // }
15347
15348 _p.transitioning = false;
15349 });
15350 } else if (_p.transitioning) {
15351 this.removeBypasses(ele, props);
15352 ele.emitAndNotify('style');
15353 _p.transitioning = false;
15354 }
15355};
15356
15357styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15358 var prop = this.properties[name];
15359 var triggerCheck = getTrigger(prop);
15360
15361 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15362 onTrigger(prop);
15363 }
15364};
15365
15366styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15367 var _this = this;
15368
15369 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15370 return prop.triggersZOrder;
15371 }, function () {
15372 _this._private.cy.notify('zorder', ele);
15373 });
15374};
15375
15376styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15377 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15378 return prop.triggersBounds;
15379 }, function (prop) {
15380 ele.dirtyCompoundBoundsCache();
15381 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15382 // then dirty the pll edge bb cache as well
15383
15384 if ( // only for beziers -- so performance of other edges isn't affected
15385 prop.triggersBoundsOfParallelBeziers && (name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') || name === 'display' && (fromValue === 'none' || toValue === 'none'))) {
15386 ele.parallelEdges().forEach(function (pllEdge) {
15387 if (pllEdge.isBundledBezier()) {
15388 pllEdge.dirtyBoundingBoxCache();
15389 }
15390 });
15391 }
15392 });
15393};
15394
15395styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15396 ele.dirtyStyleCache();
15397 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15398 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15399};
15400
15401var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15402// returns true iff application was successful for at least 1 specified property
15403
15404styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15405 var self = this;
15406 var props = [];
15407 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15408
15409 if (name === '*' || name === '**') {
15410 // apply to all property names
15411 if (value !== undefined) {
15412 for (var i = 0; i < self.properties.length; i++) {
15413 var prop = self.properties[i];
15414 var _name = prop.name;
15415 var parsedProp = this.parse(_name, value, true);
15416
15417 if (parsedProp) {
15418 props.push(parsedProp);
15419 }
15420 }
15421 }
15422 } else if (string(name)) {
15423 // then parse the single property
15424 var _parsedProp = this.parse(name, value, true);
15425
15426 if (_parsedProp) {
15427 props.push(_parsedProp);
15428 }
15429 } else if (plainObject(name)) {
15430 // then parse each property
15431 var specifiedProps = name;
15432 updateTransitions = value;
15433 var names = Object.keys(specifiedProps);
15434
15435 for (var _i = 0; _i < names.length; _i++) {
15436 var _name2 = names[_i];
15437 var _value = specifiedProps[_name2];
15438
15439 if (_value === undefined) {
15440 // try camel case name too
15441 _value = specifiedProps[dash2camel(_name2)];
15442 }
15443
15444 if (_value !== undefined) {
15445 var _parsedProp2 = this.parse(_name2, _value, true);
15446
15447 if (_parsedProp2) {
15448 props.push(_parsedProp2);
15449 }
15450 }
15451 }
15452 } else {
15453 // can't do anything without well defined properties
15454 return false;
15455 } // we've failed if there are no valid properties
15456
15457
15458 if (props.length === 0) {
15459 return false;
15460 } // now, apply the bypass properties on the elements
15461
15462
15463 var ret = false; // return true if at least one succesful bypass applied
15464
15465 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15466 // for each ele
15467 var ele = eles[_i2];
15468 var diffProps = {};
15469 var diffProp = void 0;
15470
15471 for (var j = 0; j < props.length; j++) {
15472 // for each prop
15473 var _prop = props[j];
15474
15475 if (updateTransitions) {
15476 var prevProp = ele.pstyle(_prop.name);
15477 diffProp = diffProps[_prop.name] = {
15478 prev: prevProp
15479 };
15480 }
15481
15482 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15483
15484 if (updateTransitions) {
15485 diffProp.next = ele.pstyle(_prop.name);
15486 }
15487 } // for props
15488
15489
15490 if (ret) {
15491 this.updateStyleHints(ele);
15492 }
15493
15494 if (updateTransitions) {
15495 this.updateTransitions(ele, diffProps, isBypass);
15496 }
15497 } // for eles
15498
15499
15500 return ret;
15501}; // only useful in specific cases like animation
15502
15503
15504styfn$1.overrideBypass = function (eles, name, value) {
15505 name = camel2dash(name);
15506
15507 for (var i = 0; i < eles.length; i++) {
15508 var ele = eles[i];
15509 var prop = ele._private.style[name];
15510 var type = this.properties[name].type;
15511 var isColor = type.color;
15512 var isMulti = type.mutiple;
15513 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15514
15515 if (!prop || !prop.bypass) {
15516 // need a bypass if one doesn't exist
15517 this.applyBypass(ele, name, value);
15518 } else {
15519 prop.value = value;
15520
15521 if (prop.pfValue != null) {
15522 prop.pfValue = value;
15523 }
15524
15525 if (isColor) {
15526 prop.strValue = 'rgb(' + value.join(',') + ')';
15527 } else if (isMulti) {
15528 prop.strValue = value.join(' ');
15529 } else {
15530 prop.strValue = '' + value;
15531 }
15532
15533 this.updateStyleHints(ele);
15534 }
15535
15536 this.checkTriggers(ele, name, oldValue, value);
15537 }
15538};
15539
15540styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15541 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15542};
15543
15544styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15545 var isBypass = true;
15546
15547 for (var j = 0; j < eles.length; j++) {
15548 var ele = eles[j];
15549 var diffProps = {};
15550
15551 for (var i = 0; i < props.length; i++) {
15552 var name = props[i];
15553 var prop = this.properties[name];
15554 var prevProp = ele.pstyle(prop.name);
15555
15556 if (!prevProp || !prevProp.bypass) {
15557 // if a bypass doesn't exist for the prop, nothing needs to be removed
15558 continue;
15559 }
15560
15561 var value = ''; // empty => remove bypass
15562
15563 var parsedProp = this.parse(name, value, true);
15564 var diffProp = diffProps[prop.name] = {
15565 prev: prevProp
15566 };
15567 this.applyParsedProperty(ele, parsedProp);
15568 diffProp.next = ele.pstyle(prop.name);
15569 } // for props
15570
15571
15572 this.updateStyleHints(ele);
15573
15574 if (updateTransitions) {
15575 this.updateTransitions(ele, diffProps, isBypass);
15576 }
15577 } // for eles
15578
15579};
15580
15581var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15582
15583styfn$2.getEmSizeInPixels = function () {
15584 var px = this.containerCss('font-size');
15585
15586 if (px != null) {
15587 return parseFloat(px);
15588 } else {
15589 return 1; // for headless
15590 }
15591}; // gets css property from the core container
15592
15593
15594styfn$2.containerCss = function (propName) {
15595 var cy = this._private.cy;
15596 var domElement = cy.container();
15597
15598 if (window$1 && domElement && window$1.getComputedStyle) {
15599 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15600 }
15601};
15602
15603var styfn$3 = {}; // gets the rendered style for an element
15604
15605styfn$3.getRenderedStyle = function (ele, prop) {
15606 if (prop) {
15607 return this.getStylePropertyValue(ele, prop, true);
15608 } else {
15609 return this.getRawStyle(ele, true);
15610 }
15611}; // gets the raw style for an element
15612
15613
15614styfn$3.getRawStyle = function (ele, isRenderedVal) {
15615 var self = this;
15616 ele = ele[0]; // insure it's an element
15617
15618 if (ele) {
15619 var rstyle = {};
15620
15621 for (var i = 0; i < self.properties.length; i++) {
15622 var prop = self.properties[i];
15623 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15624
15625 if (val != null) {
15626 rstyle[prop.name] = val;
15627 rstyle[dash2camel(prop.name)] = val;
15628 }
15629 }
15630
15631 return rstyle;
15632 }
15633};
15634
15635styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15636 var pstyle = ele.pstyle(property)[subproperty][index];
15637 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15638};
15639
15640styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15641 var self = this;
15642 ele = ele[0]; // insure it's an element
15643
15644 if (ele) {
15645 var prop = self.properties[propName];
15646
15647 if (prop.alias) {
15648 prop = prop.pointsTo;
15649 }
15650
15651 var type = prop.type;
15652 var styleProp = ele.pstyle(prop.name);
15653
15654 if (styleProp) {
15655 var value = styleProp.value,
15656 units = styleProp.units,
15657 strValue = styleProp.strValue;
15658
15659 if (isRenderedVal && type.number && value != null && number(value)) {
15660 var zoom = ele.cy().zoom();
15661
15662 var getRenderedValue = function getRenderedValue(val) {
15663 return val * zoom;
15664 };
15665
15666 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15667 return getRenderedValue(val) + units;
15668 };
15669
15670 var isArrayValue = array(value);
15671 var haveUnits = isArrayValue ? units.every(function (u) {
15672 return u != null;
15673 }) : units != null;
15674
15675 if (haveUnits) {
15676 if (isArrayValue) {
15677 return value.map(function (v, i) {
15678 return getValueStringWithUnits(v, units[i]);
15679 }).join(' ');
15680 } else {
15681 return getValueStringWithUnits(value, units);
15682 }
15683 } else {
15684 if (isArrayValue) {
15685 return value.map(function (v) {
15686 return string(v) ? v : '' + getRenderedValue(v);
15687 }).join(' ');
15688 } else {
15689 return '' + getRenderedValue(value);
15690 }
15691 }
15692 } else if (strValue != null) {
15693 return strValue;
15694 }
15695 }
15696
15697 return null;
15698 }
15699};
15700
15701styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15702 var rstyle = {};
15703
15704 for (var i = 0; i < aniProps.length; i++) {
15705 var aniProp = aniProps[i];
15706 var name = aniProp.name;
15707 var styleProp = ele.pstyle(name);
15708
15709 if (styleProp !== undefined) {
15710 // then make a prop of it
15711 if (plainObject(styleProp)) {
15712 styleProp = this.parse(name, styleProp.strValue);
15713 } else {
15714 styleProp = this.parse(name, styleProp);
15715 }
15716 }
15717
15718 if (styleProp) {
15719 rstyle[name] = styleProp;
15720 }
15721 }
15722
15723 return rstyle;
15724};
15725
15726styfn$3.getPropsList = function (propsObj) {
15727 var self = this;
15728 var rstyle = [];
15729 var style = propsObj;
15730 var props = self.properties;
15731
15732 if (style) {
15733 var names = Object.keys(style);
15734
15735 for (var i = 0; i < names.length; i++) {
15736 var name = names[i];
15737 var val = style[name];
15738 var prop = props[name] || props[camel2dash(name)];
15739 var styleProp = this.parse(prop.name, val);
15740
15741 if (styleProp) {
15742 rstyle.push(styleProp);
15743 }
15744 }
15745 }
15746
15747 return rstyle;
15748};
15749
15750styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15751 var hash = seed.slice();
15752 var name, val, strVal, chVal;
15753 var i, j;
15754
15755 for (i = 0; i < propNames.length; i++) {
15756 name = propNames[i];
15757 val = ele.pstyle(name, false);
15758
15759 if (val == null) {
15760 continue;
15761 } else if (val.pfValue != null) {
15762 hash[0] = hashInt(chVal, hash[0]);
15763 hash[1] = hashIntAlt(chVal, hash[1]);
15764 } else {
15765 strVal = val.strValue;
15766
15767 for (j = 0; j < strVal.length; j++) {
15768 chVal = strVal.charCodeAt(j);
15769 hash[0] = hashInt(chVal, hash[0]);
15770 hash[1] = hashIntAlt(chVal, hash[1]);
15771 }
15772 }
15773 }
15774
15775 return hash;
15776};
15777
15778styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15779
15780var styfn$4 = {};
15781
15782styfn$4.appendFromJson = function (json) {
15783 var style = this;
15784
15785 for (var i = 0; i < json.length; i++) {
15786 var context = json[i];
15787 var selector = context.selector;
15788 var props = context.style || context.css;
15789 var names = Object.keys(props);
15790 style.selector(selector); // apply selector
15791
15792 for (var j = 0; j < names.length; j++) {
15793 var name = names[j];
15794 var value = props[name];
15795 style.css(name, value); // apply property
15796 }
15797 }
15798
15799 return style;
15800}; // accessible cy.style() function
15801
15802
15803styfn$4.fromJson = function (json) {
15804 var style = this;
15805 style.resetToDefault();
15806 style.appendFromJson(json);
15807 return style;
15808}; // get json from cy.style() api
15809
15810
15811styfn$4.json = function () {
15812 var json = [];
15813
15814 for (var i = this.defaultLength; i < this.length; i++) {
15815 var cxt = this[i];
15816 var selector = cxt.selector;
15817 var props = cxt.properties;
15818 var css = {};
15819
15820 for (var j = 0; j < props.length; j++) {
15821 var prop = props[j];
15822 css[prop.name] = prop.strValue;
15823 }
15824
15825 json.push({
15826 selector: !selector ? 'core' : selector.toString(),
15827 style: css
15828 });
15829 }
15830
15831 return json;
15832};
15833
15834var styfn$5 = {};
15835
15836styfn$5.appendFromString = function (string) {
15837 var self = this;
15838 var style = this;
15839 var remaining = '' + string;
15840 var selAndBlockStr;
15841 var blockRem;
15842 var propAndValStr; // remove comments from the style string
15843
15844 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15845
15846 function removeSelAndBlockFromRemaining() {
15847 // remove the parsed selector and block from the remaining text to parse
15848 if (remaining.length > selAndBlockStr.length) {
15849 remaining = remaining.substr(selAndBlockStr.length);
15850 } else {
15851 remaining = '';
15852 }
15853 }
15854
15855 function removePropAndValFromRem() {
15856 // remove the parsed property and value from the remaining block text to parse
15857 if (blockRem.length > propAndValStr.length) {
15858 blockRem = blockRem.substr(propAndValStr.length);
15859 } else {
15860 blockRem = '';
15861 }
15862 }
15863
15864 for (;;) {
15865 var nothingLeftToParse = remaining.match(/^\s*$/);
15866
15867 if (nothingLeftToParse) {
15868 break;
15869 }
15870
15871 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15872
15873 if (!selAndBlock) {
15874 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15875 break;
15876 }
15877
15878 selAndBlockStr = selAndBlock[0]; // parse the selector
15879
15880 var selectorStr = selAndBlock[1];
15881
15882 if (selectorStr !== 'core') {
15883 var selector = new Selector(selectorStr);
15884
15885 if (selector.invalid) {
15886 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15887
15888 removeSelAndBlockFromRemaining();
15889 continue;
15890 }
15891 } // parse the block of properties and values
15892
15893
15894 var blockStr = selAndBlock[2];
15895 var invalidBlock = false;
15896 blockRem = blockStr;
15897 var props = [];
15898
15899 for (;;) {
15900 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15901
15902 if (_nothingLeftToParse) {
15903 break;
15904 }
15905
15906 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15907
15908 if (!propAndVal) {
15909 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15910 invalidBlock = true;
15911 break;
15912 }
15913
15914 propAndValStr = propAndVal[0];
15915 var propStr = propAndVal[1];
15916 var valStr = propAndVal[2];
15917 var prop = self.properties[propStr];
15918
15919 if (!prop) {
15920 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15921
15922 removePropAndValFromRem();
15923 continue;
15924 }
15925
15926 var parsedProp = style.parse(propStr, valStr);
15927
15928 if (!parsedProp) {
15929 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15930
15931 removePropAndValFromRem();
15932 continue;
15933 }
15934
15935 props.push({
15936 name: propStr,
15937 val: valStr
15938 });
15939 removePropAndValFromRem();
15940 }
15941
15942 if (invalidBlock) {
15943 removeSelAndBlockFromRemaining();
15944 break;
15945 } // put the parsed block in the style
15946
15947
15948 style.selector(selectorStr);
15949
15950 for (var i = 0; i < props.length; i++) {
15951 var _prop = props[i];
15952 style.css(_prop.name, _prop.val);
15953 }
15954
15955 removeSelAndBlockFromRemaining();
15956 }
15957
15958 return style;
15959};
15960
15961styfn$5.fromString = function (string) {
15962 var style = this;
15963 style.resetToDefault();
15964 style.appendFromString(string);
15965 return style;
15966};
15967
15968var styfn$6 = {};
15969
15970(function () {
15971 var number = number$1;
15972 var rgba = rgbaNoBackRefs;
15973 var hsla = hslaNoBackRefs;
15974 var hex3$1 = hex3;
15975 var hex6$1 = hex6;
15976
15977 var data = function data(prefix) {
15978 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15979 };
15980
15981 var mapData = function mapData(prefix) {
15982 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15983 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15984 };
15985
15986 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15987
15988 styfn$6.types = {
15989 time: {
15990 number: true,
15991 min: 0,
15992 units: 's|ms',
15993 implicitUnits: 'ms'
15994 },
15995 percent: {
15996 number: true,
15997 min: 0,
15998 max: 100,
15999 units: '%',
16000 implicitUnits: '%'
16001 },
16002 percentages: {
16003 number: true,
16004 min: 0,
16005 max: 100,
16006 units: '%',
16007 implicitUnits: '%',
16008 multiple: true
16009 },
16010 zeroOneNumber: {
16011 number: true,
16012 min: 0,
16013 max: 1,
16014 unitless: true
16015 },
16016 zeroOneNumbers: {
16017 number: true,
16018 min: 0,
16019 max: 1,
16020 unitless: true,
16021 multiple: true
16022 },
16023 nOneOneNumber: {
16024 number: true,
16025 min: -1,
16026 max: 1,
16027 unitless: true
16028 },
16029 nonNegativeInt: {
16030 number: true,
16031 min: 0,
16032 integer: true,
16033 unitless: true
16034 },
16035 position: {
16036 enums: ['parent', 'origin']
16037 },
16038 nodeSize: {
16039 number: true,
16040 min: 0,
16041 enums: ['label']
16042 },
16043 number: {
16044 number: true,
16045 unitless: true
16046 },
16047 numbers: {
16048 number: true,
16049 unitless: true,
16050 multiple: true
16051 },
16052 positiveNumber: {
16053 number: true,
16054 unitless: true,
16055 min: 0,
16056 strictMin: true
16057 },
16058 size: {
16059 number: true,
16060 min: 0
16061 },
16062 bidirectionalSize: {
16063 number: true
16064 },
16065 // allows negative
16066 bidirectionalSizeMaybePercent: {
16067 number: true,
16068 allowPercent: true
16069 },
16070 // allows negative
16071 bidirectionalSizes: {
16072 number: true,
16073 multiple: true
16074 },
16075 // allows negative
16076 sizeMaybePercent: {
16077 number: true,
16078 min: 0,
16079 allowPercent: true
16080 },
16081 axisDirection: {
16082 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16083 },
16084 paddingRelativeTo: {
16085 enums: ['width', 'height', 'average', 'min', 'max']
16086 },
16087 bgWH: {
16088 number: true,
16089 min: 0,
16090 allowPercent: true,
16091 enums: ['auto'],
16092 multiple: true
16093 },
16094 bgPos: {
16095 number: true,
16096 allowPercent: true,
16097 multiple: true
16098 },
16099 bgRelativeTo: {
16100 enums: ['inner', 'include-padding'],
16101 multiple: true
16102 },
16103 bgRepeat: {
16104 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16105 multiple: true
16106 },
16107 bgFit: {
16108 enums: ['none', 'contain', 'cover'],
16109 multiple: true
16110 },
16111 bgCrossOrigin: {
16112 enums: ['anonymous', 'use-credentials'],
16113 multiple: true
16114 },
16115 bgClip: {
16116 enums: ['none', 'node'],
16117 multiple: true
16118 },
16119 bgContainment: {
16120 enums: ['inside', 'over'],
16121 multiple: true
16122 },
16123 color: {
16124 color: true
16125 },
16126 colors: {
16127 color: true,
16128 multiple: true
16129 },
16130 fill: {
16131 enums: ['solid', 'linear-gradient', 'radial-gradient']
16132 },
16133 bool: {
16134 enums: ['yes', 'no']
16135 },
16136 bools: {
16137 enums: ['yes', 'no'],
16138 multiple: true
16139 },
16140 lineStyle: {
16141 enums: ['solid', 'dotted', 'dashed']
16142 },
16143 lineCap: {
16144 enums: ['butt', 'round', 'square']
16145 },
16146 borderStyle: {
16147 enums: ['solid', 'dotted', 'dashed', 'double']
16148 },
16149 curveStyle: {
16150 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi']
16151 },
16152 fontFamily: {
16153 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16154 },
16155 fontStyle: {
16156 enums: ['italic', 'normal', 'oblique']
16157 },
16158 fontWeight: {
16159 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16160 },
16161 textDecoration: {
16162 enums: ['none', 'underline', 'overline', 'line-through']
16163 },
16164 textTransform: {
16165 enums: ['none', 'uppercase', 'lowercase']
16166 },
16167 textWrap: {
16168 enums: ['none', 'wrap', 'ellipsis']
16169 },
16170 textOverflowWrap: {
16171 enums: ['whitespace', 'anywhere']
16172 },
16173 textBackgroundShape: {
16174 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16175 },
16176 nodeShape: {
16177 enums: ['rectangle', 'roundrectangle', 'round-rectangle', 'cutrectangle', 'cut-rectangle', 'bottomroundrectangle', 'bottom-round-rectangle', 'barrel', 'ellipse', 'triangle', 'round-triangle', 'square', 'pentagon', 'round-pentagon', 'hexagon', 'round-hexagon', 'concavehexagon', 'concave-hexagon', 'heptagon', 'round-heptagon', 'octagon', 'round-octagon', 'tag', 'round-tag', 'star', 'diamond', 'round-diamond', 'vee', 'rhomboid', 'polygon']
16178 },
16179 overlayShape: {
16180 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16181 },
16182 compoundIncludeLabels: {
16183 enums: ['include', 'exclude']
16184 },
16185 arrowShape: {
16186 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16187 },
16188 arrowFill: {
16189 enums: ['filled', 'hollow']
16190 },
16191 display: {
16192 enums: ['element', 'none']
16193 },
16194 visibility: {
16195 enums: ['hidden', 'visible']
16196 },
16197 zCompoundDepth: {
16198 enums: ['bottom', 'orphan', 'auto', 'top']
16199 },
16200 zIndexCompare: {
16201 enums: ['auto', 'manual']
16202 },
16203 valign: {
16204 enums: ['top', 'center', 'bottom']
16205 },
16206 halign: {
16207 enums: ['left', 'center', 'right']
16208 },
16209 justification: {
16210 enums: ['left', 'center', 'right', 'auto']
16211 },
16212 text: {
16213 string: true
16214 },
16215 data: {
16216 mapping: true,
16217 regex: data('data')
16218 },
16219 layoutData: {
16220 mapping: true,
16221 regex: data('layoutData')
16222 },
16223 scratch: {
16224 mapping: true,
16225 regex: data('scratch')
16226 },
16227 mapData: {
16228 mapping: true,
16229 regex: mapData('mapData')
16230 },
16231 mapLayoutData: {
16232 mapping: true,
16233 regex: mapData('mapLayoutData')
16234 },
16235 mapScratch: {
16236 mapping: true,
16237 regex: mapData('mapScratch')
16238 },
16239 fn: {
16240 mapping: true,
16241 fn: true
16242 },
16243 url: {
16244 regexes: urlRegexes,
16245 singleRegexMatchValue: true
16246 },
16247 urls: {
16248 regexes: urlRegexes,
16249 singleRegexMatchValue: true,
16250 multiple: true
16251 },
16252 propList: {
16253 propList: true
16254 },
16255 angle: {
16256 number: true,
16257 units: 'deg|rad',
16258 implicitUnits: 'rad'
16259 },
16260 textRotation: {
16261 number: true,
16262 units: 'deg|rad',
16263 implicitUnits: 'rad',
16264 enums: ['none', 'autorotate']
16265 },
16266 polygonPointList: {
16267 number: true,
16268 multiple: true,
16269 evenMultiple: true,
16270 min: -1,
16271 max: 1,
16272 unitless: true
16273 },
16274 edgeDistances: {
16275 enums: ['intersection', 'node-position']
16276 },
16277 edgeEndpoint: {
16278 number: true,
16279 multiple: true,
16280 units: '%|px|em|deg|rad',
16281 implicitUnits: 'px',
16282 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16283 singleEnum: true,
16284 validate: function validate(valArr, unitsArr) {
16285 switch (valArr.length) {
16286 case 2:
16287 // can be % or px only
16288 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16289
16290 case 1:
16291 // can be enum, deg, or rad only
16292 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16293
16294 default:
16295 return false;
16296 }
16297 }
16298 },
16299 easing: {
16300 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16301 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']
16302 },
16303 gradientDirection: {
16304 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']
16305 },
16306 boundsExpansion: {
16307 number: true,
16308 multiple: true,
16309 min: 0,
16310 validate: function validate(valArr) {
16311 var length = valArr.length;
16312 return length === 1 || length === 2 || length === 4;
16313 }
16314 }
16315 };
16316 var diff = {
16317 zeroNonZero: function zeroNonZero(val1, val2) {
16318 if ((val1 == null || val2 == null) && val1 !== val2) {
16319 return true; // null cases could represent any value
16320 }
16321
16322 if (val1 == 0 && val2 != 0) {
16323 return true;
16324 } else if (val1 != 0 && val2 == 0) {
16325 return true;
16326 } else {
16327 return false;
16328 }
16329 },
16330 any: function any(val1, val2) {
16331 return val1 != val2;
16332 },
16333 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16334 var empty1 = emptyString(str1);
16335 var empty2 = emptyString(str2);
16336 return empty1 && !empty2 || !empty1 && empty2;
16337 }
16338 }; // define visual style properties
16339 //
16340 // - n.b. adding a new group of props may require updates to updateStyleHints()
16341 // - adding new props to an existing group gets handled automatically
16342
16343 var t = styfn$6.types;
16344 var mainLabel = [{
16345 name: 'label',
16346 type: t.text,
16347 triggersBounds: diff.any,
16348 triggersZOrder: diff.emptyNonEmpty
16349 }, {
16350 name: 'text-rotation',
16351 type: t.textRotation,
16352 triggersBounds: diff.any
16353 }, {
16354 name: 'text-margin-x',
16355 type: t.bidirectionalSize,
16356 triggersBounds: diff.any
16357 }, {
16358 name: 'text-margin-y',
16359 type: t.bidirectionalSize,
16360 triggersBounds: diff.any
16361 }];
16362 var sourceLabel = [{
16363 name: 'source-label',
16364 type: t.text,
16365 triggersBounds: diff.any
16366 }, {
16367 name: 'source-text-rotation',
16368 type: t.textRotation,
16369 triggersBounds: diff.any
16370 }, {
16371 name: 'source-text-margin-x',
16372 type: t.bidirectionalSize,
16373 triggersBounds: diff.any
16374 }, {
16375 name: 'source-text-margin-y',
16376 type: t.bidirectionalSize,
16377 triggersBounds: diff.any
16378 }, {
16379 name: 'source-text-offset',
16380 type: t.size,
16381 triggersBounds: diff.any
16382 }];
16383 var targetLabel = [{
16384 name: 'target-label',
16385 type: t.text,
16386 triggersBounds: diff.any
16387 }, {
16388 name: 'target-text-rotation',
16389 type: t.textRotation,
16390 triggersBounds: diff.any
16391 }, {
16392 name: 'target-text-margin-x',
16393 type: t.bidirectionalSize,
16394 triggersBounds: diff.any
16395 }, {
16396 name: 'target-text-margin-y',
16397 type: t.bidirectionalSize,
16398 triggersBounds: diff.any
16399 }, {
16400 name: 'target-text-offset',
16401 type: t.size,
16402 triggersBounds: diff.any
16403 }];
16404 var labelDimensions = [{
16405 name: 'font-family',
16406 type: t.fontFamily,
16407 triggersBounds: diff.any
16408 }, {
16409 name: 'font-style',
16410 type: t.fontStyle,
16411 triggersBounds: diff.any
16412 }, {
16413 name: 'font-weight',
16414 type: t.fontWeight,
16415 triggersBounds: diff.any
16416 }, {
16417 name: 'font-size',
16418 type: t.size,
16419 triggersBounds: diff.any
16420 }, {
16421 name: 'text-transform',
16422 type: t.textTransform,
16423 triggersBounds: diff.any
16424 }, {
16425 name: 'text-wrap',
16426 type: t.textWrap,
16427 triggersBounds: diff.any
16428 }, {
16429 name: 'text-overflow-wrap',
16430 type: t.textOverflowWrap,
16431 triggersBounds: diff.any
16432 }, {
16433 name: 'text-max-width',
16434 type: t.size,
16435 triggersBounds: diff.any
16436 }, {
16437 name: 'text-outline-width',
16438 type: t.size,
16439 triggersBounds: diff.any
16440 }, {
16441 name: 'line-height',
16442 type: t.positiveNumber,
16443 triggersBounds: diff.any
16444 }];
16445 var commonLabel = [{
16446 name: 'text-valign',
16447 type: t.valign,
16448 triggersBounds: diff.any
16449 }, {
16450 name: 'text-halign',
16451 type: t.halign,
16452 triggersBounds: diff.any
16453 }, {
16454 name: 'color',
16455 type: t.color
16456 }, {
16457 name: 'text-outline-color',
16458 type: t.color
16459 }, {
16460 name: 'text-outline-opacity',
16461 type: t.zeroOneNumber
16462 }, {
16463 name: 'text-background-color',
16464 type: t.color
16465 }, {
16466 name: 'text-background-opacity',
16467 type: t.zeroOneNumber
16468 }, {
16469 name: 'text-background-padding',
16470 type: t.size,
16471 triggersBounds: diff.any
16472 }, {
16473 name: 'text-border-opacity',
16474 type: t.zeroOneNumber
16475 }, {
16476 name: 'text-border-color',
16477 type: t.color
16478 }, {
16479 name: 'text-border-width',
16480 type: t.size,
16481 triggersBounds: diff.any
16482 }, {
16483 name: 'text-border-style',
16484 type: t.borderStyle,
16485 triggersBounds: diff.any
16486 }, {
16487 name: 'text-background-shape',
16488 type: t.textBackgroundShape,
16489 triggersBounds: diff.any
16490 }, {
16491 name: 'text-justification',
16492 type: t.justification
16493 }];
16494 var behavior = [{
16495 name: 'events',
16496 type: t.bool
16497 }, {
16498 name: 'text-events',
16499 type: t.bool
16500 }];
16501 var visibility = [{
16502 name: 'display',
16503 type: t.display,
16504 triggersZOrder: diff.any,
16505 triggersBounds: diff.any,
16506 triggersBoundsOfParallelBeziers: true
16507 }, {
16508 name: 'visibility',
16509 type: t.visibility,
16510 triggersZOrder: diff.any
16511 }, {
16512 name: 'opacity',
16513 type: t.zeroOneNumber,
16514 triggersZOrder: diff.zeroNonZero
16515 }, {
16516 name: 'text-opacity',
16517 type: t.zeroOneNumber
16518 }, {
16519 name: 'min-zoomed-font-size',
16520 type: t.size
16521 }, {
16522 name: 'z-compound-depth',
16523 type: t.zCompoundDepth,
16524 triggersZOrder: diff.any
16525 }, {
16526 name: 'z-index-compare',
16527 type: t.zIndexCompare,
16528 triggersZOrder: diff.any
16529 }, {
16530 name: 'z-index',
16531 type: t.nonNegativeInt,
16532 triggersZOrder: diff.any
16533 }];
16534 var overlay = [{
16535 name: 'overlay-padding',
16536 type: t.size,
16537 triggersBounds: diff.any
16538 }, {
16539 name: 'overlay-color',
16540 type: t.color
16541 }, {
16542 name: 'overlay-opacity',
16543 type: t.zeroOneNumber,
16544 triggersBounds: diff.zeroNonZero
16545 }, {
16546 name: 'overlay-shape',
16547 type: t.overlayShape,
16548 triggersBounds: diff.any
16549 }];
16550 var underlay = [{
16551 name: 'underlay-padding',
16552 type: t.size,
16553 triggersBounds: diff.any
16554 }, {
16555 name: 'underlay-color',
16556 type: t.color
16557 }, {
16558 name: 'underlay-opacity',
16559 type: t.zeroOneNumber,
16560 triggersBounds: diff.zeroNonZero
16561 }, {
16562 name: 'underlay-shape',
16563 type: t.overlayShape,
16564 triggersBounds: diff.any
16565 }];
16566 var transition = [{
16567 name: 'transition-property',
16568 type: t.propList
16569 }, {
16570 name: 'transition-duration',
16571 type: t.time
16572 }, {
16573 name: 'transition-delay',
16574 type: t.time
16575 }, {
16576 name: 'transition-timing-function',
16577 type: t.easing
16578 }];
16579
16580 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16581 if (parsedProp.value === 'label') {
16582 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16583 } else {
16584 return parsedProp.pfValue;
16585 }
16586 };
16587
16588 var nodeBody = [{
16589 name: 'height',
16590 type: t.nodeSize,
16591 triggersBounds: diff.any,
16592 hashOverride: nodeSizeHashOverride
16593 }, {
16594 name: 'width',
16595 type: t.nodeSize,
16596 triggersBounds: diff.any,
16597 hashOverride: nodeSizeHashOverride
16598 }, {
16599 name: 'shape',
16600 type: t.nodeShape,
16601 triggersBounds: diff.any
16602 }, {
16603 name: 'shape-polygon-points',
16604 type: t.polygonPointList,
16605 triggersBounds: diff.any
16606 }, {
16607 name: 'background-color',
16608 type: t.color
16609 }, {
16610 name: 'background-fill',
16611 type: t.fill
16612 }, {
16613 name: 'background-opacity',
16614 type: t.zeroOneNumber
16615 }, {
16616 name: 'background-blacken',
16617 type: t.nOneOneNumber
16618 }, {
16619 name: 'background-gradient-stop-colors',
16620 type: t.colors
16621 }, {
16622 name: 'background-gradient-stop-positions',
16623 type: t.percentages
16624 }, {
16625 name: 'background-gradient-direction',
16626 type: t.gradientDirection
16627 }, {
16628 name: 'padding',
16629 type: t.sizeMaybePercent,
16630 triggersBounds: diff.any
16631 }, {
16632 name: 'padding-relative-to',
16633 type: t.paddingRelativeTo,
16634 triggersBounds: diff.any
16635 }, {
16636 name: 'bounds-expansion',
16637 type: t.boundsExpansion,
16638 triggersBounds: diff.any
16639 }];
16640 var nodeBorder = [{
16641 name: 'border-color',
16642 type: t.color
16643 }, {
16644 name: 'border-opacity',
16645 type: t.zeroOneNumber
16646 }, {
16647 name: 'border-width',
16648 type: t.size,
16649 triggersBounds: diff.any
16650 }, {
16651 name: 'border-style',
16652 type: t.borderStyle
16653 }];
16654 var backgroundImage = [{
16655 name: 'background-image',
16656 type: t.urls
16657 }, {
16658 name: 'background-image-crossorigin',
16659 type: t.bgCrossOrigin
16660 }, {
16661 name: 'background-image-opacity',
16662 type: t.zeroOneNumbers
16663 }, {
16664 name: 'background-image-containment',
16665 type: t.bgContainment
16666 }, {
16667 name: 'background-image-smoothing',
16668 type: t.bools
16669 }, {
16670 name: 'background-position-x',
16671 type: t.bgPos
16672 }, {
16673 name: 'background-position-y',
16674 type: t.bgPos
16675 }, {
16676 name: 'background-width-relative-to',
16677 type: t.bgRelativeTo
16678 }, {
16679 name: 'background-height-relative-to',
16680 type: t.bgRelativeTo
16681 }, {
16682 name: 'background-repeat',
16683 type: t.bgRepeat
16684 }, {
16685 name: 'background-fit',
16686 type: t.bgFit
16687 }, {
16688 name: 'background-clip',
16689 type: t.bgClip
16690 }, {
16691 name: 'background-width',
16692 type: t.bgWH
16693 }, {
16694 name: 'background-height',
16695 type: t.bgWH
16696 }, {
16697 name: 'background-offset-x',
16698 type: t.bgPos
16699 }, {
16700 name: 'background-offset-y',
16701 type: t.bgPos
16702 }];
16703 var compound = [{
16704 name: 'position',
16705 type: t.position,
16706 triggersBounds: diff.any
16707 }, {
16708 name: 'compound-sizing-wrt-labels',
16709 type: t.compoundIncludeLabels,
16710 triggersBounds: diff.any
16711 }, {
16712 name: 'min-width',
16713 type: t.size,
16714 triggersBounds: diff.any
16715 }, {
16716 name: 'min-width-bias-left',
16717 type: t.sizeMaybePercent,
16718 triggersBounds: diff.any
16719 }, {
16720 name: 'min-width-bias-right',
16721 type: t.sizeMaybePercent,
16722 triggersBounds: diff.any
16723 }, {
16724 name: 'min-height',
16725 type: t.size,
16726 triggersBounds: diff.any
16727 }, {
16728 name: 'min-height-bias-top',
16729 type: t.sizeMaybePercent,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'min-height-bias-bottom',
16733 type: t.sizeMaybePercent,
16734 triggersBounds: diff.any
16735 }];
16736 var edgeLine = [{
16737 name: 'line-style',
16738 type: t.lineStyle
16739 }, {
16740 name: 'line-color',
16741 type: t.color
16742 }, {
16743 name: 'line-fill',
16744 type: t.fill
16745 }, {
16746 name: 'line-cap',
16747 type: t.lineCap
16748 }, {
16749 name: 'line-opacity',
16750 type: t.zeroOneNumber
16751 }, {
16752 name: 'line-dash-pattern',
16753 type: t.numbers
16754 }, {
16755 name: 'line-dash-offset',
16756 type: t.number
16757 }, {
16758 name: 'line-gradient-stop-colors',
16759 type: t.colors
16760 }, {
16761 name: 'line-gradient-stop-positions',
16762 type: t.percentages
16763 }, {
16764 name: 'curve-style',
16765 type: t.curveStyle,
16766 triggersBounds: diff.any,
16767 triggersBoundsOfParallelBeziers: true
16768 }, {
16769 name: 'haystack-radius',
16770 type: t.zeroOneNumber,
16771 triggersBounds: diff.any
16772 }, {
16773 name: 'source-endpoint',
16774 type: t.edgeEndpoint,
16775 triggersBounds: diff.any
16776 }, {
16777 name: 'target-endpoint',
16778 type: t.edgeEndpoint,
16779 triggersBounds: diff.any
16780 }, {
16781 name: 'control-point-step-size',
16782 type: t.size,
16783 triggersBounds: diff.any
16784 }, {
16785 name: 'control-point-distances',
16786 type: t.bidirectionalSizes,
16787 triggersBounds: diff.any
16788 }, {
16789 name: 'control-point-weights',
16790 type: t.numbers,
16791 triggersBounds: diff.any
16792 }, {
16793 name: 'segment-distances',
16794 type: t.bidirectionalSizes,
16795 triggersBounds: diff.any
16796 }, {
16797 name: 'segment-weights',
16798 type: t.numbers,
16799 triggersBounds: diff.any
16800 }, {
16801 name: 'taxi-turn',
16802 type: t.bidirectionalSizeMaybePercent,
16803 triggersBounds: diff.any
16804 }, {
16805 name: 'taxi-turn-min-distance',
16806 type: t.size,
16807 triggersBounds: diff.any
16808 }, {
16809 name: 'taxi-direction',
16810 type: t.axisDirection,
16811 triggersBounds: diff.any
16812 }, {
16813 name: 'edge-distances',
16814 type: t.edgeDistances,
16815 triggersBounds: diff.any
16816 }, {
16817 name: 'arrow-scale',
16818 type: t.positiveNumber,
16819 triggersBounds: diff.any
16820 }, {
16821 name: 'loop-direction',
16822 type: t.angle,
16823 triggersBounds: diff.any
16824 }, {
16825 name: 'loop-sweep',
16826 type: t.angle,
16827 triggersBounds: diff.any
16828 }, {
16829 name: 'source-distance-from-node',
16830 type: t.size,
16831 triggersBounds: diff.any
16832 }, {
16833 name: 'target-distance-from-node',
16834 type: t.size,
16835 triggersBounds: diff.any
16836 }];
16837 var ghost = [{
16838 name: 'ghost',
16839 type: t.bool,
16840 triggersBounds: diff.any
16841 }, {
16842 name: 'ghost-offset-x',
16843 type: t.bidirectionalSize,
16844 triggersBounds: diff.any
16845 }, {
16846 name: 'ghost-offset-y',
16847 type: t.bidirectionalSize,
16848 triggersBounds: diff.any
16849 }, {
16850 name: 'ghost-opacity',
16851 type: t.zeroOneNumber
16852 }];
16853 var core = [{
16854 name: 'selection-box-color',
16855 type: t.color
16856 }, {
16857 name: 'selection-box-opacity',
16858 type: t.zeroOneNumber
16859 }, {
16860 name: 'selection-box-border-color',
16861 type: t.color
16862 }, {
16863 name: 'selection-box-border-width',
16864 type: t.size
16865 }, {
16866 name: 'active-bg-color',
16867 type: t.color
16868 }, {
16869 name: 'active-bg-opacity',
16870 type: t.zeroOneNumber
16871 }, {
16872 name: 'active-bg-size',
16873 type: t.size
16874 }, {
16875 name: 'outside-texture-bg-color',
16876 type: t.color
16877 }, {
16878 name: 'outside-texture-bg-opacity',
16879 type: t.zeroOneNumber
16880 }]; // pie backgrounds for nodes
16881
16882 var pie = [];
16883 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16884
16885 pie.push({
16886 name: 'pie-size',
16887 type: t.sizeMaybePercent
16888 });
16889
16890 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16891 pie.push({
16892 name: 'pie-' + i + '-background-color',
16893 type: t.color
16894 });
16895 pie.push({
16896 name: 'pie-' + i + '-background-size',
16897 type: t.percent
16898 });
16899 pie.push({
16900 name: 'pie-' + i + '-background-opacity',
16901 type: t.zeroOneNumber
16902 });
16903 } // edge arrows
16904
16905
16906 var edgeArrow = [];
16907 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16908 [{
16909 name: 'arrow-shape',
16910 type: t.arrowShape,
16911 triggersBounds: diff.any
16912 }, {
16913 name: 'arrow-color',
16914 type: t.color
16915 }, {
16916 name: 'arrow-fill',
16917 type: t.arrowFill
16918 }].forEach(function (prop) {
16919 arrowPrefixes.forEach(function (prefix) {
16920 var name = prefix + '-' + prop.name;
16921 var type = prop.type,
16922 triggersBounds = prop.triggersBounds;
16923 edgeArrow.push({
16924 name: name,
16925 type: type,
16926 triggersBounds: triggersBounds
16927 });
16928 });
16929 }, {});
16930 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, underlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16931 var propGroups = styfn$6.propertyGroups = {
16932 // common to all eles
16933 behavior: behavior,
16934 transition: transition,
16935 visibility: visibility,
16936 overlay: overlay,
16937 underlay: underlay,
16938 ghost: ghost,
16939 // labels
16940 commonLabel: commonLabel,
16941 labelDimensions: labelDimensions,
16942 mainLabel: mainLabel,
16943 sourceLabel: sourceLabel,
16944 targetLabel: targetLabel,
16945 // node props
16946 nodeBody: nodeBody,
16947 nodeBorder: nodeBorder,
16948 backgroundImage: backgroundImage,
16949 pie: pie,
16950 compound: compound,
16951 // edge props
16952 edgeLine: edgeLine,
16953 edgeArrow: edgeArrow,
16954 core: core
16955 };
16956 var propGroupNames = styfn$6.propertyGroupNames = {};
16957 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16958 propGroupKeys.forEach(function (key) {
16959 propGroupNames[key] = propGroups[key].map(function (prop) {
16960 return prop.name;
16961 });
16962 propGroups[key].forEach(function (prop) {
16963 return prop.groupKey = key;
16964 });
16965 }); // define aliases
16966
16967 var aliases = styfn$6.aliases = [{
16968 name: 'content',
16969 pointsTo: 'label'
16970 }, {
16971 name: 'control-point-distance',
16972 pointsTo: 'control-point-distances'
16973 }, {
16974 name: 'control-point-weight',
16975 pointsTo: 'control-point-weights'
16976 }, {
16977 name: 'edge-text-rotation',
16978 pointsTo: 'text-rotation'
16979 }, {
16980 name: 'padding-left',
16981 pointsTo: 'padding'
16982 }, {
16983 name: 'padding-right',
16984 pointsTo: 'padding'
16985 }, {
16986 name: 'padding-top',
16987 pointsTo: 'padding'
16988 }, {
16989 name: 'padding-bottom',
16990 pointsTo: 'padding'
16991 }]; // list of property names
16992
16993 styfn$6.propertyNames = props.map(function (p) {
16994 return p.name;
16995 }); // allow access of properties by name ( e.g. style.properties.height )
16996
16997 for (var _i = 0; _i < props.length; _i++) {
16998 var prop = props[_i];
16999 props[prop.name] = prop; // allow lookup by name
17000 } // map aliases
17001
17002
17003 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17004 var alias = aliases[_i2];
17005 var pointsToProp = props[alias.pointsTo];
17006 var aliasProp = {
17007 name: alias.name,
17008 alias: true,
17009 pointsTo: pointsToProp
17010 }; // add alias prop for parsing
17011
17012 props.push(aliasProp);
17013 props[alias.name] = aliasProp; // allow lookup by name
17014 }
17015})();
17016
17017styfn$6.getDefaultProperty = function (name) {
17018 return this.getDefaultProperties()[name];
17019};
17020
17021styfn$6.getDefaultProperties = function () {
17022 var _p = this._private;
17023
17024 if (_p.defaultProperties != null) {
17025 return _p.defaultProperties;
17026 }
17027
17028 var rawProps = extend({
17029 // core props
17030 'selection-box-color': '#ddd',
17031 'selection-box-opacity': 0.65,
17032 'selection-box-border-color': '#aaa',
17033 'selection-box-border-width': 1,
17034 'active-bg-color': 'black',
17035 'active-bg-opacity': 0.15,
17036 'active-bg-size': 30,
17037 'outside-texture-bg-color': '#000',
17038 'outside-texture-bg-opacity': 0.125,
17039 // common node/edge props
17040 'events': 'yes',
17041 'text-events': 'no',
17042 'text-valign': 'top',
17043 'text-halign': 'center',
17044 'text-justification': 'auto',
17045 'line-height': 1,
17046 'color': '#000',
17047 'text-outline-color': '#000',
17048 'text-outline-width': 0,
17049 'text-outline-opacity': 1,
17050 'text-opacity': 1,
17051 'text-decoration': 'none',
17052 'text-transform': 'none',
17053 'text-wrap': 'none',
17054 'text-overflow-wrap': 'whitespace',
17055 'text-max-width': 9999,
17056 'text-background-color': '#000',
17057 'text-background-opacity': 0,
17058 'text-background-shape': 'rectangle',
17059 'text-background-padding': 0,
17060 'text-border-opacity': 0,
17061 'text-border-width': 0,
17062 'text-border-style': 'solid',
17063 'text-border-color': '#000',
17064 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17065 'font-style': 'normal',
17066 'font-weight': 'normal',
17067 'font-size': 16,
17068 'min-zoomed-font-size': 0,
17069 'text-rotation': 'none',
17070 'source-text-rotation': 'none',
17071 'target-text-rotation': 'none',
17072 'visibility': 'visible',
17073 'display': 'element',
17074 'opacity': 1,
17075 'z-compound-depth': 'auto',
17076 'z-index-compare': 'auto',
17077 'z-index': 0,
17078 'label': '',
17079 'text-margin-x': 0,
17080 'text-margin-y': 0,
17081 'source-label': '',
17082 'source-text-offset': 0,
17083 'source-text-margin-x': 0,
17084 'source-text-margin-y': 0,
17085 'target-label': '',
17086 'target-text-offset': 0,
17087 'target-text-margin-x': 0,
17088 'target-text-margin-y': 0,
17089 'overlay-opacity': 0,
17090 'overlay-color': '#000',
17091 'overlay-padding': 10,
17092 'overlay-shape': 'round-rectangle',
17093 'underlay-opacity': 0,
17094 'underlay-color': '#000',
17095 'underlay-padding': 10,
17096 'underlay-shape': 'round-rectangle',
17097 'transition-property': 'none',
17098 'transition-duration': 0,
17099 'transition-delay': 0,
17100 'transition-timing-function': 'linear',
17101 // node props
17102 'background-blacken': 0,
17103 'background-color': '#999',
17104 'background-fill': 'solid',
17105 'background-opacity': 1,
17106 'background-image': 'none',
17107 'background-image-crossorigin': 'anonymous',
17108 'background-image-opacity': 1,
17109 'background-image-containment': 'inside',
17110 'background-image-smoothing': 'yes',
17111 'background-position-x': '50%',
17112 'background-position-y': '50%',
17113 'background-offset-x': 0,
17114 'background-offset-y': 0,
17115 'background-width-relative-to': 'include-padding',
17116 'background-height-relative-to': 'include-padding',
17117 'background-repeat': 'no-repeat',
17118 'background-fit': 'none',
17119 'background-clip': 'node',
17120 'background-width': 'auto',
17121 'background-height': 'auto',
17122 'border-color': '#000',
17123 'border-opacity': 1,
17124 'border-width': 0,
17125 'border-style': 'solid',
17126 'height': 30,
17127 'width': 30,
17128 'shape': 'ellipse',
17129 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17130 'bounds-expansion': 0,
17131 // node gradient
17132 'background-gradient-direction': 'to-bottom',
17133 'background-gradient-stop-colors': '#999',
17134 'background-gradient-stop-positions': '0%',
17135 // ghost props
17136 'ghost': 'no',
17137 'ghost-offset-y': 0,
17138 'ghost-offset-x': 0,
17139 'ghost-opacity': 0,
17140 // compound props
17141 'padding': 0,
17142 'padding-relative-to': 'width',
17143 'position': 'origin',
17144 'compound-sizing-wrt-labels': 'include',
17145 'min-width': 0,
17146 'min-width-bias-left': 0,
17147 'min-width-bias-right': 0,
17148 'min-height': 0,
17149 'min-height-bias-top': 0,
17150 'min-height-bias-bottom': 0
17151 }, {
17152 // node pie bg
17153 'pie-size': '100%'
17154 }, [{
17155 name: 'pie-{{i}}-background-color',
17156 value: 'black'
17157 }, {
17158 name: 'pie-{{i}}-background-size',
17159 value: '0%'
17160 }, {
17161 name: 'pie-{{i}}-background-opacity',
17162 value: 1
17163 }].reduce(function (css, prop) {
17164 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17165 var name = prop.name.replace('{{i}}', i);
17166 var val = prop.value;
17167 css[name] = val;
17168 }
17169
17170 return css;
17171 }, {}), {
17172 // edge props
17173 'line-style': 'solid',
17174 'line-color': '#999',
17175 'line-fill': 'solid',
17176 'line-cap': 'butt',
17177 'line-opacity': 1,
17178 'line-gradient-stop-colors': '#999',
17179 'line-gradient-stop-positions': '0%',
17180 'control-point-step-size': 40,
17181 'control-point-weights': 0.5,
17182 'segment-weights': 0.5,
17183 'segment-distances': 20,
17184 'taxi-turn': '50%',
17185 'taxi-turn-min-distance': 10,
17186 'taxi-direction': 'auto',
17187 'edge-distances': 'intersection',
17188 'curve-style': 'haystack',
17189 'haystack-radius': 0,
17190 'arrow-scale': 1,
17191 'loop-direction': '-45deg',
17192 'loop-sweep': '-90deg',
17193 'source-distance-from-node': 0,
17194 'target-distance-from-node': 0,
17195 'source-endpoint': 'outside-to-node',
17196 'target-endpoint': 'outside-to-node',
17197 'line-dash-pattern': [6, 3],
17198 'line-dash-offset': 0
17199 }, [{
17200 name: 'arrow-shape',
17201 value: 'none'
17202 }, {
17203 name: 'arrow-color',
17204 value: '#999'
17205 }, {
17206 name: 'arrow-fill',
17207 value: 'filled'
17208 }].reduce(function (css, prop) {
17209 styfn$6.arrowPrefixes.forEach(function (prefix) {
17210 var name = prefix + '-' + prop.name;
17211 var val = prop.value;
17212 css[name] = val;
17213 });
17214 return css;
17215 }, {}));
17216 var parsedProps = {};
17217
17218 for (var i = 0; i < this.properties.length; i++) {
17219 var prop = this.properties[i];
17220
17221 if (prop.pointsTo) {
17222 continue;
17223 }
17224
17225 var name = prop.name;
17226 var val = rawProps[name];
17227 var parsedProp = this.parse(name, val);
17228 parsedProps[name] = parsedProp;
17229 }
17230
17231 _p.defaultProperties = parsedProps;
17232 return _p.defaultProperties;
17233};
17234
17235styfn$6.addDefaultStylesheet = function () {
17236 this.selector(':parent').css({
17237 'shape': 'rectangle',
17238 'padding': 10,
17239 'background-color': '#eee',
17240 'border-color': '#ccc',
17241 'border-width': 1
17242 }).selector('edge').css({
17243 'width': 3
17244 }).selector(':loop').css({
17245 'curve-style': 'bezier'
17246 }).selector('edge:compound').css({
17247 'curve-style': 'bezier',
17248 'source-endpoint': 'outside-to-line',
17249 'target-endpoint': 'outside-to-line'
17250 }).selector(':selected').css({
17251 'background-color': '#0169D9',
17252 'line-color': '#0169D9',
17253 'source-arrow-color': '#0169D9',
17254 'target-arrow-color': '#0169D9',
17255 'mid-source-arrow-color': '#0169D9',
17256 'mid-target-arrow-color': '#0169D9'
17257 }).selector(':parent:selected').css({
17258 'background-color': '#CCE1F9',
17259 'border-color': '#aec8e5'
17260 }).selector(':active').css({
17261 'overlay-color': 'black',
17262 'overlay-padding': 10,
17263 'overlay-opacity': 0.25
17264 });
17265 this.defaultLength = this.length;
17266};
17267
17268var styfn$7 = {}; // a caching layer for property parsing
17269
17270styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17271 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17272
17273 if (fn(value)) {
17274 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17275 }
17276
17277 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17278 var bypassKey = propIsBypass ? 't' : 'f';
17279 var valueKey = '' + value;
17280 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17281 var propCache = self.propCache = self.propCache || [];
17282 var ret;
17283
17284 if (!(ret = propCache[argHash])) {
17285 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17286 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17287 // - mappings can't be shared b/c mappings are per-element
17288
17289
17290 if (propIsBypass || propIsFlat === 'mapping') {
17291 // need a copy since props are mutated later in their lifecycles
17292 ret = copy(ret);
17293
17294 if (ret) {
17295 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17296 }
17297 }
17298
17299 return ret;
17300};
17301
17302styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17303 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17304
17305 if (!prop && value != null) {
17306 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17307 }
17308
17309 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17310 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17311 }
17312
17313 return prop;
17314}; // parse a property; return null on invalid; return parsed property otherwise
17315// fields :
17316// - name : the name of the property
17317// - value : the parsed, native-typed value of the property
17318// - strValue : a string value that represents the property value in valid css
17319// - bypass : true iff the property is a bypass property
17320
17321
17322styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17323 var self = this;
17324 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17325
17326 var property = self.properties[name];
17327 var passedValue = value;
17328 var types = self.types;
17329
17330 if (!property) {
17331 return null;
17332 } // return null on property of unknown name
17333
17334
17335 if (value === undefined) {
17336 return null;
17337 } // can't assign undefined
17338 // the property may be an alias
17339
17340
17341 if (property.alias) {
17342 property = property.pointsTo;
17343 name = property.name;
17344 }
17345
17346 var valueIsString = string(value);
17347
17348 if (valueIsString) {
17349 // trim the value to make parsing easier
17350 value = value.trim();
17351 }
17352
17353 var type = property.type;
17354
17355 if (!type) {
17356 return null;
17357 } // no type, no luck
17358 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17359
17360
17361 if (propIsBypass && (value === '' || value === null)) {
17362 return {
17363 name: name,
17364 value: value,
17365 bypass: true,
17366 deleteBypass: true
17367 };
17368 } // check if value is a function used as a mapper
17369
17370
17371 if (fn(value)) {
17372 return {
17373 name: name,
17374 value: value,
17375 strValue: 'fn',
17376 mapped: types.fn,
17377 bypass: propIsBypass
17378 };
17379 } // check if value is mapped
17380
17381
17382 var data, mapData;
17383
17384 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))) {
17385 if (propIsBypass) {
17386 return false;
17387 } // mappers not allowed in bypass
17388
17389
17390 var mapped = types.data;
17391 return {
17392 name: name,
17393 value: data,
17394 strValue: '' + value,
17395 mapped: mapped,
17396 field: data[1],
17397 bypass: propIsBypass
17398 };
17399 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17400 if (propIsBypass) {
17401 return false;
17402 } // mappers not allowed in bypass
17403
17404
17405 if (type.multiple) {
17406 return false;
17407 } // impossible to map to num
17408
17409
17410 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17411
17412 if (!(type.color || type.number)) {
17413 return false;
17414 }
17415
17416 var valueMin = this.parse(name, mapData[4]); // parse to validate
17417
17418 if (!valueMin || valueMin.mapped) {
17419 return false;
17420 } // can't be invalid or mapped
17421
17422
17423 var valueMax = this.parse(name, mapData[5]); // parse to validate
17424
17425 if (!valueMax || valueMax.mapped) {
17426 return false;
17427 } // can't be invalid or mapped
17428 // check if valueMin and valueMax are the same
17429
17430
17431 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17432 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17433 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17434 } else if (type.color) {
17435 var c1 = valueMin.value;
17436 var c2 = valueMax.value;
17437 var same = c1[0] === c2[0] // red
17438 && c1[1] === c2[1] // green
17439 && c1[2] === c2[2] // blue
17440 && ( // optional alpha
17441 c1[3] === c2[3] // same alpha outright
17442 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17443 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17444 );
17445
17446 if (same) {
17447 return false;
17448 } // can't make a mapper without a range
17449
17450 }
17451
17452 return {
17453 name: name,
17454 value: mapData,
17455 strValue: '' + value,
17456 mapped: _mapped,
17457 field: mapData[1],
17458 fieldMin: parseFloat(mapData[2]),
17459 // min & max are numeric
17460 fieldMax: parseFloat(mapData[3]),
17461 valueMin: valueMin.value,
17462 valueMax: valueMax.value,
17463 bypass: propIsBypass
17464 };
17465 }
17466
17467 if (type.multiple && propIsFlat !== 'multiple') {
17468 var vals;
17469
17470 if (valueIsString) {
17471 vals = value.split(/\s+/);
17472 } else if (array(value)) {
17473 vals = value;
17474 } else {
17475 vals = [value];
17476 }
17477
17478 if (type.evenMultiple && vals.length % 2 !== 0) {
17479 return null;
17480 }
17481
17482 var valArr = [];
17483 var unitsArr = [];
17484 var pfValArr = [];
17485 var strVal = '';
17486 var hasEnum = false;
17487
17488 for (var i = 0; i < vals.length; i++) {
17489 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17490 hasEnum = hasEnum || string(p.value);
17491 valArr.push(p.value);
17492 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17493 unitsArr.push(p.units);
17494 strVal += (i > 0 ? ' ' : '') + p.strValue;
17495 }
17496
17497 if (type.validate && !type.validate(valArr, unitsArr)) {
17498 return null;
17499 }
17500
17501 if (type.singleEnum && hasEnum) {
17502 if (valArr.length === 1 && string(valArr[0])) {
17503 return {
17504 name: name,
17505 value: valArr[0],
17506 strValue: valArr[0],
17507 bypass: propIsBypass
17508 };
17509 } else {
17510 return null;
17511 }
17512 }
17513
17514 return {
17515 name: name,
17516 value: valArr,
17517 pfValue: pfValArr,
17518 strValue: strVal,
17519 bypass: propIsBypass,
17520 units: unitsArr
17521 };
17522 } // several types also allow enums
17523
17524
17525 var checkEnums = function checkEnums() {
17526 for (var _i = 0; _i < type.enums.length; _i++) {
17527 var en = type.enums[_i];
17528
17529 if (en === value) {
17530 return {
17531 name: name,
17532 value: value,
17533 strValue: '' + value,
17534 bypass: propIsBypass
17535 };
17536 }
17537 }
17538
17539 return null;
17540 }; // check the type and return the appropriate object
17541
17542
17543 if (type.number) {
17544 var units;
17545 var implicitUnits = 'px'; // not set => px
17546
17547 if (type.units) {
17548 // use specified units if set
17549 units = type.units;
17550 }
17551
17552 if (type.implicitUnits) {
17553 implicitUnits = type.implicitUnits;
17554 }
17555
17556 if (!type.unitless) {
17557 if (valueIsString) {
17558 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17559
17560 if (units) {
17561 unitsRegex = units;
17562 } // only allow explicit units if so set
17563
17564
17565 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17566
17567 if (match) {
17568 value = match[1];
17569 units = match[2] || implicitUnits;
17570 }
17571 } else if (!units || type.implicitUnits) {
17572 units = implicitUnits; // implicitly px if unspecified
17573 }
17574 }
17575
17576 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17577
17578 if (isNaN(value) && type.enums === undefined) {
17579 return null;
17580 } // check if this number type also accepts special keywords in place of numbers
17581 // (i.e. `left`, `auto`, etc)
17582
17583
17584 if (isNaN(value) && type.enums !== undefined) {
17585 value = passedValue;
17586 return checkEnums();
17587 } // check if value must be an integer
17588
17589
17590 if (type.integer && !integer(value)) {
17591 return null;
17592 } // check value is within range
17593
17594
17595 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17596 return null;
17597 }
17598
17599 var ret = {
17600 name: name,
17601 value: value,
17602 strValue: '' + value + (units ? units : ''),
17603 units: units,
17604 bypass: propIsBypass
17605 }; // normalise value in pixels
17606
17607 if (type.unitless || units !== 'px' && units !== 'em') {
17608 ret.pfValue = value;
17609 } else {
17610 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17611 } // normalise value in ms
17612
17613
17614 if (units === 'ms' || units === 's') {
17615 ret.pfValue = units === 'ms' ? value : 1000 * value;
17616 } // normalise value in rad
17617
17618
17619 if (units === 'deg' || units === 'rad') {
17620 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17621 } // normalize value in %
17622
17623
17624 if (units === '%') {
17625 ret.pfValue = value / 100;
17626 }
17627
17628 return ret;
17629 } else if (type.propList) {
17630 var props = [];
17631 var propsStr = '' + value;
17632
17633 if (propsStr === 'none') ; else {
17634 // go over each prop
17635 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17636
17637 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17638 var propName = propsSplit[_i2].trim();
17639
17640 if (self.properties[propName]) {
17641 props.push(propName);
17642 } else {
17643 warn('`' + propName + '` is not a valid property name');
17644 }
17645 }
17646
17647 if (props.length === 0) {
17648 return null;
17649 }
17650 }
17651
17652 return {
17653 name: name,
17654 value: props,
17655 strValue: props.length === 0 ? 'none' : props.join(' '),
17656 bypass: propIsBypass
17657 };
17658 } else if (type.color) {
17659 var tuple = color2tuple(value);
17660
17661 if (!tuple) {
17662 return null;
17663 }
17664
17665 return {
17666 name: name,
17667 value: tuple,
17668 pfValue: tuple,
17669 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17670 // n.b. no spaces b/c of multiple support
17671 bypass: propIsBypass
17672 };
17673 } else if (type.regex || type.regexes) {
17674 // first check enums
17675 if (type.enums) {
17676 var enumProp = checkEnums();
17677
17678 if (enumProp) {
17679 return enumProp;
17680 }
17681 }
17682
17683 var regexes = type.regexes ? type.regexes : [type.regex];
17684
17685 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17686 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17687
17688 var m = regex.exec(value);
17689
17690 if (m) {
17691 // regex matches
17692 return {
17693 name: name,
17694 value: type.singleRegexMatchValue ? m[1] : m,
17695 strValue: '' + value,
17696 bypass: propIsBypass
17697 };
17698 }
17699 }
17700
17701 return null; // didn't match any
17702 } else if (type.string) {
17703 // just return
17704 return {
17705 name: name,
17706 value: '' + value,
17707 strValue: '' + value,
17708 bypass: propIsBypass
17709 };
17710 } else if (type.enums) {
17711 // check enums last because it's a combo type in others
17712 return checkEnums();
17713 } else {
17714 return null; // not a type we can handle
17715 }
17716};
17717
17718var Style = function Style(cy) {
17719 if (!(this instanceof Style)) {
17720 return new Style(cy);
17721 }
17722
17723 if (!core(cy)) {
17724 error('A style must have a core reference');
17725 return;
17726 }
17727
17728 this._private = {
17729 cy: cy,
17730 coreStyle: {}
17731 };
17732 this.length = 0;
17733 this.resetToDefault();
17734};
17735
17736var styfn$8 = Style.prototype;
17737
17738styfn$8.instanceString = function () {
17739 return 'style';
17740}; // remove all contexts
17741
17742
17743styfn$8.clear = function () {
17744 var _p = this._private;
17745 var cy = _p.cy;
17746 var eles = cy.elements();
17747
17748 for (var i = 0; i < this.length; i++) {
17749 this[i] = undefined;
17750 }
17751
17752 this.length = 0;
17753 _p.contextStyles = {};
17754 _p.propDiffs = {};
17755 this.cleanElements(eles, true);
17756 eles.forEach(function (ele) {
17757 var ele_p = ele[0]._private;
17758 ele_p.styleDirty = true;
17759 ele_p.appliedInitStyle = false;
17760 });
17761 return this; // chaining
17762};
17763
17764styfn$8.resetToDefault = function () {
17765 this.clear();
17766 this.addDefaultStylesheet();
17767 return this;
17768}; // builds a style object for the 'core' selector
17769
17770
17771styfn$8.core = function (propName) {
17772 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17773}; // create a new context from the specified selector string and switch to that context
17774
17775
17776styfn$8.selector = function (selectorStr) {
17777 // 'core' is a special case and does not need a selector
17778 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17779 var i = this.length++; // new context means new index
17780
17781 this[i] = {
17782 selector: selector,
17783 properties: [],
17784 mappedProperties: [],
17785 index: i
17786 };
17787 return this; // chaining
17788}; // add one or many css rules to the current context
17789
17790
17791styfn$8.css = function () {
17792 var self = this;
17793 var args = arguments;
17794
17795 if (args.length === 1) {
17796 var map = args[0];
17797
17798 for (var i = 0; i < self.properties.length; i++) {
17799 var prop = self.properties[i];
17800 var mapVal = map[prop.name];
17801
17802 if (mapVal === undefined) {
17803 mapVal = map[dash2camel(prop.name)];
17804 }
17805
17806 if (mapVal !== undefined) {
17807 this.cssRule(prop.name, mapVal);
17808 }
17809 }
17810 } else if (args.length === 2) {
17811 this.cssRule(args[0], args[1]);
17812 } // do nothing if args are invalid
17813
17814
17815 return this; // chaining
17816};
17817
17818styfn$8.style = styfn$8.css; // add a single css rule to the current context
17819
17820styfn$8.cssRule = function (name, value) {
17821 // name-value pair
17822 var property = this.parse(name, value); // add property to current context if valid
17823
17824 if (property) {
17825 var i = this.length - 1;
17826 this[i].properties.push(property);
17827 this[i].properties[property.name] = property; // allow access by name as well
17828
17829 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17830 this._private.hasPie = true;
17831 }
17832
17833 if (property.mapped) {
17834 this[i].mappedProperties.push(property);
17835 } // add to core style if necessary
17836
17837
17838 var currentSelectorIsCore = !this[i].selector;
17839
17840 if (currentSelectorIsCore) {
17841 this._private.coreStyle[property.name] = property;
17842 }
17843 }
17844
17845 return this; // chaining
17846};
17847
17848styfn$8.append = function (style) {
17849 if (stylesheet(style)) {
17850 style.appendToStyle(this);
17851 } else if (array(style)) {
17852 this.appendFromJson(style);
17853 } else if (string(style)) {
17854 this.appendFromString(style);
17855 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17856
17857
17858 return this;
17859}; // static function
17860
17861
17862Style.fromJson = function (cy, json) {
17863 var style = new Style(cy);
17864 style.fromJson(json);
17865 return style;
17866};
17867
17868Style.fromString = function (cy, string) {
17869 return new Style(cy).fromString(string);
17870};
17871
17872[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17873 extend(styfn$8, props);
17874});
17875Style.types = styfn$8.types;
17876Style.properties = styfn$8.properties;
17877Style.propertyGroups = styfn$8.propertyGroups;
17878Style.propertyGroupNames = styfn$8.propertyGroupNames;
17879Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17880
17881var corefn$7 = {
17882 style: function style(newStyle) {
17883 if (newStyle) {
17884 var s = this.setStyle(newStyle);
17885 s.update();
17886 }
17887
17888 return this._private.style;
17889 },
17890 setStyle: function setStyle(style) {
17891 var _p = this._private;
17892
17893 if (stylesheet(style)) {
17894 _p.style = style.generateStyle(this);
17895 } else if (array(style)) {
17896 _p.style = Style.fromJson(this, style);
17897 } else if (string(style)) {
17898 _p.style = Style.fromString(this, style);
17899 } else {
17900 _p.style = Style(this);
17901 }
17902
17903 return _p.style;
17904 },
17905 // e.g. cy.data() changed => recalc ele mappers
17906 updateStyle: function updateStyle() {
17907 this.mutableElements().updateStyle(); // just send to all eles
17908 }
17909};
17910
17911var defaultSelectionType = 'single';
17912var corefn$8 = {
17913 autolock: function autolock(bool) {
17914 if (bool !== undefined) {
17915 this._private.autolock = bool ? true : false;
17916 } else {
17917 return this._private.autolock;
17918 }
17919
17920 return this; // chaining
17921 },
17922 autoungrabify: function autoungrabify(bool) {
17923 if (bool !== undefined) {
17924 this._private.autoungrabify = bool ? true : false;
17925 } else {
17926 return this._private.autoungrabify;
17927 }
17928
17929 return this; // chaining
17930 },
17931 autounselectify: function autounselectify(bool) {
17932 if (bool !== undefined) {
17933 this._private.autounselectify = bool ? true : false;
17934 } else {
17935 return this._private.autounselectify;
17936 }
17937
17938 return this; // chaining
17939 },
17940 selectionType: function selectionType(selType) {
17941 var _p = this._private;
17942
17943 if (_p.selectionType == null) {
17944 _p.selectionType = defaultSelectionType;
17945 }
17946
17947 if (selType !== undefined) {
17948 if (selType === 'additive' || selType === 'single') {
17949 _p.selectionType = selType;
17950 }
17951 } else {
17952 return _p.selectionType;
17953 }
17954
17955 return this;
17956 },
17957 panningEnabled: function panningEnabled(bool) {
17958 if (bool !== undefined) {
17959 this._private.panningEnabled = bool ? true : false;
17960 } else {
17961 return this._private.panningEnabled;
17962 }
17963
17964 return this; // chaining
17965 },
17966 userPanningEnabled: function userPanningEnabled(bool) {
17967 if (bool !== undefined) {
17968 this._private.userPanningEnabled = bool ? true : false;
17969 } else {
17970 return this._private.userPanningEnabled;
17971 }
17972
17973 return this; // chaining
17974 },
17975 zoomingEnabled: function zoomingEnabled(bool) {
17976 if (bool !== undefined) {
17977 this._private.zoomingEnabled = bool ? true : false;
17978 } else {
17979 return this._private.zoomingEnabled;
17980 }
17981
17982 return this; // chaining
17983 },
17984 userZoomingEnabled: function userZoomingEnabled(bool) {
17985 if (bool !== undefined) {
17986 this._private.userZoomingEnabled = bool ? true : false;
17987 } else {
17988 return this._private.userZoomingEnabled;
17989 }
17990
17991 return this; // chaining
17992 },
17993 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17994 if (bool !== undefined) {
17995 this._private.boxSelectionEnabled = bool ? true : false;
17996 } else {
17997 return this._private.boxSelectionEnabled;
17998 }
17999
18000 return this; // chaining
18001 },
18002 pan: function pan() {
18003 var args = arguments;
18004 var pan = this._private.pan;
18005 var dim, val, dims, x, y;
18006
18007 switch (args.length) {
18008 case 0:
18009 // .pan()
18010 return pan;
18011
18012 case 1:
18013 if (string(args[0])) {
18014 // .pan('x')
18015 dim = args[0];
18016 return pan[dim];
18017 } else if (plainObject(args[0])) {
18018 // .pan({ x: 0, y: 100 })
18019 if (!this._private.panningEnabled) {
18020 return this;
18021 }
18022
18023 dims = args[0];
18024 x = dims.x;
18025 y = dims.y;
18026
18027 if (number(x)) {
18028 pan.x = x;
18029 }
18030
18031 if (number(y)) {
18032 pan.y = y;
18033 }
18034
18035 this.emit('pan viewport');
18036 }
18037
18038 break;
18039
18040 case 2:
18041 // .pan('x', 100)
18042 if (!this._private.panningEnabled) {
18043 return this;
18044 }
18045
18046 dim = args[0];
18047 val = args[1];
18048
18049 if ((dim === 'x' || dim === 'y') && number(val)) {
18050 pan[dim] = val;
18051 }
18052
18053 this.emit('pan viewport');
18054 break;
18055 // invalid
18056 }
18057
18058 this.notify('viewport');
18059 return this; // chaining
18060 },
18061 panBy: function panBy(arg0, arg1) {
18062 var args = arguments;
18063 var pan = this._private.pan;
18064 var dim, val, dims, x, y;
18065
18066 if (!this._private.panningEnabled) {
18067 return this;
18068 }
18069
18070 switch (args.length) {
18071 case 1:
18072 if (plainObject(arg0)) {
18073 // .panBy({ x: 0, y: 100 })
18074 dims = args[0];
18075 x = dims.x;
18076 y = dims.y;
18077
18078 if (number(x)) {
18079 pan.x += x;
18080 }
18081
18082 if (number(y)) {
18083 pan.y += y;
18084 }
18085
18086 this.emit('pan viewport');
18087 }
18088
18089 break;
18090
18091 case 2:
18092 // .panBy('x', 100)
18093 dim = arg0;
18094 val = arg1;
18095
18096 if ((dim === 'x' || dim === 'y') && number(val)) {
18097 pan[dim] += val;
18098 }
18099
18100 this.emit('pan viewport');
18101 break;
18102 // invalid
18103 }
18104
18105 this.notify('viewport');
18106 return this; // chaining
18107 },
18108 fit: function fit(elements, padding) {
18109 var viewportState = this.getFitViewport(elements, padding);
18110
18111 if (viewportState) {
18112 var _p = this._private;
18113 _p.zoom = viewportState.zoom;
18114 _p.pan = viewportState.pan;
18115 this.emit('pan zoom viewport');
18116 this.notify('viewport');
18117 }
18118
18119 return this; // chaining
18120 },
18121 getFitViewport: function getFitViewport(elements, padding) {
18122 if (number(elements) && padding === undefined) {
18123 // elements is optional
18124 padding = elements;
18125 elements = undefined;
18126 }
18127
18128 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18129 return;
18130 }
18131
18132 var bb;
18133
18134 if (string(elements)) {
18135 var sel = elements;
18136 elements = this.$(sel);
18137 } else if (boundingBox(elements)) {
18138 // assume bb
18139 var bbe = elements;
18140 bb = {
18141 x1: bbe.x1,
18142 y1: bbe.y1,
18143 x2: bbe.x2,
18144 y2: bbe.y2
18145 };
18146 bb.w = bb.x2 - bb.x1;
18147 bb.h = bb.y2 - bb.y1;
18148 } else if (!elementOrCollection(elements)) {
18149 elements = this.mutableElements();
18150 }
18151
18152 if (elementOrCollection(elements) && elements.empty()) {
18153 return;
18154 } // can't fit to nothing
18155
18156
18157 bb = bb || elements.boundingBox();
18158 var w = this.width();
18159 var h = this.height();
18160 var zoom;
18161 padding = number(padding) ? padding : 0;
18162
18163 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18164 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18165
18166 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18167 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18168 var pan = {
18169 // now pan to middle
18170 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18171 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18172 };
18173 return {
18174 zoom: zoom,
18175 pan: pan
18176 };
18177 }
18178
18179 return;
18180 },
18181 zoomRange: function zoomRange(min, max) {
18182 var _p = this._private;
18183
18184 if (max == null) {
18185 var opts = min;
18186 min = opts.min;
18187 max = opts.max;
18188 }
18189
18190 if (number(min) && number(max) && min <= max) {
18191 _p.minZoom = min;
18192 _p.maxZoom = max;
18193 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18194 _p.minZoom = min;
18195 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18196 _p.maxZoom = max;
18197 }
18198
18199 return this;
18200 },
18201 minZoom: function minZoom(zoom) {
18202 if (zoom === undefined) {
18203 return this._private.minZoom;
18204 } else {
18205 return this.zoomRange({
18206 min: zoom
18207 });
18208 }
18209 },
18210 maxZoom: function maxZoom(zoom) {
18211 if (zoom === undefined) {
18212 return this._private.maxZoom;
18213 } else {
18214 return this.zoomRange({
18215 max: zoom
18216 });
18217 }
18218 },
18219 getZoomedViewport: function getZoomedViewport(params) {
18220 var _p = this._private;
18221 var currentPan = _p.pan;
18222 var currentZoom = _p.zoom;
18223 var pos; // in rendered px
18224
18225 var zoom;
18226 var bail = false;
18227
18228 if (!_p.zoomingEnabled) {
18229 // zooming disabled
18230 bail = true;
18231 }
18232
18233 if (number(params)) {
18234 // then set the zoom
18235 zoom = params;
18236 } else if (plainObject(params)) {
18237 // then zoom about a point
18238 zoom = params.level;
18239
18240 if (params.position != null) {
18241 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18242 } else if (params.renderedPosition != null) {
18243 pos = params.renderedPosition;
18244 }
18245
18246 if (pos != null && !_p.panningEnabled) {
18247 // panning disabled
18248 bail = true;
18249 }
18250 } // crop zoom
18251
18252
18253 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18254 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18255
18256 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18257 return null;
18258 }
18259
18260 if (pos != null) {
18261 // set zoom about position
18262 var pan1 = currentPan;
18263 var zoom1 = currentZoom;
18264 var zoom2 = zoom;
18265 var pan2 = {
18266 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18267 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18268 };
18269 return {
18270 zoomed: true,
18271 panned: true,
18272 zoom: zoom2,
18273 pan: pan2
18274 };
18275 } else {
18276 // just set the zoom
18277 return {
18278 zoomed: true,
18279 panned: false,
18280 zoom: zoom,
18281 pan: currentPan
18282 };
18283 }
18284 },
18285 zoom: function zoom(params) {
18286 if (params === undefined) {
18287 // get
18288 return this._private.zoom;
18289 } else {
18290 // set
18291 var vp = this.getZoomedViewport(params);
18292 var _p = this._private;
18293
18294 if (vp == null || !vp.zoomed) {
18295 return this;
18296 }
18297
18298 _p.zoom = vp.zoom;
18299
18300 if (vp.panned) {
18301 _p.pan.x = vp.pan.x;
18302 _p.pan.y = vp.pan.y;
18303 }
18304
18305 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18306 this.notify('viewport');
18307 return this; // chaining
18308 }
18309 },
18310 viewport: function viewport(opts) {
18311 var _p = this._private;
18312 var zoomDefd = true;
18313 var panDefd = true;
18314 var events = []; // to trigger
18315
18316 var zoomFailed = false;
18317 var panFailed = false;
18318
18319 if (!opts) {
18320 return this;
18321 }
18322
18323 if (!number(opts.zoom)) {
18324 zoomDefd = false;
18325 }
18326
18327 if (!plainObject(opts.pan)) {
18328 panDefd = false;
18329 }
18330
18331 if (!zoomDefd && !panDefd) {
18332 return this;
18333 }
18334
18335 if (zoomDefd) {
18336 var z = opts.zoom;
18337
18338 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18339 zoomFailed = true;
18340 } else {
18341 _p.zoom = z;
18342 events.push('zoom');
18343 }
18344 }
18345
18346 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18347 var p = opts.pan;
18348
18349 if (number(p.x)) {
18350 _p.pan.x = p.x;
18351 panFailed = false;
18352 }
18353
18354 if (number(p.y)) {
18355 _p.pan.y = p.y;
18356 panFailed = false;
18357 }
18358
18359 if (!panFailed) {
18360 events.push('pan');
18361 }
18362 }
18363
18364 if (events.length > 0) {
18365 events.push('viewport');
18366 this.emit(events.join(' '));
18367 this.notify('viewport');
18368 }
18369
18370 return this; // chaining
18371 },
18372 center: function center(elements) {
18373 var pan = this.getCenterPan(elements);
18374
18375 if (pan) {
18376 this._private.pan = pan;
18377 this.emit('pan viewport');
18378 this.notify('viewport');
18379 }
18380
18381 return this; // chaining
18382 },
18383 getCenterPan: function getCenterPan(elements, zoom) {
18384 if (!this._private.panningEnabled) {
18385 return;
18386 }
18387
18388 if (string(elements)) {
18389 var selector = elements;
18390 elements = this.mutableElements().filter(selector);
18391 } else if (!elementOrCollection(elements)) {
18392 elements = this.mutableElements();
18393 }
18394
18395 if (elements.length === 0) {
18396 return;
18397 } // can't centre pan to nothing
18398
18399
18400 var bb = elements.boundingBox();
18401 var w = this.width();
18402 var h = this.height();
18403 zoom = zoom === undefined ? this._private.zoom : zoom;
18404 var pan = {
18405 // middle
18406 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18407 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18408 };
18409 return pan;
18410 },
18411 reset: function reset() {
18412 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18413 return this;
18414 }
18415
18416 this.viewport({
18417 pan: {
18418 x: 0,
18419 y: 0
18420 },
18421 zoom: 1
18422 });
18423 return this; // chaining
18424 },
18425 invalidateSize: function invalidateSize() {
18426 this._private.sizeCache = null;
18427 },
18428 size: function size() {
18429 var _p = this._private;
18430 var container = _p.container;
18431 return _p.sizeCache = _p.sizeCache || (container ? function () {
18432 var style = window$1.getComputedStyle(container);
18433
18434 var val = function val(name) {
18435 return parseFloat(style.getPropertyValue(name));
18436 };
18437
18438 return {
18439 width: container.clientWidth - val('padding-left') - val('padding-right'),
18440 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18441 };
18442 }() : {
18443 // fallback if no container (not 0 b/c can be used for dividing etc)
18444 width: 1,
18445 height: 1
18446 });
18447 },
18448 width: function width() {
18449 return this.size().width;
18450 },
18451 height: function height() {
18452 return this.size().height;
18453 },
18454 extent: function extent() {
18455 var pan = this._private.pan;
18456 var zoom = this._private.zoom;
18457 var rb = this.renderedExtent();
18458 var b = {
18459 x1: (rb.x1 - pan.x) / zoom,
18460 x2: (rb.x2 - pan.x) / zoom,
18461 y1: (rb.y1 - pan.y) / zoom,
18462 y2: (rb.y2 - pan.y) / zoom
18463 };
18464 b.w = b.x2 - b.x1;
18465 b.h = b.y2 - b.y1;
18466 return b;
18467 },
18468 renderedExtent: function renderedExtent() {
18469 var width = this.width();
18470 var height = this.height();
18471 return {
18472 x1: 0,
18473 y1: 0,
18474 x2: width,
18475 y2: height,
18476 w: width,
18477 h: height
18478 };
18479 },
18480 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18481 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18482 return this; // chaining
18483 }
18484}; // aliases
18485
18486corefn$8.centre = corefn$8.center; // backwards compatibility
18487
18488corefn$8.autolockNodes = corefn$8.autolock;
18489corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18490
18491var fn$6 = {
18492 data: define$3.data({
18493 field: 'data',
18494 bindingEvent: 'data',
18495 allowBinding: true,
18496 allowSetting: true,
18497 settingEvent: 'data',
18498 settingTriggersEvent: true,
18499 triggerFnName: 'trigger',
18500 allowGetting: true,
18501 updateStyle: true
18502 }),
18503 removeData: define$3.removeData({
18504 field: 'data',
18505 event: 'data',
18506 triggerFnName: 'trigger',
18507 triggerEvent: true,
18508 updateStyle: true
18509 }),
18510 scratch: define$3.data({
18511 field: 'scratch',
18512 bindingEvent: 'scratch',
18513 allowBinding: true,
18514 allowSetting: true,
18515 settingEvent: 'scratch',
18516 settingTriggersEvent: true,
18517 triggerFnName: 'trigger',
18518 allowGetting: true,
18519 updateStyle: true
18520 }),
18521 removeScratch: define$3.removeData({
18522 field: 'scratch',
18523 event: 'scratch',
18524 triggerFnName: 'trigger',
18525 triggerEvent: true,
18526 updateStyle: true
18527 })
18528}; // aliases
18529
18530fn$6.attr = fn$6.data;
18531fn$6.removeAttr = fn$6.removeData;
18532
18533var Core = function Core(opts) {
18534 var cy = this;
18535 opts = extend({}, opts);
18536 var container = opts.container; // allow for passing a wrapped jquery object
18537 // e.g. cytoscape({ container: $('#cy') })
18538
18539 if (container && !htmlElement(container) && htmlElement(container[0])) {
18540 container = container[0];
18541 }
18542
18543 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18544
18545 reg = reg || {};
18546
18547 if (reg && reg.cy) {
18548 reg.cy.destroy();
18549 reg = {}; // old instance => replace reg completely
18550 }
18551
18552 var readies = reg.readies = reg.readies || [];
18553
18554 if (container) {
18555 container._cyreg = reg;
18556 } // make sure container assoc'd reg points to this cy
18557
18558
18559 reg.cy = cy;
18560 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18561 var options = opts;
18562 options.layout = extend({
18563 name: head ? 'grid' : 'null'
18564 }, options.layout);
18565 options.renderer = extend({
18566 name: head ? 'canvas' : 'null'
18567 }, options.renderer);
18568
18569 var defVal = function defVal(def, val, altVal) {
18570 if (val !== undefined) {
18571 return val;
18572 } else if (altVal !== undefined) {
18573 return altVal;
18574 } else {
18575 return def;
18576 }
18577 };
18578
18579 var _p = this._private = {
18580 container: container,
18581 // html dom ele container
18582 ready: false,
18583 // whether ready has been triggered
18584 options: options,
18585 // cached options
18586 elements: new Collection(this),
18587 // elements in the graph
18588 listeners: [],
18589 // list of listeners
18590 aniEles: new Collection(this),
18591 // elements being animated
18592 data: options.data || {},
18593 // data for the core
18594 scratch: {},
18595 // scratch object for core
18596 layout: null,
18597 renderer: null,
18598 destroyed: false,
18599 // whether destroy was called
18600 notificationsEnabled: true,
18601 // whether notifications are sent to the renderer
18602 minZoom: 1e-50,
18603 maxZoom: 1e50,
18604 zoomingEnabled: defVal(true, options.zoomingEnabled),
18605 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18606 panningEnabled: defVal(true, options.panningEnabled),
18607 userPanningEnabled: defVal(true, options.userPanningEnabled),
18608 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18609 autolock: defVal(false, options.autolock, options.autolockNodes),
18610 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18611 autounselectify: defVal(false, options.autounselectify),
18612 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18613 zoom: number(options.zoom) ? options.zoom : 1,
18614 pan: {
18615 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18616 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18617 },
18618 animation: {
18619 // object for currently-running animations
18620 current: [],
18621 queue: []
18622 },
18623 hasCompoundNodes: false,
18624 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18625 };
18626
18627 this.createEmitter(); // set selection type
18628
18629 this.selectionType(options.selectionType); // init zoom bounds
18630
18631 this.zoomRange({
18632 min: options.minZoom,
18633 max: options.maxZoom
18634 });
18635
18636 var loadExtData = function loadExtData(extData, next) {
18637 var anyIsPromise = extData.some(promise);
18638
18639 if (anyIsPromise) {
18640 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18641 } else {
18642 next(extData); // exec synchronously for convenience
18643 }
18644 }; // start with the default stylesheet so we have something before loading an external stylesheet
18645
18646
18647 if (_p.styleEnabled) {
18648 cy.setStyle([]);
18649 } // create the renderer
18650
18651
18652 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18653
18654 cy.initRenderer(rendererOptions);
18655
18656 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18657 cy.notifications(false); // remove old elements
18658
18659 var oldEles = cy.mutableElements();
18660
18661 if (oldEles.length > 0) {
18662 oldEles.remove();
18663 }
18664
18665 if (elements != null) {
18666 if (plainObject(elements) || array(elements)) {
18667 cy.add(elements);
18668 }
18669 }
18670
18671 cy.one('layoutready', function (e) {
18672 cy.notifications(true);
18673 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18674
18675 cy.one('load', onload);
18676 cy.emitAndNotify('load');
18677 }).one('layoutstop', function () {
18678 cy.one('done', ondone);
18679 cy.emit('done');
18680 });
18681 var layoutOpts = extend({}, cy._private.options.layout);
18682 layoutOpts.eles = cy.elements();
18683 cy.layout(layoutOpts).run();
18684 };
18685
18686 loadExtData([options.style, options.elements], function (thens) {
18687 var initStyle = thens[0];
18688 var initEles = thens[1]; // init style
18689
18690 if (_p.styleEnabled) {
18691 cy.style().append(initStyle);
18692 } // initial load
18693
18694
18695 setElesAndLayout(initEles, function () {
18696 // onready
18697 cy.startAnimationLoop();
18698 _p.ready = true; // if a ready callback is specified as an option, the bind it
18699
18700 if (fn(options.ready)) {
18701 cy.on('ready', options.ready);
18702 } // bind all the ready handlers registered before creating this instance
18703
18704
18705 for (var i = 0; i < readies.length; i++) {
18706 var fn$1 = readies[i];
18707 cy.on('ready', fn$1);
18708 }
18709
18710 if (reg) {
18711 reg.readies = [];
18712 } // 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
18713
18714
18715 cy.emit('ready');
18716 }, options.done);
18717 });
18718};
18719
18720var corefn$9 = Core.prototype; // short alias
18721
18722extend(corefn$9, {
18723 instanceString: function instanceString() {
18724 return 'core';
18725 },
18726 isReady: function isReady() {
18727 return this._private.ready;
18728 },
18729 destroyed: function destroyed() {
18730 return this._private.destroyed;
18731 },
18732 ready: function ready(fn) {
18733 if (this.isReady()) {
18734 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18735 } else {
18736 this.on('ready', fn);
18737 }
18738
18739 return this;
18740 },
18741 destroy: function destroy() {
18742 var cy = this;
18743 if (cy.destroyed()) return;
18744 cy.stopAnimationLoop();
18745 cy.destroyRenderer();
18746 this.emit('destroy');
18747 cy._private.destroyed = true;
18748 return cy;
18749 },
18750 hasElementWithId: function hasElementWithId(id) {
18751 return this._private.elements.hasElementWithId(id);
18752 },
18753 getElementById: function getElementById(id) {
18754 return this._private.elements.getElementById(id);
18755 },
18756 hasCompoundNodes: function hasCompoundNodes() {
18757 return this._private.hasCompoundNodes;
18758 },
18759 headless: function headless() {
18760 return this._private.renderer.isHeadless();
18761 },
18762 styleEnabled: function styleEnabled() {
18763 return this._private.styleEnabled;
18764 },
18765 addToPool: function addToPool(eles) {
18766 this._private.elements.merge(eles);
18767
18768 return this; // chaining
18769 },
18770 removeFromPool: function removeFromPool(eles) {
18771 this._private.elements.unmerge(eles);
18772
18773 return this;
18774 },
18775 container: function container() {
18776 return this._private.container || null;
18777 },
18778 mount: function mount(container) {
18779 if (container == null) {
18780 return;
18781 }
18782
18783 var cy = this;
18784 var _p = cy._private;
18785 var options = _p.options;
18786
18787 if (!htmlElement(container) && htmlElement(container[0])) {
18788 container = container[0];
18789 }
18790
18791 cy.stopAnimationLoop();
18792 cy.destroyRenderer();
18793 _p.container = container;
18794 _p.styleEnabled = true;
18795 cy.invalidateSize();
18796 cy.initRenderer(extend({}, options, options.renderer, {
18797 // allow custom renderer name to be re-used, otherwise use canvas
18798 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18799 }));
18800 cy.startAnimationLoop();
18801 cy.style(options.style);
18802 cy.emit('mount');
18803 return cy;
18804 },
18805 unmount: function unmount() {
18806 var cy = this;
18807 cy.stopAnimationLoop();
18808 cy.destroyRenderer();
18809 cy.initRenderer({
18810 name: 'null'
18811 });
18812 cy.emit('unmount');
18813 return cy;
18814 },
18815 options: function options() {
18816 return copy(this._private.options);
18817 },
18818 json: function json(obj) {
18819 var cy = this;
18820 var _p = cy._private;
18821 var eles = cy.mutableElements();
18822
18823 var getFreshRef = function getFreshRef(ele) {
18824 return cy.getElementById(ele.id());
18825 };
18826
18827 if (plainObject(obj)) {
18828 // set
18829 cy.startBatch();
18830
18831 if (obj.elements) {
18832 var idInJson = {};
18833
18834 var updateEles = function updateEles(jsons, gr) {
18835 var toAdd = [];
18836 var toMod = [];
18837
18838 for (var i = 0; i < jsons.length; i++) {
18839 var json = jsons[i];
18840
18841 if (!json.data.id) {
18842 warn('cy.json() cannot handle elements without an ID attribute');
18843 continue;
18844 }
18845
18846 var id = '' + json.data.id; // id must be string
18847
18848 var ele = cy.getElementById(id);
18849 idInJson[id] = true;
18850
18851 if (ele.length !== 0) {
18852 // existing element should be updated
18853 toMod.push({
18854 ele: ele,
18855 json: json
18856 });
18857 } else {
18858 // otherwise should be added
18859 if (gr) {
18860 json.group = gr;
18861 toAdd.push(json);
18862 } else {
18863 toAdd.push(json);
18864 }
18865 }
18866 }
18867
18868 cy.add(toAdd);
18869
18870 for (var _i = 0; _i < toMod.length; _i++) {
18871 var _toMod$_i = toMod[_i],
18872 _ele = _toMod$_i.ele,
18873 _json = _toMod$_i.json;
18874
18875 _ele.json(_json);
18876 }
18877 };
18878
18879 if (array(obj.elements)) {
18880 // elements: []
18881 updateEles(obj.elements);
18882 } else {
18883 // elements: { nodes: [], edges: [] }
18884 var grs = ['nodes', 'edges'];
18885
18886 for (var i = 0; i < grs.length; i++) {
18887 var gr = grs[i];
18888 var elements = obj.elements[gr];
18889
18890 if (array(elements)) {
18891 updateEles(elements, gr);
18892 }
18893 }
18894 }
18895
18896 var parentsToRemove = cy.collection();
18897 eles.filter(function (ele) {
18898 return !idInJson[ele.id()];
18899 }).forEach(function (ele) {
18900 if (ele.isParent()) {
18901 parentsToRemove.merge(ele);
18902 } else {
18903 ele.remove();
18904 }
18905 }); // so that children are not removed w/parent
18906
18907 parentsToRemove.forEach(function (ele) {
18908 return ele.children().move({
18909 parent: null
18910 });
18911 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18912
18913 parentsToRemove.forEach(function (ele) {
18914 return getFreshRef(ele).remove();
18915 });
18916 }
18917
18918 if (obj.style) {
18919 cy.style(obj.style);
18920 }
18921
18922 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18923 cy.zoom(obj.zoom);
18924 }
18925
18926 if (obj.pan) {
18927 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18928 cy.pan(obj.pan);
18929 }
18930 }
18931
18932 if (obj.data) {
18933 cy.data(obj.data);
18934 }
18935
18936 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
18937
18938 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18939 var f = fields[_i2];
18940
18941 if (obj[f] != null) {
18942 cy[f](obj[f]);
18943 }
18944 }
18945
18946 cy.endBatch();
18947 return this; // chaining
18948 } else {
18949 // get
18950 var flat = !!obj;
18951 var json = {};
18952
18953 if (flat) {
18954 json.elements = this.elements().map(function (ele) {
18955 return ele.json();
18956 });
18957 } else {
18958 json.elements = {};
18959 eles.forEach(function (ele) {
18960 var group = ele.group();
18961
18962 if (!json.elements[group]) {
18963 json.elements[group] = [];
18964 }
18965
18966 json.elements[group].push(ele.json());
18967 });
18968 }
18969
18970 if (this._private.styleEnabled) {
18971 json.style = cy.style().json();
18972 }
18973
18974 json.data = copy(cy.data());
18975 var options = _p.options;
18976 json.zoomingEnabled = _p.zoomingEnabled;
18977 json.userZoomingEnabled = _p.userZoomingEnabled;
18978 json.zoom = _p.zoom;
18979 json.minZoom = _p.minZoom;
18980 json.maxZoom = _p.maxZoom;
18981 json.panningEnabled = _p.panningEnabled;
18982 json.userPanningEnabled = _p.userPanningEnabled;
18983 json.pan = copy(_p.pan);
18984 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18985 json.renderer = copy(options.renderer);
18986 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18987 json.textureOnViewport = options.textureOnViewport;
18988 json.wheelSensitivity = options.wheelSensitivity;
18989 json.motionBlur = options.motionBlur;
18990 json.multiClickDebounceTime = options.multiClickDebounceTime;
18991 return json;
18992 }
18993 }
18994});
18995corefn$9.$id = corefn$9.getElementById;
18996[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18997 extend(corefn$9, props);
18998});
18999
19000/* eslint-disable no-unused-vars */
19001
19002var defaults$9 = {
19003 fit: true,
19004 // whether to fit the viewport to the graph
19005 directed: false,
19006 // whether the tree is directed downwards (or edges can point in any direction if false)
19007 padding: 30,
19008 // padding on fit
19009 circle: false,
19010 // put depths in concentric circles if true, put depths top down if false
19011 grid: false,
19012 // whether to create an even grid into which the DAG is placed (circle:false only)
19013 spacingFactor: 1.75,
19014 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19015 boundingBox: undefined,
19016 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19017 avoidOverlap: true,
19018 // prevents node overlap, may overflow boundingBox if not enough space
19019 nodeDimensionsIncludeLabels: false,
19020 // Excludes the label when calculating node bounding boxes for the layout algorithm
19021 roots: undefined,
19022 // the roots of the trees
19023 maximal: false,
19024 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19025 depthSort: undefined,
19026 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19027 animate: false,
19028 // whether to transition the node positions
19029 animationDuration: 500,
19030 // duration of animation in ms if enabled
19031 animationEasing: undefined,
19032 // easing of animation if enabled,
19033 animateFilter: function animateFilter(node, i) {
19034 return true;
19035 },
19036 // 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
19037 ready: undefined,
19038 // callback on layoutready
19039 stop: undefined,
19040 // callback on layoutstop
19041 transform: function transform(node, position) {
19042 return position;
19043 } // transform a given node position. Useful for changing flow direction in discrete layouts
19044
19045};
19046/* eslint-enable */
19047
19048var getInfo = function getInfo(ele) {
19049 return ele.scratch('breadthfirst');
19050};
19051
19052var setInfo = function setInfo(ele, obj) {
19053 return ele.scratch('breadthfirst', obj);
19054};
19055
19056function BreadthFirstLayout(options) {
19057 this.options = extend({}, defaults$9, options);
19058}
19059
19060BreadthFirstLayout.prototype.run = function () {
19061 var params = this.options;
19062 var options = params;
19063 var cy = params.cy;
19064 var eles = options.eles;
19065 var nodes = eles.nodes().filter(function (n) {
19066 return !n.isParent();
19067 });
19068 var graph = eles;
19069 var directed = options.directed;
19070 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19071
19072 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19073 x1: 0,
19074 y1: 0,
19075 w: cy.width(),
19076 h: cy.height()
19077 });
19078 var roots;
19079
19080 if (elementOrCollection(options.roots)) {
19081 roots = options.roots;
19082 } else if (array(options.roots)) {
19083 var rootsArray = [];
19084
19085 for (var i = 0; i < options.roots.length; i++) {
19086 var id = options.roots[i];
19087 var ele = cy.getElementById(id);
19088 rootsArray.push(ele);
19089 }
19090
19091 roots = cy.collection(rootsArray);
19092 } else if (string(options.roots)) {
19093 roots = cy.$(options.roots);
19094 } else {
19095 if (directed) {
19096 roots = nodes.roots();
19097 } else {
19098 var components = eles.components();
19099 roots = cy.collection();
19100
19101 var _loop = function _loop(_i) {
19102 var comp = components[_i];
19103 var maxDegree = comp.maxDegree(false);
19104 var compRoots = comp.filter(function (ele) {
19105 return ele.degree(false) === maxDegree;
19106 });
19107 roots = roots.add(compRoots);
19108 };
19109
19110 for (var _i = 0; _i < components.length; _i++) {
19111 _loop(_i);
19112 }
19113 }
19114 }
19115
19116 var depths = [];
19117 var foundByBfs = {};
19118
19119 var addToDepth = function addToDepth(ele, d) {
19120 if (depths[d] == null) {
19121 depths[d] = [];
19122 }
19123
19124 var i = depths[d].length;
19125 depths[d].push(ele);
19126 setInfo(ele, {
19127 index: i,
19128 depth: d
19129 });
19130 };
19131
19132 var changeDepth = function changeDepth(ele, newDepth) {
19133 var _getInfo = getInfo(ele),
19134 depth = _getInfo.depth,
19135 index = _getInfo.index;
19136
19137 depths[depth][index] = null;
19138 addToDepth(ele, newDepth);
19139 }; // find the depths of the nodes
19140
19141
19142 graph.bfs({
19143 roots: roots,
19144 directed: options.directed,
19145 visit: function visit(node, edge, pNode, i, depth) {
19146 var ele = node[0];
19147 var id = ele.id();
19148 addToDepth(ele, depth);
19149 foundByBfs[id] = true;
19150 }
19151 }); // check for nodes not found by bfs
19152
19153 var orphanNodes = [];
19154
19155 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19156 var _ele = nodes[_i2];
19157
19158 if (foundByBfs[_ele.id()]) {
19159 continue;
19160 } else {
19161 orphanNodes.push(_ele);
19162 }
19163 } // assign the nodes a depth and index
19164
19165
19166 var assignDepthsAt = function assignDepthsAt(i) {
19167 var eles = depths[i];
19168
19169 for (var j = 0; j < eles.length; j++) {
19170 var _ele2 = eles[j];
19171
19172 if (_ele2 == null) {
19173 eles.splice(j, 1);
19174 j--;
19175 continue;
19176 }
19177
19178 setInfo(_ele2, {
19179 depth: i,
19180 index: j
19181 });
19182 }
19183 };
19184
19185 var assignDepths = function assignDepths() {
19186 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19187 assignDepthsAt(_i3);
19188 }
19189 };
19190
19191 var adjustMaximally = function adjustMaximally(ele, shifted) {
19192 var eInfo = getInfo(ele);
19193 var incomers = ele.incomers().filter(function (el) {
19194 return el.isNode() && eles.has(el);
19195 });
19196 var maxDepth = -1;
19197 var id = ele.id();
19198
19199 for (var k = 0; k < incomers.length; k++) {
19200 var incmr = incomers[k];
19201 var iInfo = getInfo(incmr);
19202 maxDepth = Math.max(maxDepth, iInfo.depth);
19203 }
19204
19205 if (eInfo.depth <= maxDepth) {
19206 if (shifted[id]) {
19207 return null;
19208 }
19209
19210 changeDepth(ele, maxDepth + 1);
19211 shifted[id] = true;
19212 return true;
19213 }
19214
19215 return false;
19216 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19217
19218
19219 if (directed && maximal) {
19220 var Q = [];
19221 var shifted = {};
19222
19223 var enqueue = function enqueue(n) {
19224 return Q.push(n);
19225 };
19226
19227 var dequeue = function dequeue() {
19228 return Q.shift();
19229 };
19230
19231 nodes.forEach(function (n) {
19232 return Q.push(n);
19233 });
19234
19235 while (Q.length > 0) {
19236 var _ele3 = dequeue();
19237
19238 var didShift = adjustMaximally(_ele3, shifted);
19239
19240 if (didShift) {
19241 _ele3.outgoers().filter(function (el) {
19242 return el.isNode() && eles.has(el);
19243 }).forEach(enqueue);
19244 } else if (didShift === null) {
19245 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19246 break; // exit on failure
19247 }
19248 }
19249 }
19250
19251 assignDepths(); // clear holes
19252 // find min distance we need to leave between nodes
19253
19254 var minDistance = 0;
19255
19256 if (options.avoidOverlap) {
19257 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19258 var n = nodes[_i4];
19259 var nbb = n.layoutDimensions(options);
19260 var w = nbb.w;
19261 var h = nbb.h;
19262 minDistance = Math.max(minDistance, w, h);
19263 }
19264 } // get the weighted percent for an element based on its connectivity to other levels
19265
19266
19267 var cachedWeightedPercent = {};
19268
19269 var getWeightedPercent = function getWeightedPercent(ele) {
19270 if (cachedWeightedPercent[ele.id()]) {
19271 return cachedWeightedPercent[ele.id()];
19272 }
19273
19274 var eleDepth = getInfo(ele).depth;
19275 var neighbors = ele.neighborhood();
19276 var percent = 0;
19277 var samples = 0;
19278
19279 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19280 var neighbor = neighbors[_i5];
19281
19282 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19283 continue;
19284 }
19285
19286 var bf = getInfo(neighbor);
19287
19288 if (bf == null) {
19289 continue;
19290 }
19291
19292 var index = bf.index;
19293 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19294
19295 if (index == null || depth == null) {
19296 continue;
19297 }
19298
19299 var nDepth = depths[depth].length;
19300
19301 if (depth < eleDepth) {
19302 // only get influenced by elements above
19303 percent += index / nDepth;
19304 samples++;
19305 }
19306 }
19307
19308 samples = Math.max(1, samples);
19309 percent = percent / samples;
19310
19311 if (samples === 0) {
19312 // put lone nodes at the start
19313 percent = 0;
19314 }
19315
19316 cachedWeightedPercent[ele.id()] = percent;
19317 return percent;
19318 }; // rearrange the indices in each depth level based on connectivity
19319
19320
19321 var sortFn = function sortFn(a, b) {
19322 var apct = getWeightedPercent(a);
19323 var bpct = getWeightedPercent(b);
19324 var diff = apct - bpct;
19325
19326 if (diff === 0) {
19327 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19328 } else {
19329 return diff;
19330 }
19331 };
19332
19333 if (options.depthSort !== undefined) {
19334 sortFn = options.depthSort;
19335 } // sort each level to make connected nodes closer
19336
19337
19338 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19339 depths[_i6].sort(sortFn);
19340
19341 assignDepthsAt(_i6);
19342 } // assign orphan nodes to a new top-level depth
19343
19344
19345 var orphanDepth = [];
19346
19347 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19348 orphanDepth.push(orphanNodes[_i7]);
19349 }
19350
19351 depths.unshift(orphanDepth);
19352 assignDepths();
19353 var biggestDepthSize = 0;
19354
19355 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19356 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19357 }
19358
19359 var center = {
19360 x: bb.x1 + bb.w / 2,
19361 y: bb.x1 + bb.h / 2
19362 };
19363 var maxDepthSize = depths.reduce(function (max, eles) {
19364 return Math.max(max, eles.length);
19365 }, 0);
19366
19367 var getPosition = function getPosition(ele) {
19368 var _getInfo2 = getInfo(ele),
19369 depth = _getInfo2.depth,
19370 index = _getInfo2.index;
19371
19372 var depthSize = depths[depth].length;
19373 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19374 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19375 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19376 radiusStepSize = Math.max(radiusStepSize, minDistance);
19377
19378 if (!options.circle) {
19379 var epos = {
19380 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19381 y: (depth + 1) * distanceY
19382 };
19383 return epos;
19384 } else {
19385 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19386 var theta = 2 * Math.PI / depths[depth].length * index;
19387
19388 if (depth === 0 && depths[0].length === 1) {
19389 radius = 1;
19390 }
19391
19392 return {
19393 x: center.x + radius * Math.cos(theta),
19394 y: center.y + radius * Math.sin(theta)
19395 };
19396 }
19397 };
19398
19399 eles.nodes().layoutPositions(this, options, getPosition);
19400 return this; // chaining
19401};
19402
19403var defaults$a = {
19404 fit: true,
19405 // whether to fit the viewport to the graph
19406 padding: 30,
19407 // the padding on fit
19408 boundingBox: undefined,
19409 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19410 avoidOverlap: true,
19411 // prevents node overlap, may overflow boundingBox and radius if not enough space
19412 nodeDimensionsIncludeLabels: false,
19413 // Excludes the label when calculating node bounding boxes for the layout algorithm
19414 spacingFactor: undefined,
19415 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19416 radius: undefined,
19417 // the radius of the circle
19418 startAngle: 3 / 2 * Math.PI,
19419 // where nodes start in radians
19420 sweep: undefined,
19421 // how many radians should be between the first and last node (defaults to full circle)
19422 clockwise: true,
19423 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19424 sort: undefined,
19425 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19426 animate: false,
19427 // whether to transition the node positions
19428 animationDuration: 500,
19429 // duration of animation in ms if enabled
19430 animationEasing: undefined,
19431 // easing of animation if enabled
19432 animateFilter: function animateFilter(node, i) {
19433 return true;
19434 },
19435 // 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
19436 ready: undefined,
19437 // callback on layoutready
19438 stop: undefined,
19439 // callback on layoutstop
19440 transform: function transform(node, position) {
19441 return position;
19442 } // transform a given node position. Useful for changing flow direction in discrete layouts
19443
19444};
19445
19446function CircleLayout(options) {
19447 this.options = extend({}, defaults$a, options);
19448}
19449
19450CircleLayout.prototype.run = function () {
19451 var params = this.options;
19452 var options = params;
19453 var cy = params.cy;
19454 var eles = options.eles;
19455 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19456 var nodes = eles.nodes().not(':parent');
19457
19458 if (options.sort) {
19459 nodes = nodes.sort(options.sort);
19460 }
19461
19462 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19463 x1: 0,
19464 y1: 0,
19465 w: cy.width(),
19466 h: cy.height()
19467 });
19468 var center = {
19469 x: bb.x1 + bb.w / 2,
19470 y: bb.y1 + bb.h / 2
19471 };
19472 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19473 var dTheta = sweep / Math.max(1, nodes.length - 1);
19474 var r;
19475 var minDistance = 0;
19476
19477 for (var i = 0; i < nodes.length; i++) {
19478 var n = nodes[i];
19479 var nbb = n.layoutDimensions(options);
19480 var w = nbb.w;
19481 var h = nbb.h;
19482 minDistance = Math.max(minDistance, w, h);
19483 }
19484
19485 if (number(options.radius)) {
19486 r = options.radius;
19487 } else if (nodes.length <= 1) {
19488 r = 0;
19489 } else {
19490 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19491 } // calculate the radius
19492
19493
19494 if (nodes.length > 1 && options.avoidOverlap) {
19495 // but only if more than one node (can't overlap)
19496 minDistance *= 1.75; // just to have some nice spacing
19497
19498 var dcos = Math.cos(dTheta) - Math.cos(0);
19499 var dsin = Math.sin(dTheta) - Math.sin(0);
19500 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19501
19502 r = Math.max(rMin, r);
19503 }
19504
19505 var getPos = function getPos(ele, i) {
19506 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19507 var rx = r * Math.cos(theta);
19508 var ry = r * Math.sin(theta);
19509 var pos = {
19510 x: center.x + rx,
19511 y: center.y + ry
19512 };
19513 return pos;
19514 };
19515
19516 eles.nodes().layoutPositions(this, options, getPos);
19517 return this; // chaining
19518};
19519
19520var defaults$b = {
19521 fit: true,
19522 // whether to fit the viewport to the graph
19523 padding: 30,
19524 // the padding on fit
19525 startAngle: 3 / 2 * Math.PI,
19526 // where nodes start in radians
19527 sweep: undefined,
19528 // how many radians should be between the first and last node (defaults to full circle)
19529 clockwise: true,
19530 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19531 equidistant: false,
19532 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19533 minNodeSpacing: 10,
19534 // min spacing between outside of nodes (used for radius adjustment)
19535 boundingBox: undefined,
19536 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19537 avoidOverlap: true,
19538 // prevents node overlap, may overflow boundingBox if not enough space
19539 nodeDimensionsIncludeLabels: false,
19540 // Excludes the label when calculating node bounding boxes for the layout algorithm
19541 height: undefined,
19542 // height of layout area (overrides container height)
19543 width: undefined,
19544 // width of layout area (overrides container width)
19545 spacingFactor: undefined,
19546 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19547 concentric: function concentric(node) {
19548 // returns numeric value for each node, placing higher nodes in levels towards the centre
19549 return node.degree();
19550 },
19551 levelWidth: function levelWidth(nodes) {
19552 // the variation of concentric values in each level
19553 return nodes.maxDegree() / 4;
19554 },
19555 animate: false,
19556 // whether to transition the node positions
19557 animationDuration: 500,
19558 // duration of animation in ms if enabled
19559 animationEasing: undefined,
19560 // easing of animation if enabled
19561 animateFilter: function animateFilter(node, i) {
19562 return true;
19563 },
19564 // 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
19565 ready: undefined,
19566 // callback on layoutready
19567 stop: undefined,
19568 // callback on layoutstop
19569 transform: function transform(node, position) {
19570 return position;
19571 } // transform a given node position. Useful for changing flow direction in discrete layouts
19572
19573};
19574
19575function ConcentricLayout(options) {
19576 this.options = extend({}, defaults$b, options);
19577}
19578
19579ConcentricLayout.prototype.run = function () {
19580 var params = this.options;
19581 var options = params;
19582 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19583 var cy = params.cy;
19584 var eles = options.eles;
19585 var nodes = eles.nodes().not(':parent');
19586 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19587 x1: 0,
19588 y1: 0,
19589 w: cy.width(),
19590 h: cy.height()
19591 });
19592 var center = {
19593 x: bb.x1 + bb.w / 2,
19594 y: bb.y1 + bb.h / 2
19595 };
19596 var nodeValues = []; // { node, value }
19597
19598 var maxNodeSize = 0;
19599
19600 for (var i = 0; i < nodes.length; i++) {
19601 var node = nodes[i];
19602 var value = void 0; // calculate the node value
19603
19604 value = options.concentric(node);
19605 nodeValues.push({
19606 value: value,
19607 node: node
19608 }); // for style mapping
19609
19610 node._private.scratch.concentric = value;
19611 } // in case we used the `concentric` in style
19612
19613
19614 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19615
19616 for (var _i = 0; _i < nodes.length; _i++) {
19617 var _node = nodes[_i];
19618
19619 var nbb = _node.layoutDimensions(options);
19620
19621 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19622 } // sort node values in descreasing order
19623
19624
19625 nodeValues.sort(function (a, b) {
19626 return b.value - a.value;
19627 });
19628 var levelWidth = options.levelWidth(nodes); // put the values into levels
19629
19630 var levels = [[]];
19631 var currentLevel = levels[0];
19632
19633 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19634 var val = nodeValues[_i2];
19635
19636 if (currentLevel.length > 0) {
19637 var diff = Math.abs(currentLevel[0].value - val.value);
19638
19639 if (diff >= levelWidth) {
19640 currentLevel = [];
19641 levels.push(currentLevel);
19642 }
19643 }
19644
19645 currentLevel.push(val);
19646 } // create positions from levels
19647
19648
19649 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19650
19651 if (!options.avoidOverlap) {
19652 // then strictly constrain to bb
19653 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19654 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19655 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19656 minDist = Math.min(minDist, rStep);
19657 } // find the metrics for each level
19658
19659
19660 var r = 0;
19661
19662 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19663 var level = levels[_i3];
19664 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19665 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19666
19667 if (level.length > 1 && options.avoidOverlap) {
19668 // but only if more than one node (can't overlap)
19669 var dcos = Math.cos(dTheta) - Math.cos(0);
19670 var dsin = Math.sin(dTheta) - Math.sin(0);
19671 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19672
19673 r = Math.max(rMin, r);
19674 }
19675
19676 level.r = r;
19677 r += minDist;
19678 }
19679
19680 if (options.equidistant) {
19681 var rDeltaMax = 0;
19682 var _r = 0;
19683
19684 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19685 var _level = levels[_i4];
19686 var rDelta = _level.r - _r;
19687 rDeltaMax = Math.max(rDeltaMax, rDelta);
19688 }
19689
19690 _r = 0;
19691
19692 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19693 var _level2 = levels[_i5];
19694
19695 if (_i5 === 0) {
19696 _r = _level2.r;
19697 }
19698
19699 _level2.r = _r;
19700 _r += rDeltaMax;
19701 }
19702 } // calculate the node positions
19703
19704
19705 var pos = {}; // id => position
19706
19707 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19708 var _level3 = levels[_i6];
19709 var _dTheta = _level3.dTheta;
19710 var _r2 = _level3.r;
19711
19712 for (var j = 0; j < _level3.length; j++) {
19713 var _val = _level3[j];
19714 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19715 var p = {
19716 x: center.x + _r2 * Math.cos(theta),
19717 y: center.y + _r2 * Math.sin(theta)
19718 };
19719 pos[_val.node.id()] = p;
19720 }
19721 } // position the nodes
19722
19723
19724 eles.nodes().layoutPositions(this, options, function (ele) {
19725 var id = ele.id();
19726 return pos[id];
19727 });
19728 return this; // chaining
19729};
19730
19731/*
19732The CoSE layout was written by Gerardo Huck.
19733https://www.linkedin.com/in/gerardohuck/
19734
19735Based on the following article:
19736http://dl.acm.org/citation.cfm?id=1498047
19737
19738Modifications tracked on Github.
19739*/
19740var DEBUG;
19741/**
19742 * @brief : default layout options
19743 */
19744
19745var defaults$c = {
19746 // Called on `layoutready`
19747 ready: function ready() {},
19748 // Called on `layoutstop`
19749 stop: function stop() {},
19750 // Whether to animate while running the layout
19751 // true : Animate continuously as the layout is running
19752 // false : Just show the end result
19753 // 'end' : Animate with the end result, from the initial positions to the end positions
19754 animate: true,
19755 // Easing of the animation for animate:'end'
19756 animationEasing: undefined,
19757 // The duration of the animation for animate:'end'
19758 animationDuration: undefined,
19759 // A function that determines whether the node should be animated
19760 // All nodes animated by default on animate enabled
19761 // Non-animated nodes are positioned immediately when the layout starts
19762 animateFilter: function animateFilter(node, i) {
19763 return true;
19764 },
19765 // The layout animates only after this many milliseconds for animate:true
19766 // (prevents flashing on fast runs)
19767 animationThreshold: 250,
19768 // Number of iterations between consecutive screen positions update
19769 refresh: 20,
19770 // Whether to fit the network view after when done
19771 fit: true,
19772 // Padding on fit
19773 padding: 30,
19774 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19775 boundingBox: undefined,
19776 // Excludes the label when calculating node bounding boxes for the layout algorithm
19777 nodeDimensionsIncludeLabels: false,
19778 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19779 randomize: false,
19780 // Extra spacing between components in non-compound graphs
19781 componentSpacing: 40,
19782 // Node repulsion (non overlapping) multiplier
19783 nodeRepulsion: function nodeRepulsion(node) {
19784 return 2048;
19785 },
19786 // Node repulsion (overlapping) multiplier
19787 nodeOverlap: 4,
19788 // Ideal edge (non nested) length
19789 idealEdgeLength: function idealEdgeLength(edge) {
19790 return 32;
19791 },
19792 // Divisor to compute edge forces
19793 edgeElasticity: function edgeElasticity(edge) {
19794 return 32;
19795 },
19796 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19797 nestingFactor: 1.2,
19798 // Gravity force (constant)
19799 gravity: 1,
19800 // Maximum number of iterations to perform
19801 numIter: 1000,
19802 // Initial temperature (maximum node displacement)
19803 initialTemp: 1000,
19804 // Cooling factor (how the temperature is reduced between consecutive iterations
19805 coolingFactor: 0.99,
19806 // Lower temperature threshold (below this point the layout will end)
19807 minTemp: 1.0
19808};
19809/**
19810 * @brief : constructor
19811 * @arg options : object containing layout options
19812 */
19813
19814function CoseLayout(options) {
19815 this.options = extend({}, defaults$c, options);
19816 this.options.layout = this;
19817}
19818/**
19819 * @brief : runs the layout
19820 */
19821
19822
19823CoseLayout.prototype.run = function () {
19824 var options = this.options;
19825 var cy = options.cy;
19826 var layout = this;
19827 layout.stopped = false;
19828
19829 if (options.animate === true || options.animate === false) {
19830 layout.emit({
19831 type: 'layoutstart',
19832 layout: layout
19833 });
19834 } // Set DEBUG - Global variable
19835
19836
19837 if (true === options.debug) {
19838 DEBUG = true;
19839 } else {
19840 DEBUG = false;
19841 } // Initialize layout info
19842
19843
19844 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19845
19846 if (DEBUG) {
19847 printLayoutInfo(layoutInfo);
19848 } // If required, randomize node positions
19849
19850
19851 if (options.randomize) {
19852 randomizePositions(layoutInfo);
19853 }
19854
19855 var startTime = performanceNow();
19856
19857 var refresh = function refresh() {
19858 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19859
19860 if (true === options.fit) {
19861 cy.fit(options.padding);
19862 }
19863 };
19864
19865 var mainLoop = function mainLoop(i) {
19866 if (layout.stopped || i >= options.numIter) {
19867 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19868 return false;
19869 } // Do one step in the phisical simulation
19870
19871
19872 step$1(layoutInfo, options); // Update temperature
19873
19874 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19875
19876 if (layoutInfo.temperature < options.minTemp) {
19877 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19878 return false;
19879 }
19880
19881 return true;
19882 };
19883
19884 var done = function done() {
19885 if (options.animate === true || options.animate === false) {
19886 refresh(); // Layout has finished
19887
19888 layout.one('layoutstop', options.stop);
19889 layout.emit({
19890 type: 'layoutstop',
19891 layout: layout
19892 });
19893 } else {
19894 var nodes = options.eles.nodes();
19895 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19896 nodes.layoutPositions(layout, options, getScaledPos);
19897 }
19898 };
19899
19900 var i = 0;
19901 var loopRet = true;
19902
19903 if (options.animate === true) {
19904 var frame = function frame() {
19905 var f = 0;
19906
19907 while (loopRet && f < options.refresh) {
19908 loopRet = mainLoop(i);
19909 i++;
19910 f++;
19911 }
19912
19913 if (!loopRet) {
19914 // it's done
19915 separateComponents(layoutInfo, options);
19916 done();
19917 } else {
19918 var now = performanceNow();
19919
19920 if (now - startTime >= options.animationThreshold) {
19921 refresh();
19922 }
19923
19924 requestAnimationFrame(frame);
19925 }
19926 };
19927
19928 frame();
19929 } else {
19930 while (loopRet) {
19931 loopRet = mainLoop(i);
19932 i++;
19933 }
19934
19935 separateComponents(layoutInfo, options);
19936 done();
19937 }
19938
19939 return this; // chaining
19940};
19941/**
19942 * @brief : called on continuous layouts to stop them before they finish
19943 */
19944
19945
19946CoseLayout.prototype.stop = function () {
19947 this.stopped = true;
19948
19949 if (this.thread) {
19950 this.thread.stop();
19951 }
19952
19953 this.emit('layoutstop');
19954 return this; // chaining
19955};
19956
19957CoseLayout.prototype.destroy = function () {
19958 if (this.thread) {
19959 this.thread.stop();
19960 }
19961
19962 return this; // chaining
19963};
19964/**
19965 * @brief : Creates an object which is contains all the data
19966 * used in the layout process
19967 * @arg cy : cytoscape.js object
19968 * @return : layoutInfo object initialized
19969 */
19970
19971
19972var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19973 // Shortcut
19974 var edges = options.eles.edges();
19975 var nodes = options.eles.nodes();
19976 var layoutInfo = {
19977 isCompound: cy.hasCompoundNodes(),
19978 layoutNodes: [],
19979 idToIndex: {},
19980 nodeSize: nodes.size(),
19981 graphSet: [],
19982 indexToGraph: [],
19983 layoutEdges: [],
19984 edgeSize: edges.size(),
19985 temperature: options.initialTemp,
19986 clientWidth: cy.width(),
19987 clientHeight: cy.width(),
19988 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19989 x1: 0,
19990 y1: 0,
19991 w: cy.width(),
19992 h: cy.height()
19993 })
19994 };
19995 var components = options.eles.components();
19996 var id2cmptId = {};
19997
19998 for (var i = 0; i < components.length; i++) {
19999 var component = components[i];
20000
20001 for (var j = 0; j < component.length; j++) {
20002 var node = component[j];
20003 id2cmptId[node.id()] = i;
20004 }
20005 } // Iterate over all nodes, creating layout nodes
20006
20007
20008 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20009 var n = nodes[i];
20010 var nbb = n.layoutDimensions(options);
20011 var tempNode = {};
20012 tempNode.isLocked = n.locked();
20013 tempNode.id = n.data('id');
20014 tempNode.parentId = n.data('parent');
20015 tempNode.cmptId = id2cmptId[n.id()];
20016 tempNode.children = [];
20017 tempNode.positionX = n.position('x');
20018 tempNode.positionY = n.position('y');
20019 tempNode.offsetX = 0;
20020 tempNode.offsetY = 0;
20021 tempNode.height = nbb.w;
20022 tempNode.width = nbb.h;
20023 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20024 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20025 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20026 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20027 tempNode.padLeft = parseFloat(n.style('padding'));
20028 tempNode.padRight = parseFloat(n.style('padding'));
20029 tempNode.padTop = parseFloat(n.style('padding'));
20030 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20031
20032 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20033
20034 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20035
20036 layoutInfo.idToIndex[tempNode.id] = i;
20037 } // Inline implementation of a queue, used for traversing the graph in BFS order
20038
20039
20040 var queue = [];
20041 var start = 0; // Points to the start the queue
20042
20043 var end = -1; // Points to the end of the queue
20044
20045 var tempGraph = []; // Second pass to add child information and
20046 // initialize queue for hierarchical traversal
20047
20048 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20049 var n = layoutInfo.layoutNodes[i];
20050 var p_id = n.parentId; // Check if node n has a parent node
20051
20052 if (null != p_id) {
20053 // Add node Id to parent's list of children
20054 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20055 } else {
20056 // If a node doesn't have a parent, then it's in the root graph
20057 queue[++end] = n.id;
20058 tempGraph.push(n.id);
20059 }
20060 } // Add root graph to graphSet
20061
20062
20063 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20064
20065 while (start <= end) {
20066 // Get the node to visit and remove it from queue
20067 var node_id = queue[start++];
20068 var node_ix = layoutInfo.idToIndex[node_id];
20069 var node = layoutInfo.layoutNodes[node_ix];
20070 var children = node.children;
20071
20072 if (children.length > 0) {
20073 // Add children nodes as a new graph to graph set
20074 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20075
20076 for (var i = 0; i < children.length; i++) {
20077 queue[++end] = children[i];
20078 }
20079 }
20080 } // Create indexToGraph map
20081
20082
20083 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20084 var graph = layoutInfo.graphSet[i];
20085
20086 for (var j = 0; j < graph.length; j++) {
20087 var index = layoutInfo.idToIndex[graph[j]];
20088 layoutInfo.indexToGraph[index] = i;
20089 }
20090 } // Iterate over all edges, creating Layout Edges
20091
20092
20093 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20094 var e = edges[i];
20095 var tempEdge = {};
20096 tempEdge.id = e.data('id');
20097 tempEdge.sourceId = e.data('source');
20098 tempEdge.targetId = e.data('target'); // Compute ideal length
20099
20100 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20101 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20102
20103 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20104 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20105 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20106 var targetGraph = layoutInfo.indexToGraph[targetIx];
20107
20108 if (sourceGraph != targetGraph) {
20109 // Find lowest common graph ancestor
20110 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20111
20112 var lcaGraph = layoutInfo.graphSet[lca];
20113 var depth = 0; // Source depth
20114
20115 var tempNode = layoutInfo.layoutNodes[sourceIx];
20116
20117 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20118 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20119 depth++;
20120 } // Target depth
20121
20122
20123 tempNode = layoutInfo.layoutNodes[targetIx];
20124
20125 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20126 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20127 depth++;
20128 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20129 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20130 // ". Depth: " + depth);
20131 // Update idealLength
20132
20133
20134 idealLength *= depth * options.nestingFactor;
20135 }
20136
20137 tempEdge.idealLength = idealLength;
20138 tempEdge.elasticity = elasticity;
20139 layoutInfo.layoutEdges.push(tempEdge);
20140 } // Finally, return layoutInfo object
20141
20142
20143 return layoutInfo;
20144};
20145/**
20146 * @brief : This function finds the index of the lowest common
20147 * graph ancestor between 2 nodes in the subtree
20148 * (from the graph hierarchy induced tree) whose
20149 * root is graphIx
20150 *
20151 * @arg node1: node1's ID
20152 * @arg node2: node2's ID
20153 * @arg layoutInfo: layoutInfo object
20154 *
20155 */
20156
20157
20158var findLCA = function findLCA(node1, node2, layoutInfo) {
20159 // Find their common ancester, starting from the root graph
20160 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20161
20162 if (2 > res.count) {
20163 // If aux function couldn't find the common ancester,
20164 // then it is the root graph
20165 return 0;
20166 } else {
20167 return res.graph;
20168 }
20169};
20170/**
20171 * @brief : Auxiliary function used for LCA computation
20172 *
20173 * @arg node1 : node1's ID
20174 * @arg node2 : node2's ID
20175 * @arg graphIx : subgraph index
20176 * @arg layoutInfo : layoutInfo object
20177 *
20178 * @return : object of the form {count: X, graph: Y}, where:
20179 * X is the number of ancesters (max: 2) found in
20180 * graphIx (and it's subgraphs),
20181 * Y is the graph index of the lowest graph containing
20182 * all X nodes
20183 */
20184
20185
20186var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20187 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20188
20189 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20190 return {
20191 count: 2,
20192 graph: graphIx
20193 };
20194 } // Make recursive calls for all subgraphs
20195
20196
20197 var c = 0;
20198
20199 for (var i = 0; i < graph.length; i++) {
20200 var nodeId = graph[i];
20201 var nodeIx = layoutInfo.idToIndex[nodeId];
20202 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20203
20204 if (0 === children.length) {
20205 continue;
20206 }
20207
20208 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20209 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20210
20211 if (0 === result.count) {
20212 // Neither node1 nor node2 are present in this subgraph
20213 continue;
20214 } else if (1 === result.count) {
20215 // One of (node1, node2) is present in this subgraph
20216 c++;
20217
20218 if (2 === c) {
20219 // We've already found both nodes, no need to keep searching
20220 break;
20221 }
20222 } else {
20223 // Both nodes are present in this subgraph
20224 return result;
20225 }
20226 }
20227
20228 return {
20229 count: c,
20230 graph: graphIx
20231 };
20232};
20233/**
20234 * @brief: printsLayoutInfo into js console
20235 * Only used for debbuging
20236 */
20237
20238
20239if (false) {
20240 var printLayoutInfo;
20241}
20242/**
20243 * @brief : Randomizes the position of all nodes
20244 */
20245
20246
20247var randomizePositions = function randomizePositions(layoutInfo, cy) {
20248 var width = layoutInfo.clientWidth;
20249 var height = layoutInfo.clientHeight;
20250
20251 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20252 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20253
20254 if (0 === n.children.length && !n.isLocked) {
20255 n.positionX = Math.random() * width;
20256 n.positionY = Math.random() * height;
20257 }
20258 }
20259};
20260
20261var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20262 var bb = layoutInfo.boundingBox;
20263 var coseBB = {
20264 x1: Infinity,
20265 x2: -Infinity,
20266 y1: Infinity,
20267 y2: -Infinity
20268 };
20269
20270 if (options.boundingBox) {
20271 nodes.forEach(function (node) {
20272 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20273 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20274 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20275 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20276 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20277 });
20278 coseBB.w = coseBB.x2 - coseBB.x1;
20279 coseBB.h = coseBB.y2 - coseBB.y1;
20280 }
20281
20282 return function (ele, i) {
20283 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20284
20285 if (options.boundingBox) {
20286 // then add extra bounding box constraint
20287 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20288 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20289 return {
20290 x: bb.x1 + pctX * bb.w,
20291 y: bb.y1 + pctY * bb.h
20292 };
20293 } else {
20294 return {
20295 x: lnode.positionX,
20296 y: lnode.positionY
20297 };
20298 }
20299 };
20300};
20301/**
20302 * @brief : Updates the positions of nodes in the network
20303 * @arg layoutInfo : LayoutInfo object
20304 * @arg cy : Cytoscape object
20305 * @arg options : Layout options
20306 */
20307
20308
20309var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20310 // var s = 'Refreshing positions';
20311 // logDebug(s);
20312 var layout = options.layout;
20313 var nodes = options.eles.nodes();
20314 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20315 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20316
20317 if (true !== layoutInfo.ready) {
20318 // s = 'Triggering layoutready';
20319 // logDebug(s);
20320 layoutInfo.ready = true;
20321 layout.one('layoutready', options.ready);
20322 layout.emit({
20323 type: 'layoutready',
20324 layout: this
20325 });
20326 }
20327};
20328/**
20329 * @brief : Logs a debug message in JS console, if DEBUG is ON
20330 */
20331// var logDebug = function(text) {
20332// if (DEBUG) {
20333// console.debug(text);
20334// }
20335// };
20336
20337/**
20338 * @brief : Performs one iteration of the physical simulation
20339 * @arg layoutInfo : LayoutInfo object already initialized
20340 * @arg cy : Cytoscape object
20341 * @arg options : Layout options
20342 */
20343
20344
20345var step$1 = function step(layoutInfo, options, _step) {
20346 // var s = "\n\n###############################";
20347 // s += "\nSTEP: " + step;
20348 // s += "\n###############################\n";
20349 // logDebug(s);
20350 // Calculate node repulsions
20351 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20352
20353 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20354
20355 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20356
20357 propagateForces(layoutInfo); // Update positions based on calculated forces
20358
20359 updatePositions(layoutInfo);
20360};
20361/**
20362 * @brief : Computes the node repulsion forces
20363 */
20364
20365
20366var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20367 // Go through each of the graphs in graphSet
20368 // Nodes only repel each other if they belong to the same graph
20369 // var s = 'calculateNodeForces';
20370 // logDebug(s);
20371 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20372 var graph = layoutInfo.graphSet[i];
20373 var numNodes = graph.length; // s = "Set: " + graph.toString();
20374 // logDebug(s);
20375 // Now get all the pairs of nodes
20376 // Only get each pair once, (A, B) = (B, A)
20377
20378 for (var j = 0; j < numNodes; j++) {
20379 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20380
20381 for (var k = j + 1; k < numNodes; k++) {
20382 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20383 nodeRepulsion(node1, node2, layoutInfo, options);
20384 }
20385 }
20386 }
20387};
20388
20389var randomDistance = function randomDistance(max) {
20390 return -max + 2 * max * Math.random();
20391};
20392/**
20393 * @brief : Compute the node repulsion forces between a pair of nodes
20394 */
20395
20396
20397var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20398 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20399 var cmptId1 = node1.cmptId;
20400 var cmptId2 = node2.cmptId;
20401
20402 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20403 return;
20404 } // Get direction of line connecting both node centers
20405
20406
20407 var directionX = node2.positionX - node1.positionX;
20408 var directionY = node2.positionY - node1.positionY;
20409 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20410 // If both centers are the same, apply a random force
20411
20412 if (0 === directionX && 0 === directionY) {
20413 directionX = randomDistance(maxRandDist);
20414 directionY = randomDistance(maxRandDist);
20415 }
20416
20417 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20418
20419 if (overlap > 0) {
20420 // s += "\nNodes DO overlap.";
20421 // s += "\nOverlap: " + overlap;
20422 // If nodes overlap, repulsion force is proportional
20423 // to the overlap
20424 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20425
20426 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20427
20428 var forceX = force * directionX / distance;
20429 var forceY = force * directionY / distance;
20430 } else {
20431 // s += "\nNodes do NOT overlap.";
20432 // If there's no overlap, force is inversely proportional
20433 // to squared distance
20434 // Get clipping points for both nodes
20435 var point1 = findClippingPoint(node1, directionX, directionY);
20436 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20437
20438 var distanceX = point2.x - point1.x;
20439 var distanceY = point2.y - point1.y;
20440 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20441 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20442 // Compute the module and components of the force vector
20443
20444 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20445 var forceX = force * distanceX / distance;
20446 var forceY = force * distanceY / distance;
20447 } // Apply force
20448
20449
20450 if (!node1.isLocked) {
20451 node1.offsetX -= forceX;
20452 node1.offsetY -= forceY;
20453 }
20454
20455 if (!node2.isLocked) {
20456 node2.offsetX += forceX;
20457 node2.offsetY += forceY;
20458 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20459 // logDebug(s);
20460
20461
20462 return;
20463};
20464/**
20465 * @brief : Determines whether two nodes overlap or not
20466 * @return : Amount of overlapping (0 => no overlap)
20467 */
20468
20469
20470var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20471 if (dX > 0) {
20472 var overlapX = node1.maxX - node2.minX;
20473 } else {
20474 var overlapX = node2.maxX - node1.minX;
20475 }
20476
20477 if (dY > 0) {
20478 var overlapY = node1.maxY - node2.minY;
20479 } else {
20480 var overlapY = node2.maxY - node1.minY;
20481 }
20482
20483 if (overlapX >= 0 && overlapY >= 0) {
20484 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20485 } else {
20486 return 0;
20487 }
20488};
20489/**
20490 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20491 * the rectangular bounding box of it's source/target node
20492 */
20493
20494
20495var findClippingPoint = function findClippingPoint(node, dX, dY) {
20496 // Shorcuts
20497 var X = node.positionX;
20498 var Y = node.positionY;
20499 var H = node.height || 1;
20500 var W = node.width || 1;
20501 var dirSlope = dY / dX;
20502 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20503 // " . Height: " + H + ", Width: " + W +
20504 // "\nDirection " + dX + ", " + dY;
20505 //
20506 // Compute intersection
20507
20508 var res = {}; // Case: Vertical direction (up)
20509
20510 if (0 === dX && 0 < dY) {
20511 res.x = X; // s += "\nUp direction";
20512
20513 res.y = Y + H / 2;
20514 return res;
20515 } // Case: Vertical direction (down)
20516
20517
20518 if (0 === dX && 0 > dY) {
20519 res.x = X;
20520 res.y = Y + H / 2; // s += "\nDown direction";
20521
20522 return res;
20523 } // Case: Intersects the right border
20524
20525
20526 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20527 res.x = X + W / 2;
20528 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20529
20530 return res;
20531 } // Case: Intersects the left border
20532
20533
20534 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20535 res.x = X - W / 2;
20536 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20537
20538 return res;
20539 } // Case: Intersects the top border
20540
20541
20542 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20543 res.x = X + H * dX / 2 / dY;
20544 res.y = Y + H / 2; // s += "\nTop border";
20545
20546 return res;
20547 } // Case: Intersects the bottom border
20548
20549
20550 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20551 res.x = X - H * dX / 2 / dY;
20552 res.y = Y - H / 2; // s += "\nBottom border";
20553
20554 return res;
20555 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20556 // logDebug(s);
20557
20558
20559 return res;
20560};
20561/**
20562 * @brief : Calculates all edge forces
20563 */
20564
20565
20566var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20567 // Iterate over all edges
20568 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20569 // Get edge, source & target nodes
20570 var edge = layoutInfo.layoutEdges[i];
20571 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20572 var source = layoutInfo.layoutNodes[sourceIx];
20573 var targetIx = layoutInfo.idToIndex[edge.targetId];
20574 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20575
20576 var directionX = target.positionX - source.positionX;
20577 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20578 // A random force has already been applied as node repulsion
20579
20580 if (0 === directionX && 0 === directionY) {
20581 continue;
20582 } // Get clipping points for both nodes
20583
20584
20585 var point1 = findClippingPoint(source, directionX, directionY);
20586 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20587 var lx = point2.x - point1.x;
20588 var ly = point2.y - point1.y;
20589 var l = Math.sqrt(lx * lx + ly * ly);
20590 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20591
20592 if (0 !== l) {
20593 var forceX = force * lx / l;
20594 var forceY = force * ly / l;
20595 } else {
20596 var forceX = 0;
20597 var forceY = 0;
20598 } // Add this force to target and source nodes
20599
20600
20601 if (!source.isLocked) {
20602 source.offsetX += forceX;
20603 source.offsetY += forceY;
20604 }
20605
20606 if (!target.isLocked) {
20607 target.offsetX -= forceX;
20608 target.offsetY -= forceY;
20609 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20610 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20611 // logDebug(s);
20612
20613 }
20614};
20615/**
20616 * @brief : Computes gravity forces for all nodes
20617 */
20618
20619
20620var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20621 if (options.gravity === 0) {
20622 return;
20623 }
20624
20625 var distThreshold = 1; // var s = 'calculateGravityForces';
20626 // logDebug(s);
20627
20628 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20629 var graph = layoutInfo.graphSet[i];
20630 var numNodes = graph.length; // s = "Set: " + graph.toString();
20631 // logDebug(s);
20632 // Compute graph center
20633
20634 if (0 === i) {
20635 var centerX = layoutInfo.clientHeight / 2;
20636 var centerY = layoutInfo.clientWidth / 2;
20637 } else {
20638 // Get Parent node for this graph, and use its position as center
20639 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20640 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20641 var centerX = parent.positionX;
20642 var centerY = parent.positionY;
20643 } // s = "Center found at: " + centerX + ", " + centerY;
20644 // logDebug(s);
20645 // Apply force to all nodes in graph
20646
20647
20648 for (var j = 0; j < numNodes; j++) {
20649 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20650
20651 if (node.isLocked) {
20652 continue;
20653 }
20654
20655 var dx = centerX - node.positionX;
20656 var dy = centerY - node.positionY;
20657 var d = Math.sqrt(dx * dx + dy * dy);
20658
20659 if (d > distThreshold) {
20660 var fx = options.gravity * dx / d;
20661 var fy = options.gravity * dy / d;
20662 node.offsetX += fx;
20663 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20664 } // s += ": skypped since it's too close to center";
20665 // logDebug(s);
20666
20667 }
20668 }
20669};
20670/**
20671 * @brief : This function propagates the existing offsets from
20672 * parent nodes to its descendents.
20673 * @arg layoutInfo : layoutInfo Object
20674 * @arg cy : cytoscape Object
20675 * @arg options : Layout options
20676 */
20677
20678
20679var propagateForces = function propagateForces(layoutInfo, options) {
20680 // Inline implementation of a queue, used for traversing the graph in BFS order
20681 var queue = [];
20682 var start = 0; // Points to the start the queue
20683
20684 var end = -1; // Points to the end of the queue
20685 // logDebug('propagateForces');
20686 // Start by visiting the nodes in the root graph
20687
20688 queue.push.apply(queue, layoutInfo.graphSet[0]);
20689 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20690
20691 while (start <= end) {
20692 // Get the node to visit and remove it from queue
20693 var nodeId = queue[start++];
20694 var nodeIndex = layoutInfo.idToIndex[nodeId];
20695 var node = layoutInfo.layoutNodes[nodeIndex];
20696 var children = node.children; // We only need to process the node if it's compound
20697
20698 if (0 < children.length && !node.isLocked) {
20699 var offX = node.offsetX;
20700 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20701 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20702 // s += "\n Children: " + children.toString();
20703 // logDebug(s);
20704
20705 for (var i = 0; i < children.length; i++) {
20706 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20707
20708 childNode.offsetX += offX;
20709 childNode.offsetY += offY; // Add children to queue to be visited
20710
20711 queue[++end] = children[i];
20712 } // Reset parent offsets
20713
20714
20715 node.offsetX = 0;
20716 node.offsetY = 0;
20717 }
20718 }
20719};
20720/**
20721 * @brief : Updates the layout model positions, based on
20722 * the accumulated forces
20723 */
20724
20725
20726var updatePositions = function updatePositions(layoutInfo, options) {
20727 // var s = 'Updating positions';
20728 // logDebug(s);
20729 // Reset boundaries for compound nodes
20730 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20731 var n = layoutInfo.layoutNodes[i];
20732
20733 if (0 < n.children.length) {
20734 // logDebug("Resetting boundaries of compound node: " + n.id);
20735 n.maxX = undefined;
20736 n.minX = undefined;
20737 n.maxY = undefined;
20738 n.minY = undefined;
20739 }
20740 }
20741
20742 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20743 var n = layoutInfo.layoutNodes[i];
20744
20745 if (0 < n.children.length || n.isLocked) {
20746 // No need to set compound or locked node position
20747 // logDebug("Skipping position update of node: " + n.id);
20748 continue;
20749 } // s = "Node: " + n.id + " Previous position: (" +
20750 // n.positionX + ", " + n.positionY + ").";
20751 // Limit displacement in order to improve stability
20752
20753
20754 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20755 n.positionX += tempForce.x;
20756 n.positionY += tempForce.y;
20757 n.offsetX = 0;
20758 n.offsetY = 0;
20759 n.minX = n.positionX - n.width;
20760 n.maxX = n.positionX + n.width;
20761 n.minY = n.positionY - n.height;
20762 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20763 // logDebug(s);
20764 // Update ancestry boudaries
20765
20766 updateAncestryBoundaries(n, layoutInfo);
20767 } // Update size, position of compund nodes
20768
20769
20770 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20771 var n = layoutInfo.layoutNodes[i];
20772
20773 if (0 < n.children.length && !n.isLocked) {
20774 n.positionX = (n.maxX + n.minX) / 2;
20775 n.positionY = (n.maxY + n.minY) / 2;
20776 n.width = n.maxX - n.minX;
20777 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20778 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20779 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20780 // logDebug(s);
20781 }
20782 }
20783};
20784/**
20785 * @brief : Limits a force (forceX, forceY) to be not
20786 * greater (in modulo) than max.
20787 8 Preserves force direction.
20788 */
20789
20790
20791var limitForce = function limitForce(forceX, forceY, max) {
20792 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20793 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20794
20795 if (force > max) {
20796 var res = {
20797 x: max * forceX / force,
20798 y: max * forceY / force
20799 };
20800 } else {
20801 var res = {
20802 x: forceX,
20803 y: forceY
20804 };
20805 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20806 // logDebug(s);
20807
20808
20809 return res;
20810};
20811/**
20812 * @brief : Function used for keeping track of compound node
20813 * sizes, since they should bound all their subnodes.
20814 */
20815
20816
20817var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20818 // var s = "Propagating new position/size of node " + node.id;
20819 var parentId = node.parentId;
20820
20821 if (null == parentId) {
20822 // If there's no parent, we are done
20823 // s += ". No parent node.";
20824 // logDebug(s);
20825 return;
20826 } // Get Parent Node
20827
20828
20829 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20830 var flag = false; // MaxX
20831
20832 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20833 p.maxX = node.maxX + p.padRight;
20834 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20835 } // MinX
20836
20837
20838 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20839 p.minX = node.minX - p.padLeft;
20840 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20841 } // MaxY
20842
20843
20844 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20845 p.maxY = node.maxY + p.padBottom;
20846 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20847 } // MinY
20848
20849
20850 if (null == p.minY || node.minY - p.padTop < p.minY) {
20851 p.minY = node.minY - p.padTop;
20852 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20853 } // If updated boundaries, propagate changes upward
20854
20855
20856 if (flag) {
20857 // logDebug(s);
20858 return updateAncestryBoundaries(p, layoutInfo);
20859 } // s += ". No changes in boundaries/position of parent node " + p.id;
20860 // logDebug(s);
20861
20862
20863 return;
20864};
20865
20866var separateComponents = function separateComponents(layoutInfo, options) {
20867 var nodes = layoutInfo.layoutNodes;
20868 var components = [];
20869
20870 for (var i = 0; i < nodes.length; i++) {
20871 var node = nodes[i];
20872 var cid = node.cmptId;
20873 var component = components[cid] = components[cid] || [];
20874 component.push(node);
20875 }
20876
20877 var totalA = 0;
20878
20879 for (var i = 0; i < components.length; i++) {
20880 var c = components[i];
20881
20882 if (!c) {
20883 continue;
20884 }
20885
20886 c.x1 = Infinity;
20887 c.x2 = -Infinity;
20888 c.y1 = Infinity;
20889 c.y2 = -Infinity;
20890
20891 for (var j = 0; j < c.length; j++) {
20892 var n = c[j];
20893 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20894 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20895 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20896 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20897 }
20898
20899 c.w = c.x2 - c.x1;
20900 c.h = c.y2 - c.y1;
20901 totalA += c.w * c.h;
20902 }
20903
20904 components.sort(function (c1, c2) {
20905 return c2.w * c2.h - c1.w * c1.h;
20906 });
20907 var x = 0;
20908 var y = 0;
20909 var usedW = 0;
20910 var rowH = 0;
20911 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20912
20913 for (var i = 0; i < components.length; i++) {
20914 var c = components[i];
20915
20916 if (!c) {
20917 continue;
20918 }
20919
20920 for (var j = 0; j < c.length; j++) {
20921 var n = c[j];
20922
20923 if (!n.isLocked) {
20924 n.positionX += x - c.x1;
20925 n.positionY += y - c.y1;
20926 }
20927 }
20928
20929 x += c.w + options.componentSpacing;
20930 usedW += c.w + options.componentSpacing;
20931 rowH = Math.max(rowH, c.h);
20932
20933 if (usedW > maxRowW) {
20934 y += rowH + options.componentSpacing;
20935 x = 0;
20936 usedW = 0;
20937 rowH = 0;
20938 }
20939 }
20940};
20941
20942var defaults$d = {
20943 fit: true,
20944 // whether to fit the viewport to the graph
20945 padding: 30,
20946 // padding used on fit
20947 boundingBox: undefined,
20948 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20949 avoidOverlap: true,
20950 // prevents node overlap, may overflow boundingBox if not enough space
20951 avoidOverlapPadding: 10,
20952 // extra spacing around nodes when avoidOverlap: true
20953 nodeDimensionsIncludeLabels: false,
20954 // Excludes the label when calculating node bounding boxes for the layout algorithm
20955 spacingFactor: undefined,
20956 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20957 condense: false,
20958 // uses all available space on false, uses minimal space on true
20959 rows: undefined,
20960 // force num of rows in the grid
20961 cols: undefined,
20962 // force num of columns in the grid
20963 position: function position(node) {},
20964 // returns { row, col } for element
20965 sort: undefined,
20966 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20967 animate: false,
20968 // whether to transition the node positions
20969 animationDuration: 500,
20970 // duration of animation in ms if enabled
20971 animationEasing: undefined,
20972 // easing of animation if enabled
20973 animateFilter: function animateFilter(node, i) {
20974 return true;
20975 },
20976 // 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
20977 ready: undefined,
20978 // callback on layoutready
20979 stop: undefined,
20980 // callback on layoutstop
20981 transform: function transform(node, position) {
20982 return position;
20983 } // transform a given node position. Useful for changing flow direction in discrete layouts
20984
20985};
20986
20987function GridLayout(options) {
20988 this.options = extend({}, defaults$d, options);
20989}
20990
20991GridLayout.prototype.run = function () {
20992 var params = this.options;
20993 var options = params;
20994 var cy = params.cy;
20995 var eles = options.eles;
20996 var nodes = eles.nodes().not(':parent');
20997
20998 if (options.sort) {
20999 nodes = nodes.sort(options.sort);
21000 }
21001
21002 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21003 x1: 0,
21004 y1: 0,
21005 w: cy.width(),
21006 h: cy.height()
21007 });
21008
21009 if (bb.h === 0 || bb.w === 0) {
21010 eles.nodes().layoutPositions(this, options, function (ele) {
21011 return {
21012 x: bb.x1,
21013 y: bb.y1
21014 };
21015 });
21016 } else {
21017 // width/height * splits^2 = cells where splits is number of times to split width
21018 var cells = nodes.size();
21019 var splits = Math.sqrt(cells * bb.h / bb.w);
21020 var rows = Math.round(splits);
21021 var cols = Math.round(bb.w / bb.h * splits);
21022
21023 var small = function small(val) {
21024 if (val == null) {
21025 return Math.min(rows, cols);
21026 } else {
21027 var min = Math.min(rows, cols);
21028
21029 if (min == rows) {
21030 rows = val;
21031 } else {
21032 cols = val;
21033 }
21034 }
21035 };
21036
21037 var large = function large(val) {
21038 if (val == null) {
21039 return Math.max(rows, cols);
21040 } else {
21041 var max = Math.max(rows, cols);
21042
21043 if (max == rows) {
21044 rows = val;
21045 } else {
21046 cols = val;
21047 }
21048 }
21049 };
21050
21051 var oRows = options.rows;
21052 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21053
21054 if (oRows != null && oCols != null) {
21055 rows = oRows;
21056 cols = oCols;
21057 } else if (oRows != null && oCols == null) {
21058 rows = oRows;
21059 cols = Math.ceil(cells / rows);
21060 } else if (oRows == null && oCols != null) {
21061 cols = oCols;
21062 rows = Math.ceil(cells / cols);
21063 } // otherwise use the automatic values and adjust accordingly
21064 // if rounding was up, see if we can reduce rows or columns
21065 else if (cols * rows > cells) {
21066 var sm = small();
21067 var lg = large(); // reducing the small side takes away the most cells, so try it first
21068
21069 if ((sm - 1) * lg >= cells) {
21070 small(sm - 1);
21071 } else if ((lg - 1) * sm >= cells) {
21072 large(lg - 1);
21073 }
21074 } else {
21075 // if rounding was too low, add rows or columns
21076 while (cols * rows < cells) {
21077 var _sm = small();
21078
21079 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21080
21081
21082 if ((_lg + 1) * _sm >= cells) {
21083 large(_lg + 1);
21084 } else {
21085 small(_sm + 1);
21086 }
21087 }
21088 }
21089
21090 var cellWidth = bb.w / cols;
21091 var cellHeight = bb.h / rows;
21092
21093 if (options.condense) {
21094 cellWidth = 0;
21095 cellHeight = 0;
21096 }
21097
21098 if (options.avoidOverlap) {
21099 for (var i = 0; i < nodes.length; i++) {
21100 var node = nodes[i];
21101 var pos = node._private.position;
21102
21103 if (pos.x == null || pos.y == null) {
21104 // for bb
21105 pos.x = 0;
21106 pos.y = 0;
21107 }
21108
21109 var nbb = node.layoutDimensions(options);
21110 var p = options.avoidOverlapPadding;
21111 var w = nbb.w + p;
21112 var h = nbb.h + p;
21113 cellWidth = Math.max(cellWidth, w);
21114 cellHeight = Math.max(cellHeight, h);
21115 }
21116 }
21117
21118 var cellUsed = {}; // e.g. 'c-0-2' => true
21119
21120 var used = function used(row, col) {
21121 return cellUsed['c-' + row + '-' + col] ? true : false;
21122 };
21123
21124 var use = function use(row, col) {
21125 cellUsed['c-' + row + '-' + col] = true;
21126 }; // to keep track of current cell position
21127
21128
21129 var row = 0;
21130 var col = 0;
21131
21132 var moveToNextCell = function moveToNextCell() {
21133 col++;
21134
21135 if (col >= cols) {
21136 col = 0;
21137 row++;
21138 }
21139 }; // get a cache of all the manual positions
21140
21141
21142 var id2manPos = {};
21143
21144 for (var _i = 0; _i < nodes.length; _i++) {
21145 var _node = nodes[_i];
21146 var rcPos = options.position(_node);
21147
21148 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21149 // must have at least row or col def'd
21150 var _pos = {
21151 row: rcPos.row,
21152 col: rcPos.col
21153 };
21154
21155 if (_pos.col === undefined) {
21156 // find unused col
21157 _pos.col = 0;
21158
21159 while (used(_pos.row, _pos.col)) {
21160 _pos.col++;
21161 }
21162 } else if (_pos.row === undefined) {
21163 // find unused row
21164 _pos.row = 0;
21165
21166 while (used(_pos.row, _pos.col)) {
21167 _pos.row++;
21168 }
21169 }
21170
21171 id2manPos[_node.id()] = _pos;
21172 use(_pos.row, _pos.col);
21173 }
21174 }
21175
21176 var getPos = function getPos(element, i) {
21177 var x, y;
21178
21179 if (element.locked() || element.isParent()) {
21180 return false;
21181 } // see if we have a manual position set
21182
21183
21184 var rcPos = id2manPos[element.id()];
21185
21186 if (rcPos) {
21187 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21188 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21189 } else {
21190 // otherwise set automatically
21191 while (used(row, col)) {
21192 moveToNextCell();
21193 }
21194
21195 x = col * cellWidth + cellWidth / 2 + bb.x1;
21196 y = row * cellHeight + cellHeight / 2 + bb.y1;
21197 use(row, col);
21198 moveToNextCell();
21199 }
21200
21201 return {
21202 x: x,
21203 y: y
21204 };
21205 };
21206
21207 nodes.layoutPositions(this, options, getPos);
21208 }
21209
21210 return this; // chaining
21211};
21212
21213var defaults$e = {
21214 ready: function ready() {},
21215 // on layoutready
21216 stop: function stop() {} // on layoutstop
21217
21218}; // constructor
21219// options : object containing layout options
21220
21221function NullLayout(options) {
21222 this.options = extend({}, defaults$e, options);
21223} // runs the layout
21224
21225
21226NullLayout.prototype.run = function () {
21227 var options = this.options;
21228 var eles = options.eles; // elements to consider in the layout
21229
21230 var layout = this; // cy is automatically populated for us in the constructor
21231 // (disable eslint for next line as this serves as example layout code to external developers)
21232 // eslint-disable-next-line no-unused-vars
21233
21234 var cy = options.cy;
21235 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21236 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21237
21238 eles.nodes().positions(function () {
21239 return {
21240 x: 0,
21241 y: 0
21242 };
21243 }); // trigger layoutready when each node has had its position set at least once
21244
21245 layout.one('layoutready', options.ready);
21246 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21247
21248 layout.one('layoutstop', options.stop);
21249 layout.emit('layoutstop');
21250 return this; // chaining
21251}; // called on continuous layouts to stop them before they finish
21252
21253
21254NullLayout.prototype.stop = function () {
21255 return this; // chaining
21256};
21257
21258var defaults$f = {
21259 positions: undefined,
21260 // map of (node id) => (position obj); or function(node){ return somPos; }
21261 zoom: undefined,
21262 // the zoom level to set (prob want fit = false if set)
21263 pan: undefined,
21264 // the pan level to set (prob want fit = false if set)
21265 fit: true,
21266 // whether to fit to viewport
21267 padding: 30,
21268 // padding on fit
21269 animate: false,
21270 // whether to transition the node positions
21271 animationDuration: 500,
21272 // duration of animation in ms if enabled
21273 animationEasing: undefined,
21274 // easing of animation if enabled
21275 animateFilter: function animateFilter(node, i) {
21276 return true;
21277 },
21278 // 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
21279 ready: undefined,
21280 // callback on layoutready
21281 stop: undefined,
21282 // callback on layoutstop
21283 transform: function transform(node, position) {
21284 return position;
21285 } // transform a given node position. Useful for changing flow direction in discrete layouts
21286
21287};
21288
21289function PresetLayout(options) {
21290 this.options = extend({}, defaults$f, options);
21291}
21292
21293PresetLayout.prototype.run = function () {
21294 var options = this.options;
21295 var eles = options.eles;
21296 var nodes = eles.nodes();
21297 var posIsFn = fn(options.positions);
21298
21299 function getPosition(node) {
21300 if (options.positions == null) {
21301 return copyPosition(node.position());
21302 }
21303
21304 if (posIsFn) {
21305 return options.positions(node);
21306 }
21307
21308 var pos = options.positions[node._private.data.id];
21309
21310 if (pos == null) {
21311 return null;
21312 }
21313
21314 return pos;
21315 }
21316
21317 nodes.layoutPositions(this, options, function (node, i) {
21318 var position = getPosition(node);
21319
21320 if (node.locked() || position == null) {
21321 return false;
21322 }
21323
21324 return position;
21325 });
21326 return this; // chaining
21327};
21328
21329var defaults$g = {
21330 fit: true,
21331 // whether to fit to viewport
21332 padding: 30,
21333 // fit padding
21334 boundingBox: undefined,
21335 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21336 animate: false,
21337 // whether to transition the node positions
21338 animationDuration: 500,
21339 // duration of animation in ms if enabled
21340 animationEasing: undefined,
21341 // easing of animation if enabled
21342 animateFilter: function animateFilter(node, i) {
21343 return true;
21344 },
21345 // 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
21346 ready: undefined,
21347 // callback on layoutready
21348 stop: undefined,
21349 // callback on layoutstop
21350 transform: function transform(node, position) {
21351 return position;
21352 } // transform a given node position. Useful for changing flow direction in discrete layouts
21353
21354};
21355
21356function RandomLayout(options) {
21357 this.options = extend({}, defaults$g, options);
21358}
21359
21360RandomLayout.prototype.run = function () {
21361 var options = this.options;
21362 var cy = options.cy;
21363 var eles = options.eles;
21364 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21365 x1: 0,
21366 y1: 0,
21367 w: cy.width(),
21368 h: cy.height()
21369 });
21370
21371 var getPos = function getPos(node, i) {
21372 return {
21373 x: bb.x1 + Math.round(Math.random() * bb.w),
21374 y: bb.y1 + Math.round(Math.random() * bb.h)
21375 };
21376 };
21377
21378 eles.nodes().layoutPositions(this, options, getPos);
21379 return this; // chaining
21380};
21381
21382var layout = [{
21383 name: 'breadthfirst',
21384 impl: BreadthFirstLayout
21385}, {
21386 name: 'circle',
21387 impl: CircleLayout
21388}, {
21389 name: 'concentric',
21390 impl: ConcentricLayout
21391}, {
21392 name: 'cose',
21393 impl: CoseLayout
21394}, {
21395 name: 'grid',
21396 impl: GridLayout
21397}, {
21398 name: 'null',
21399 impl: NullLayout
21400}, {
21401 name: 'preset',
21402 impl: PresetLayout
21403}, {
21404 name: 'random',
21405 impl: RandomLayout
21406}];
21407
21408function NullRenderer(options) {
21409 this.options = options;
21410 this.notifications = 0; // for testing
21411}
21412
21413var noop$1 = function noop() {};
21414
21415var throwImgErr = function throwImgErr() {
21416 throw new Error('A headless instance can not render images');
21417};
21418
21419NullRenderer.prototype = {
21420 recalculateRenderedStyle: noop$1,
21421 notify: function notify() {
21422 this.notifications++;
21423 },
21424 init: noop$1,
21425 isHeadless: function isHeadless() {
21426 return true;
21427 },
21428 png: throwImgErr,
21429 jpg: throwImgErr
21430};
21431
21432var BRp = {};
21433BRp.arrowShapeWidth = 0.3;
21434
21435BRp.registerArrowShapes = function () {
21436 var arrowShapes = this.arrowShapes = {};
21437 var renderer = this; // Contract for arrow shapes:
21438 // 0, 0 is arrow tip
21439 // (0, 1) is direction towards node
21440 // (1, 0) is right
21441 //
21442 // functional api:
21443 // collide: check x, y in shape
21444 // roughCollide: called before collide, no false negatives
21445 // draw: draw
21446 // spacing: dist(arrowTip, nodeBoundary)
21447 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21448
21449 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21450 var x1 = translation.x - size / 2 - padding;
21451 var x2 = translation.x + size / 2 + padding;
21452 var y1 = translation.y - size / 2 - padding;
21453 var y2 = translation.y + size / 2 + padding;
21454 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21455 return inside;
21456 };
21457
21458 var transform = function transform(x, y, size, angle, translation) {
21459 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21460 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21461 var xScaled = xRotated * size;
21462 var yScaled = yRotated * size;
21463 var xTranslated = xScaled + translation.x;
21464 var yTranslated = yScaled + translation.y;
21465 return {
21466 x: xTranslated,
21467 y: yTranslated
21468 };
21469 };
21470
21471 var transformPoints = function transformPoints(pts, size, angle, translation) {
21472 var retPts = [];
21473
21474 for (var i = 0; i < pts.length; i += 2) {
21475 var x = pts[i];
21476 var y = pts[i + 1];
21477 retPts.push(transform(x, y, size, angle, translation));
21478 }
21479
21480 return retPts;
21481 };
21482
21483 var pointsToArr = function pointsToArr(pts) {
21484 var ret = [];
21485
21486 for (var i = 0; i < pts.length; i++) {
21487 var p = pts[i];
21488 ret.push(p.x, p.y);
21489 }
21490
21491 return ret;
21492 };
21493
21494 var standardGap = function standardGap(edge) {
21495 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21496 };
21497
21498 var defineArrowShape = function defineArrowShape(name, defn) {
21499 if (string(defn)) {
21500 defn = arrowShapes[defn];
21501 }
21502
21503 arrowShapes[name] = extend({
21504 name: name,
21505 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21506 collide: function collide(x, y, size, angle, translation, padding) {
21507 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21508 var inside = pointInsidePolygonPoints(x, y, points);
21509 return inside;
21510 },
21511 roughCollide: bbCollide,
21512 draw: function draw(context, size, angle, translation) {
21513 var points = transformPoints(this.points, size, angle, translation);
21514 renderer.arrowShapeImpl('polygon')(context, points);
21515 },
21516 spacing: function spacing(edge) {
21517 return 0;
21518 },
21519 gap: standardGap
21520 }, defn);
21521 };
21522
21523 defineArrowShape('none', {
21524 collide: falsify,
21525 roughCollide: falsify,
21526 draw: noop,
21527 spacing: zeroify,
21528 gap: zeroify
21529 });
21530 defineArrowShape('triangle', {
21531 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21532 });
21533 defineArrowShape('arrow', 'triangle');
21534 defineArrowShape('triangle-backcurve', {
21535 points: arrowShapes['triangle'].points,
21536 controlPoint: [0, -0.15],
21537 roughCollide: bbCollide,
21538 draw: function draw(context, size, angle, translation, edgeWidth) {
21539 var ptsTrans = transformPoints(this.points, size, angle, translation);
21540 var ctrlPt = this.controlPoint;
21541 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21542 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21543 },
21544 gap: function gap(edge) {
21545 return standardGap(edge) * 0.8;
21546 }
21547 });
21548 defineArrowShape('triangle-tee', {
21549 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21550 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21551 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21552 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21553 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21554 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21555 return inside;
21556 },
21557 draw: function draw(context, size, angle, translation, edgeWidth) {
21558 var triPts = transformPoints(this.points, size, angle, translation);
21559 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21560 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21561 }
21562 });
21563 defineArrowShape('circle-triangle', {
21564 radius: 0.15,
21565 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21566 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21567 var t = translation;
21568 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21569 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21570 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21571 },
21572 draw: function draw(context, size, angle, translation, edgeWidth) {
21573 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21574 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21575 },
21576 spacing: function spacing(edge) {
21577 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21578 }
21579 });
21580 defineArrowShape('triangle-cross', {
21581 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21582 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21583 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21584 0.15, -0.4],
21585 crossLinePts: function crossLinePts(size, edgeWidth) {
21586 // shift points so that the distance between the cross points matches edge width
21587 var p = this.baseCrossLinePts.slice();
21588 var shiftFactor = edgeWidth / size;
21589 var y0 = 3;
21590 var y1 = 5;
21591 p[y0] = p[y0] - shiftFactor;
21592 p[y1] = p[y1] - shiftFactor;
21593 return p;
21594 },
21595 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21596 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21597 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21598 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21599 return inside;
21600 },
21601 draw: function draw(context, size, angle, translation, edgeWidth) {
21602 var triPts = transformPoints(this.points, size, angle, translation);
21603 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21604 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21605 }
21606 });
21607 defineArrowShape('vee', {
21608 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21609 gap: function gap(edge) {
21610 return standardGap(edge) * 0.525;
21611 }
21612 });
21613 defineArrowShape('circle', {
21614 radius: 0.15,
21615 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21616 var t = translation;
21617 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21618 return inside;
21619 },
21620 draw: function draw(context, size, angle, translation, edgeWidth) {
21621 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21622 },
21623 spacing: function spacing(edge) {
21624 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21625 }
21626 });
21627 defineArrowShape('tee', {
21628 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21629 spacing: function spacing(edge) {
21630 return 1;
21631 },
21632 gap: function gap(edge) {
21633 return 1;
21634 }
21635 });
21636 defineArrowShape('square', {
21637 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21638 });
21639 defineArrowShape('diamond', {
21640 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21641 gap: function gap(edge) {
21642 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21643 }
21644 });
21645 defineArrowShape('chevron', {
21646 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21647 gap: function gap(edge) {
21648 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21649 }
21650 });
21651};
21652
21653var BRp$1 = {}; // Project mouse
21654
21655BRp$1.projectIntoViewport = function (clientX, clientY) {
21656 var cy = this.cy;
21657 var offsets = this.findContainerClientCoords();
21658 var offsetLeft = offsets[0];
21659 var offsetTop = offsets[1];
21660 var scale = offsets[4];
21661 var pan = cy.pan();
21662 var zoom = cy.zoom();
21663 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21664 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21665 return [x, y];
21666};
21667
21668BRp$1.findContainerClientCoords = function () {
21669 if (this.containerBB) {
21670 return this.containerBB;
21671 }
21672
21673 var container = this.container;
21674 var rect = container.getBoundingClientRect();
21675 var style = window$1.getComputedStyle(container);
21676
21677 var styleValue = function styleValue(name) {
21678 return parseFloat(style.getPropertyValue(name));
21679 };
21680
21681 var padding = {
21682 left: styleValue('padding-left'),
21683 right: styleValue('padding-right'),
21684 top: styleValue('padding-top'),
21685 bottom: styleValue('padding-bottom')
21686 };
21687 var border = {
21688 left: styleValue('border-left-width'),
21689 right: styleValue('border-right-width'),
21690 top: styleValue('border-top-width'),
21691 bottom: styleValue('border-bottom-width')
21692 };
21693 var clientWidth = container.clientWidth;
21694 var clientHeight = container.clientHeight;
21695 var paddingHor = padding.left + padding.right;
21696 var paddingVer = padding.top + padding.bottom;
21697 var borderHor = border.left + border.right;
21698 var scale = rect.width / (clientWidth + borderHor);
21699 var unscaledW = clientWidth - paddingHor;
21700 var unscaledH = clientHeight - paddingVer;
21701 var left = rect.left + padding.left + border.left;
21702 var top = rect.top + padding.top + border.top;
21703 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21704};
21705
21706BRp$1.invalidateContainerClientCoordsCache = function () {
21707 this.containerBB = null;
21708};
21709
21710BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21711 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21712};
21713
21714BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21715 var self = this;
21716 var r = this;
21717 var eles = r.getCachedZSortedEles();
21718 var near = []; // 1 node max, 1 edge max
21719
21720 var zoom = r.cy.zoom();
21721 var hasCompounds = r.cy.hasCompoundNodes();
21722 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21723 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21724 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21725 var minSqDist = Infinity;
21726 var nearEdge;
21727 var nearNode;
21728
21729 if (interactiveElementsOnly) {
21730 eles = eles.interactive;
21731 }
21732
21733 function addEle(ele, sqDist) {
21734 if (ele.isNode()) {
21735 if (nearNode) {
21736 return; // can't replace node
21737 } else {
21738 nearNode = ele;
21739 near.push(ele);
21740 }
21741 }
21742
21743 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21744 if (nearEdge) {
21745 // then replace existing edge
21746 // can replace only if same z-index
21747 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) {
21748 for (var i = 0; i < near.length; i++) {
21749 if (near[i].isEdge()) {
21750 near[i] = ele;
21751 nearEdge = ele;
21752 minSqDist = sqDist != null ? sqDist : minSqDist;
21753 break;
21754 }
21755 }
21756 }
21757 } else {
21758 near.push(ele);
21759 nearEdge = ele;
21760 minSqDist = sqDist != null ? sqDist : minSqDist;
21761 }
21762 }
21763 }
21764
21765 function checkNode(node) {
21766 var width = node.outerWidth() + 2 * nodeThreshold;
21767 var height = node.outerHeight() + 2 * nodeThreshold;
21768 var hw = width / 2;
21769 var hh = height / 2;
21770 var pos = node.position();
21771
21772 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21773 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21774 ) {
21775 var shape = r.nodeShapes[self.getNodeShape(node)];
21776
21777 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21778 addEle(node, 0);
21779 return true;
21780 }
21781 }
21782 }
21783
21784 function checkEdge(edge) {
21785 var _p = edge._private;
21786 var rs = _p.rscratch;
21787 var styleWidth = edge.pstyle('width').pfValue;
21788 var scale = edge.pstyle('arrow-scale').value;
21789 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21790
21791 var widthSq = width * width;
21792 var width2 = width * 2;
21793 var src = _p.source;
21794 var tgt = _p.target;
21795 var sqDist;
21796
21797 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21798 var pts = rs.allpts;
21799
21800 for (var i = 0; i + 3 < pts.length; i += 2) {
21801 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]))) {
21802 addEle(edge, sqDist);
21803 return true;
21804 }
21805 }
21806 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21807 var pts = rs.allpts;
21808
21809 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21810 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]))) {
21811 addEle(edge, sqDist);
21812 return true;
21813 }
21814 }
21815 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21816
21817
21818 var src = src || _p.source;
21819 var tgt = tgt || _p.target;
21820 var arSize = self.getArrowWidth(styleWidth, scale);
21821 var arrows = [{
21822 name: 'source',
21823 x: rs.arrowStartX,
21824 y: rs.arrowStartY,
21825 angle: rs.srcArrowAngle
21826 }, {
21827 name: 'target',
21828 x: rs.arrowEndX,
21829 y: rs.arrowEndY,
21830 angle: rs.tgtArrowAngle
21831 }, {
21832 name: 'mid-source',
21833 x: rs.midX,
21834 y: rs.midY,
21835 angle: rs.midsrcArrowAngle
21836 }, {
21837 name: 'mid-target',
21838 x: rs.midX,
21839 y: rs.midY,
21840 angle: rs.midtgtArrowAngle
21841 }];
21842
21843 for (var i = 0; i < arrows.length; i++) {
21844 var ar = arrows[i];
21845 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21846 var edgeWidth = edge.pstyle('width').pfValue;
21847
21848 if (shape.roughCollide(x, y, arSize, ar.angle, {
21849 x: ar.x,
21850 y: ar.y
21851 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21852 x: ar.x,
21853 y: ar.y
21854 }, edgeWidth, edgeThreshold)) {
21855 addEle(edge);
21856 return true;
21857 }
21858 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21859
21860
21861 if (hasCompounds && near.length > 0) {
21862 checkNode(src);
21863 checkNode(tgt);
21864 }
21865 }
21866
21867 function preprop(obj, name, pre) {
21868 return getPrefixedProperty(obj, name, pre);
21869 }
21870
21871 function checkLabel(ele, prefix) {
21872 var _p = ele._private;
21873 var th = labelThreshold;
21874 var prefixDash;
21875
21876 if (prefix) {
21877 prefixDash = prefix + '-';
21878 } else {
21879 prefixDash = '';
21880 }
21881
21882 ele.boundingBox();
21883 var bb = _p.labelBounds[prefix || 'main'];
21884 var text = ele.pstyle(prefixDash + 'label').value;
21885 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21886
21887 if (!eventsEnabled || !text) {
21888 return;
21889 }
21890
21891 var lx = preprop(_p.rscratch, 'labelX', prefix);
21892 var ly = preprop(_p.rscratch, 'labelY', prefix);
21893 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21894 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21895 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21896 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21897
21898 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21899
21900 var ly1 = bb.y1 - th - oy;
21901 var ly2 = bb.y2 + th - oy;
21902
21903 if (theta) {
21904 var cos = Math.cos(theta);
21905 var sin = Math.sin(theta);
21906
21907 var rotate = function rotate(x, y) {
21908 x = x - lx;
21909 y = y - ly;
21910 return {
21911 x: x * cos - y * sin + lx,
21912 y: x * sin + y * cos + ly
21913 };
21914 };
21915
21916 var px1y1 = rotate(lx1, ly1);
21917 var px1y2 = rotate(lx1, ly2);
21918 var px2y1 = rotate(lx2, ly1);
21919 var px2y2 = rotate(lx2, ly2);
21920 var points = [// with the margin added after the rotation is applied
21921 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21922
21923 if (pointInsidePolygonPoints(x, y, points)) {
21924 addEle(ele);
21925 return true;
21926 }
21927 } else {
21928 // do a cheaper bb check
21929 if (inBoundingBox(bb, x, y)) {
21930 addEle(ele);
21931 return true;
21932 }
21933 }
21934 }
21935
21936 for (var i = eles.length - 1; i >= 0; i--) {
21937 // reverse order for precedence
21938 var ele = eles[i];
21939
21940 if (ele.isNode()) {
21941 checkNode(ele) || checkLabel(ele);
21942 } else {
21943 // then edge
21944 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21945 }
21946 }
21947
21948 return near;
21949}; // 'Give me everything from this box'
21950
21951
21952BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21953 var eles = this.getCachedZSortedEles().interactive;
21954 var box = [];
21955 var x1c = Math.min(x1, x2);
21956 var x2c = Math.max(x1, x2);
21957 var y1c = Math.min(y1, y2);
21958 var y2c = Math.max(y1, y2);
21959 x1 = x1c;
21960 x2 = x2c;
21961 y1 = y1c;
21962 y2 = y2c;
21963 var boxBb = makeBoundingBox({
21964 x1: x1,
21965 y1: y1,
21966 x2: x2,
21967 y2: y2
21968 });
21969
21970 for (var e = 0; e < eles.length; e++) {
21971 var ele = eles[e];
21972
21973 if (ele.isNode()) {
21974 var node = ele;
21975 var nodeBb = node.boundingBox({
21976 includeNodes: true,
21977 includeEdges: false,
21978 includeLabels: false
21979 });
21980
21981 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21982 box.push(node);
21983 }
21984 } else {
21985 var edge = ele;
21986 var _p = edge._private;
21987 var rs = _p.rscratch;
21988
21989 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21990 continue;
21991 }
21992
21993 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21994 continue;
21995 }
21996
21997 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21998 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21999 var allInside = true;
22000
22001 for (var i = 0; i < pts.length; i++) {
22002 if (!pointInBoundingBox(boxBb, pts[i])) {
22003 allInside = false;
22004 break;
22005 }
22006 }
22007
22008 if (allInside) {
22009 box.push(edge);
22010 }
22011 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22012 box.push(edge);
22013 }
22014 }
22015 }
22016
22017 return box;
22018};
22019
22020var BRp$2 = {};
22021
22022BRp$2.calculateArrowAngles = function (edge) {
22023 var rs = edge._private.rscratch;
22024 var isHaystack = rs.edgeType === 'haystack';
22025 var isBezier = rs.edgeType === 'bezier';
22026 var isMultibezier = rs.edgeType === 'multibezier';
22027 var isSegments = rs.edgeType === 'segments';
22028 var isCompound = rs.edgeType === 'compound';
22029 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22030
22031 var dispX, dispY;
22032 var startX, startY, endX, endY, midX, midY;
22033
22034 if (isHaystack) {
22035 startX = rs.haystackPts[0];
22036 startY = rs.haystackPts[1];
22037 endX = rs.haystackPts[2];
22038 endY = rs.haystackPts[3];
22039 } else {
22040 startX = rs.arrowStartX;
22041 startY = rs.arrowStartY;
22042 endX = rs.arrowEndX;
22043 endY = rs.arrowEndY;
22044 }
22045
22046 midX = rs.midX;
22047 midY = rs.midY; // source
22048 //
22049
22050 if (isSegments) {
22051 dispX = startX - rs.segpts[0];
22052 dispY = startY - rs.segpts[1];
22053 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22054 var pts = rs.allpts;
22055 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22056 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22057 dispX = startX - bX;
22058 dispY = startY - bY;
22059 } else {
22060 dispX = startX - midX;
22061 dispY = startY - midY;
22062 }
22063
22064 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22065 //
22066
22067 var midX = rs.midX;
22068 var midY = rs.midY;
22069
22070 if (isHaystack) {
22071 midX = (startX + endX) / 2;
22072 midY = (startY + endY) / 2;
22073 }
22074
22075 dispX = endX - startX;
22076 dispY = endY - startY;
22077
22078 if (isSegments) {
22079 var pts = rs.allpts;
22080
22081 if (pts.length / 2 % 2 === 0) {
22082 var i2 = pts.length / 2;
22083 var i1 = i2 - 2;
22084 dispX = pts[i2] - pts[i1];
22085 dispY = pts[i2 + 1] - pts[i1 + 1];
22086 } else {
22087 var i2 = pts.length / 2 - 1;
22088 var i1 = i2 - 2;
22089 var i3 = i2 + 2;
22090 dispX = pts[i2] - pts[i1];
22091 dispY = pts[i2 + 1] - pts[i1 + 1];
22092 }
22093 } else if (isMultibezier || isCompound || isSelf) {
22094 var pts = rs.allpts;
22095 var cpts = rs.ctrlpts;
22096 var bp0x, bp0y;
22097 var bp1x, bp1y;
22098
22099 if (cpts.length / 2 % 2 === 0) {
22100 var p0 = pts.length / 2 - 1; // startpt
22101
22102 var ic = p0 + 2;
22103 var p1 = ic + 2;
22104 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22105 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22106 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22107 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22108 } else {
22109 var ic = pts.length / 2 - 1; // ctrpt
22110
22111 var p0 = ic - 2; // startpt
22112
22113 var p1 = ic + 2; // endpt
22114
22115 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22116 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22117 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22118 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22119 }
22120
22121 dispX = bp1x - bp0x;
22122 dispY = bp1y - bp0y;
22123 }
22124
22125 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22126 rs.midDispX = dispX;
22127 rs.midDispY = dispY; // mid source
22128 //
22129
22130 dispX *= -1;
22131 dispY *= -1;
22132
22133 if (isSegments) {
22134 var pts = rs.allpts;
22135
22136 if (pts.length / 2 % 2 === 0) ; else {
22137 var i2 = pts.length / 2 - 1;
22138 var i3 = i2 + 2;
22139 dispX = -(pts[i3] - pts[i2]);
22140 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22141 }
22142 }
22143
22144 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22145 //
22146
22147 if (isSegments) {
22148 dispX = endX - rs.segpts[rs.segpts.length - 2];
22149 dispY = endY - rs.segpts[rs.segpts.length - 1];
22150 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22151 var pts = rs.allpts;
22152 var l = pts.length;
22153 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22154 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22155 dispX = endX - bX;
22156 dispY = endY - bY;
22157 } else {
22158 dispX = endX - midX;
22159 dispY = endY - midY;
22160 }
22161
22162 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22163};
22164
22165BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22166 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22167 var cachedVal = cache[edgeWidth + ', ' + scale];
22168
22169 if (cachedVal) {
22170 return cachedVal;
22171 }
22172
22173 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22174 cache[edgeWidth + ', ' + scale] = cachedVal;
22175 return cachedVal;
22176};
22177
22178var BRp$3 = {};
22179
22180BRp$3.findHaystackPoints = function (edges) {
22181 for (var i = 0; i < edges.length; i++) {
22182 var edge = edges[i];
22183 var _p = edge._private;
22184 var rs = _p.rscratch;
22185
22186 if (!rs.haystack) {
22187 var angle = Math.random() * 2 * Math.PI;
22188 rs.source = {
22189 x: Math.cos(angle),
22190 y: Math.sin(angle)
22191 };
22192 angle = Math.random() * 2 * Math.PI;
22193 rs.target = {
22194 x: Math.cos(angle),
22195 y: Math.sin(angle)
22196 };
22197 }
22198
22199 var src = _p.source;
22200 var tgt = _p.target;
22201 var srcPos = src.position();
22202 var tgtPos = tgt.position();
22203 var srcW = src.width();
22204 var tgtW = tgt.width();
22205 var srcH = src.height();
22206 var tgtH = tgt.height();
22207 var radius = edge.pstyle('haystack-radius').value;
22208 var halfRadius = radius / 2; // b/c have to half width/height
22209
22210 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];
22211 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22212 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22213
22214 rs.edgeType = 'haystack';
22215 rs.haystack = true;
22216 this.storeEdgeProjections(edge);
22217 this.calculateArrowAngles(edge);
22218 this.recalculateEdgeLabelProjections(edge);
22219 this.calculateLabelAngles(edge);
22220 }
22221};
22222
22223BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22224 // Segments (multiple straight lines)
22225 var rs = edge._private.rscratch;
22226 var posPts = pairInfo.posPts,
22227 intersectionPts = pairInfo.intersectionPts,
22228 vectorNormInverse = pairInfo.vectorNormInverse;
22229 var edgeDistances = edge.pstyle('edge-distances').value;
22230 var segmentWs = edge.pstyle('segment-weights');
22231 var segmentDs = edge.pstyle('segment-distances');
22232 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22233 rs.edgeType = 'segments';
22234 rs.segpts = [];
22235
22236 for (var s = 0; s < segmentsN; s++) {
22237 var w = segmentWs.pfValue[s];
22238 var d = segmentDs.pfValue[s];
22239 var w1 = 1 - w;
22240 var w2 = w;
22241 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22242 var adjustedMidpt = {
22243 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22244 y: midptPts.y1 * w1 + midptPts.y2 * w2
22245 };
22246 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22247 }
22248};
22249
22250BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22251 // Self-edge
22252 var rs = edge._private.rscratch;
22253 var dirCounts = pairInfo.dirCounts,
22254 srcPos = pairInfo.srcPos;
22255 var ctrlptDists = edge.pstyle('control-point-distances');
22256 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22257 var loopDir = edge.pstyle('loop-direction').pfValue;
22258 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22259 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22260 rs.edgeType = 'self';
22261 var j = i;
22262 var loopDist = stepSize;
22263
22264 if (edgeIsUnbundled) {
22265 j = 0;
22266 loopDist = ctrlptDist;
22267 }
22268
22269 var loopAngle = loopDir - Math.PI / 2;
22270 var outAngle = loopAngle - loopSwp / 2;
22271 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22272
22273 var dc = String(loopDir + '_' + loopSwp);
22274 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22275 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)];
22276};
22277
22278BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22279 // Compound edge
22280 var rs = edge._private.rscratch;
22281 rs.edgeType = 'compound';
22282 var srcPos = pairInfo.srcPos,
22283 tgtPos = pairInfo.tgtPos,
22284 srcW = pairInfo.srcW,
22285 srcH = pairInfo.srcH,
22286 tgtW = pairInfo.tgtW,
22287 tgtH = pairInfo.tgtH;
22288 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22289 var ctrlptDists = edge.pstyle('control-point-distances');
22290 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22291 var j = i;
22292 var loopDist = stepSize;
22293
22294 if (edgeIsUnbundled) {
22295 j = 0;
22296 loopDist = ctrlptDist;
22297 }
22298
22299 var loopW = 50;
22300 var loopaPos = {
22301 x: srcPos.x - srcW / 2,
22302 y: srcPos.y - srcH / 2
22303 };
22304 var loopbPos = {
22305 x: tgtPos.x - tgtW / 2,
22306 y: tgtPos.y - tgtH / 2
22307 };
22308 var loopPos = {
22309 x: Math.min(loopaPos.x, loopbPos.x),
22310 y: Math.min(loopaPos.y, loopbPos.y)
22311 }; // avoids cases with impossible beziers
22312
22313 var minCompoundStretch = 0.5;
22314 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22315 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22316 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];
22317};
22318
22319BRp$3.findStraightEdgePoints = function (edge) {
22320 // Straight edge within bundle
22321 edge._private.rscratch.edgeType = 'straight';
22322};
22323
22324BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22325 var rs = edge._private.rscratch;
22326 var vectorNormInverse = pairInfo.vectorNormInverse,
22327 posPts = pairInfo.posPts,
22328 intersectionPts = pairInfo.intersectionPts;
22329 var edgeDistances = edge.pstyle('edge-distances').value;
22330 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22331 var ctrlptDists = edge.pstyle('control-point-distances');
22332 var ctrlptWs = edge.pstyle('control-point-weights');
22333 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22334 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22335 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22336
22337 var multi = edgeIsUnbundled;
22338 rs.edgeType = multi ? 'multibezier' : 'bezier';
22339 rs.ctrlpts = [];
22340
22341 for (var b = 0; b < bezierN; b++) {
22342 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22343 var manctrlptDist = void 0;
22344 var sign = signum(normctrlptDist);
22345
22346 if (multi) {
22347 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22348
22349 ctrlptWeight = ctrlptWs.value[b];
22350 }
22351
22352 if (edgeIsUnbundled) {
22353 // multi or single unbundled
22354 manctrlptDist = ctrlptDist;
22355 } else {
22356 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22357 }
22358
22359 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22360 var w1 = 1 - ctrlptWeight;
22361 var w2 = ctrlptWeight;
22362 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22363 var adjustedMidpt = {
22364 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22365 y: midptPts.y1 * w1 + midptPts.y2 * w2
22366 };
22367 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22368 }
22369};
22370
22371BRp$3.findTaxiPoints = function (edge, pairInfo) {
22372 // Taxicab geometry with two turns maximum
22373 var rs = edge._private.rscratch;
22374 rs.edgeType = 'segments';
22375 var VERTICAL = 'vertical';
22376 var HORIZONTAL = 'horizontal';
22377 var LEFTWARD = 'leftward';
22378 var RIGHTWARD = 'rightward';
22379 var DOWNWARD = 'downward';
22380 var UPWARD = 'upward';
22381 var AUTO = 'auto';
22382 var posPts = pairInfo.posPts,
22383 srcW = pairInfo.srcW,
22384 srcH = pairInfo.srcH,
22385 tgtW = pairInfo.tgtW,
22386 tgtH = pairInfo.tgtH;
22387 var edgeDistances = edge.pstyle('edge-distances').value;
22388 var dIncludesNodeBody = edgeDistances !== 'node-position';
22389 var taxiDir = edge.pstyle('taxi-direction').value;
22390 var rawTaxiDir = taxiDir; // unprocessed value
22391
22392 var taxiTurn = edge.pstyle('taxi-turn');
22393 var turnIsPercent = taxiTurn.units === '%';
22394 var taxiTurnPfVal = taxiTurn.pfValue;
22395 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22396
22397 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22398 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22399 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22400 var pdx = posPts.x2 - posPts.x1;
22401 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22402
22403 var subDWH = function subDWH(dxy, dwh) {
22404 if (dxy > 0) {
22405 return Math.max(dxy - dwh, 0);
22406 } else {
22407 return Math.min(dxy + dwh, 0);
22408 }
22409 };
22410
22411 var dx = subDWH(pdx, dw);
22412 var dy = subDWH(pdy, dh);
22413 var isExplicitDir = false;
22414
22415 if (rawTaxiDir === AUTO) {
22416 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22417 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22418 taxiDir = VERTICAL;
22419 isExplicitDir = true;
22420 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22421 taxiDir = HORIZONTAL;
22422 isExplicitDir = true;
22423 }
22424
22425 var isVert = taxiDir === VERTICAL;
22426 var l = isVert ? dy : dx;
22427 var pl = isVert ? pdy : pdx;
22428 var sgnL = signum(pl);
22429 var forcedDir = false;
22430
22431 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22432 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22433 sgnL *= -1;
22434 l = sgnL * Math.abs(l);
22435 forcedDir = true;
22436 }
22437
22438 var d;
22439
22440 if (turnIsPercent) {
22441 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22442 d = p * l;
22443 } else {
22444 var k = taxiTurnPfVal < 0 ? l : 0;
22445 d = k + taxiTurnPfVal * sgnL;
22446 }
22447
22448 var getIsTooClose = function getIsTooClose(d) {
22449 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22450 };
22451
22452 var isTooCloseSrc = getIsTooClose(d);
22453 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22454 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22455
22456 if (isTooClose && !forcedDir) {
22457 // non-ideal routing
22458 if (isVert) {
22459 // vertical fallbacks
22460 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22461 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22462
22463 if (lShapeInsideSrc) {
22464 // horizontal Z-shape (direction not respected)
22465 var x = (posPts.x1 + posPts.x2) / 2;
22466 var y1 = posPts.y1,
22467 y2 = posPts.y2;
22468 rs.segpts = [x, y1, x, y2];
22469 } else if (lShapeInsideTgt) {
22470 // vertical Z-shape (distance not respected)
22471 var y = (posPts.y1 + posPts.y2) / 2;
22472 var x1 = posPts.x1,
22473 x2 = posPts.x2;
22474 rs.segpts = [x1, y, x2, y];
22475 } else {
22476 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22477 rs.segpts = [posPts.x1, posPts.y2];
22478 }
22479 } else {
22480 // horizontal fallbacks
22481 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22482
22483 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22484
22485 if (_lShapeInsideSrc) {
22486 // vertical Z-shape (direction not respected)
22487 var _y = (posPts.y1 + posPts.y2) / 2;
22488
22489 var _x = posPts.x1,
22490 _x2 = posPts.x2;
22491 rs.segpts = [_x, _y, _x2, _y];
22492 } else if (_lShapeInsideTgt) {
22493 // horizontal Z-shape (turn distance not respected)
22494 var _x3 = (posPts.x1 + posPts.x2) / 2;
22495
22496 var _y2 = posPts.y1,
22497 _y3 = posPts.y2;
22498 rs.segpts = [_x3, _y2, _x3, _y3];
22499 } else {
22500 // L-shape (turn distance not respected, but works well for tree siblings)
22501 rs.segpts = [posPts.x2, posPts.y1];
22502 }
22503 }
22504 } else {
22505 // ideal routing
22506 if (isVert) {
22507 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22508
22509 var _x4 = posPts.x1,
22510 _x5 = posPts.x2;
22511 rs.segpts = [_x4, _y4, _x5, _y4];
22512 } else {
22513 // horizontal
22514 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22515
22516 var _y5 = posPts.y1,
22517 _y6 = posPts.y2;
22518 rs.segpts = [_x6, _y5, _x6, _y6];
22519 }
22520 }
22521};
22522
22523BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22524 var rs = edge._private.rscratch; // can only correct beziers for now...
22525
22526 if (rs.edgeType === 'bezier') {
22527 var srcPos = pairInfo.srcPos,
22528 tgtPos = pairInfo.tgtPos,
22529 srcW = pairInfo.srcW,
22530 srcH = pairInfo.srcH,
22531 tgtW = pairInfo.tgtW,
22532 tgtH = pairInfo.tgtH,
22533 srcShape = pairInfo.srcShape,
22534 tgtShape = pairInfo.tgtShape;
22535 var badStart = !number(rs.startX) || !number(rs.startY);
22536 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22537 var badEnd = !number(rs.endX) || !number(rs.endY);
22538 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22539 var minCpADistFactor = 3;
22540 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22541 var minCpADist = minCpADistFactor * arrowW;
22542 var startACpDist = dist({
22543 x: rs.ctrlpts[0],
22544 y: rs.ctrlpts[1]
22545 }, {
22546 x: rs.startX,
22547 y: rs.startY
22548 });
22549 var closeStartACp = startACpDist < minCpADist;
22550 var endACpDist = dist({
22551 x: rs.ctrlpts[0],
22552 y: rs.ctrlpts[1]
22553 }, {
22554 x: rs.endX,
22555 y: rs.endY
22556 });
22557 var closeEndACp = endACpDist < minCpADist;
22558 var overlapping = false;
22559
22560 if (badStart || badAStart || closeStartACp) {
22561 overlapping = true; // project control point along line from src centre to outside the src shape
22562 // (otherwise intersection will yield nothing)
22563
22564 var cpD = {
22565 // delta
22566 x: rs.ctrlpts[0] - srcPos.x,
22567 y: rs.ctrlpts[1] - srcPos.y
22568 };
22569 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22570
22571 var cpM = {
22572 // normalised delta
22573 x: cpD.x / cpL,
22574 y: cpD.y / cpL
22575 };
22576 var radius = Math.max(srcW, srcH);
22577 var cpProj = {
22578 // *2 radius guarantees outside shape
22579 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22580 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22581 };
22582 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22583
22584 if (closeStartACp) {
22585 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22586 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22587 } else {
22588 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22589 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22590 }
22591 }
22592
22593 if (badEnd || badAEnd || closeEndACp) {
22594 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22595 // (otherwise intersection will yield nothing)
22596
22597 var _cpD = {
22598 // delta
22599 x: rs.ctrlpts[0] - tgtPos.x,
22600 y: rs.ctrlpts[1] - tgtPos.y
22601 };
22602
22603 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22604
22605
22606 var _cpM = {
22607 // normalised delta
22608 x: _cpD.x / _cpL,
22609 y: _cpD.y / _cpL
22610 };
22611
22612 var _radius = Math.max(srcW, srcH);
22613
22614 var _cpProj = {
22615 // *2 radius guarantees outside shape
22616 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22617 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22618 };
22619 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22620
22621 if (closeEndACp) {
22622 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22623 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22624 } else {
22625 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22626 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22627 }
22628 }
22629
22630 if (overlapping) {
22631 // recalc endpts
22632 this.findEndpoints(edge);
22633 }
22634 }
22635};
22636
22637BRp$3.storeAllpts = function (edge) {
22638 var rs = edge._private.rscratch;
22639
22640 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22641 rs.allpts = [];
22642 rs.allpts.push(rs.startX, rs.startY);
22643
22644 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22645 // ctrl pt itself
22646 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22647
22648 if (b + 3 < rs.ctrlpts.length) {
22649 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22650 }
22651 }
22652
22653 rs.allpts.push(rs.endX, rs.endY);
22654 var m, mt;
22655
22656 if (rs.ctrlpts.length / 2 % 2 === 0) {
22657 m = rs.allpts.length / 2 - 1;
22658 rs.midX = rs.allpts[m];
22659 rs.midY = rs.allpts[m + 1];
22660 } else {
22661 m = rs.allpts.length / 2 - 3;
22662 mt = 0.5;
22663 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22664 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22665 }
22666 } else if (rs.edgeType === 'straight') {
22667 // need to calc these after endpts
22668 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22669
22670 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22671 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22672 } else if (rs.edgeType === 'segments') {
22673 rs.allpts = [];
22674 rs.allpts.push(rs.startX, rs.startY);
22675 rs.allpts.push.apply(rs.allpts, rs.segpts);
22676 rs.allpts.push(rs.endX, rs.endY);
22677
22678 if (rs.segpts.length % 4 === 0) {
22679 var i2 = rs.segpts.length / 2;
22680 var i1 = i2 - 2;
22681 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22682 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22683 } else {
22684 var _i = rs.segpts.length / 2 - 1;
22685
22686 rs.midX = rs.segpts[_i];
22687 rs.midY = rs.segpts[_i + 1];
22688 }
22689 }
22690};
22691
22692BRp$3.checkForInvalidEdgeWarning = function (edge) {
22693 var rs = edge[0]._private.rscratch;
22694
22695 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22696 rs.loggedErr = false;
22697 } else {
22698 if (!rs.loggedErr) {
22699 rs.loggedErr = true;
22700 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.');
22701 }
22702 }
22703};
22704
22705BRp$3.findEdgeControlPoints = function (edges) {
22706 var _this = this;
22707
22708 if (!edges || edges.length === 0) {
22709 return;
22710 }
22711
22712 var r = this;
22713 var cy = r.cy;
22714 var hasCompounds = cy.hasCompoundNodes();
22715 var hashTable = {
22716 map: new Map$1(),
22717 get: function get(pairId) {
22718 var map2 = this.map.get(pairId[0]);
22719
22720 if (map2 != null) {
22721 return map2.get(pairId[1]);
22722 } else {
22723 return null;
22724 }
22725 },
22726 set: function set(pairId, val) {
22727 var map2 = this.map.get(pairId[0]);
22728
22729 if (map2 == null) {
22730 map2 = new Map$1();
22731 this.map.set(pairId[0], map2);
22732 }
22733
22734 map2.set(pairId[1], val);
22735 }
22736 };
22737 var pairIds = [];
22738 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22739
22740 for (var i = 0; i < edges.length; i++) {
22741 var edge = edges[i];
22742 var _p = edge._private;
22743 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22744 // they shouldn't take up space
22745
22746 if (edge.removed() || !edge.takesUpSpace()) {
22747 continue;
22748 }
22749
22750 if (curveStyle === 'haystack') {
22751 haystackEdges.push(edge);
22752 continue;
22753 }
22754
22755 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle === 'taxi';
22756 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22757 var src = _p.source;
22758 var tgt = _p.target;
22759 var srcIndex = src.poolIndex();
22760 var tgtIndex = tgt.poolIndex();
22761 var pairId = [srcIndex, tgtIndex].sort();
22762 var tableEntry = hashTable.get(pairId);
22763
22764 if (tableEntry == null) {
22765 tableEntry = {
22766 eles: []
22767 };
22768 hashTable.set(pairId, tableEntry);
22769 pairIds.push(pairId);
22770 }
22771
22772 tableEntry.eles.push(edge);
22773
22774 if (edgeIsUnbundled) {
22775 tableEntry.hasUnbundled = true;
22776 }
22777
22778 if (edgeIsBezier) {
22779 tableEntry.hasBezier = true;
22780 }
22781 } // for each pair (src, tgt), create the ctrl pts
22782 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22783
22784
22785 var _loop = function _loop(p) {
22786 var pairId = pairIds[p];
22787 var pairInfo = hashTable.get(pairId);
22788 var swappedpairInfo = void 0;
22789
22790 if (!pairInfo.hasUnbundled) {
22791 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22792 return e.isBundledBezier();
22793 });
22794 clearArray(pairInfo.eles);
22795 pllEdges.forEach(function (edge) {
22796 return pairInfo.eles.push(edge);
22797 }); // for each pair id, the edges should be sorted by index
22798
22799 pairInfo.eles.sort(function (edge1, edge2) {
22800 return edge1.poolIndex() - edge2.poolIndex();
22801 });
22802 }
22803
22804 var firstEdge = pairInfo.eles[0];
22805 var src = firstEdge.source();
22806 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22807
22808 if (src.poolIndex() > tgt.poolIndex()) {
22809 var temp = src;
22810 src = tgt;
22811 tgt = temp;
22812 }
22813
22814 var srcPos = pairInfo.srcPos = src.position();
22815 var tgtPos = pairInfo.tgtPos = tgt.position();
22816 var srcW = pairInfo.srcW = src.outerWidth();
22817 var srcH = pairInfo.srcH = src.outerHeight();
22818 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22819 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22820
22821 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22822
22823 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22824
22825 pairInfo.dirCounts = {
22826 'north': 0,
22827 'west': 0,
22828 'south': 0,
22829 'east': 0,
22830 'northwest': 0,
22831 'southwest': 0,
22832 'northeast': 0,
22833 'southeast': 0
22834 };
22835
22836 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22837 var _edge = pairInfo.eles[_i2];
22838 var rs = _edge[0]._private.rscratch;
22839
22840 var _curveStyle = _edge.pstyle('curve-style').value;
22841
22842 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22843
22844
22845 var edgeIsSwapped = !src.same(_edge.source());
22846
22847 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22848 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22849
22850 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22851 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22852
22853 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22854 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22855 var intersectionPts = pairInfo.intersectionPts = {
22856 x1: srcOutside[0],
22857 x2: tgtOutside[0],
22858 y1: srcOutside[1],
22859 y2: tgtOutside[1]
22860 };
22861 var posPts = pairInfo.posPts = {
22862 x1: srcPos.x,
22863 x2: tgtPos.x,
22864 y1: srcPos.y,
22865 y2: tgtPos.y
22866 };
22867 var dy = tgtOutside[1] - srcOutside[1];
22868 var dx = tgtOutside[0] - srcOutside[0];
22869 var l = Math.sqrt(dx * dx + dy * dy);
22870 var vector = pairInfo.vector = {
22871 x: dx,
22872 y: dy
22873 };
22874 var vectorNorm = pairInfo.vectorNorm = {
22875 x: vector.x / l,
22876 y: vector.y / l
22877 };
22878 var vectorNormInverse = {
22879 x: -vectorNorm.y,
22880 y: vectorNorm.x
22881 }; // if node shapes overlap, then no ctrl pts to draw
22882
22883 pairInfo.nodesOverlap = !number(l) || tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y) || srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y);
22884 pairInfo.vectorNormInverse = vectorNormInverse;
22885 swappedpairInfo = {
22886 nodesOverlap: pairInfo.nodesOverlap,
22887 dirCounts: pairInfo.dirCounts,
22888 calculatedIntersection: true,
22889 hasBezier: pairInfo.hasBezier,
22890 hasUnbundled: pairInfo.hasUnbundled,
22891 eles: pairInfo.eles,
22892 srcPos: tgtPos,
22893 tgtPos: srcPos,
22894 srcW: tgtW,
22895 srcH: tgtH,
22896 tgtW: srcW,
22897 tgtH: srcH,
22898 srcIntn: tgtIntn,
22899 tgtIntn: srcIntn,
22900 srcShape: tgtShape,
22901 tgtShape: srcShape,
22902 posPts: {
22903 x1: posPts.x2,
22904 y1: posPts.y2,
22905 x2: posPts.x1,
22906 y2: posPts.y1
22907 },
22908 intersectionPts: {
22909 x1: intersectionPts.x2,
22910 y1: intersectionPts.y2,
22911 x2: intersectionPts.x1,
22912 y2: intersectionPts.y1
22913 },
22914 vector: {
22915 x: -vector.x,
22916 y: -vector.y
22917 },
22918 vectorNorm: {
22919 x: -vectorNorm.x,
22920 y: -vectorNorm.y
22921 },
22922 vectorNormInverse: {
22923 x: -vectorNormInverse.x,
22924 y: -vectorNormInverse.y
22925 }
22926 };
22927 }
22928
22929 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22930 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22931 rs.srcIntn = passedPairInfo.srcIntn;
22932 rs.tgtIntn = passedPairInfo.tgtIntn;
22933
22934 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22935 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22936 } else if (src === tgt) {
22937 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22938 } else if (_curveStyle === 'segments') {
22939 _this.findSegmentsPoints(_edge, passedPairInfo);
22940 } else if (_curveStyle === 'taxi') {
22941 _this.findTaxiPoints(_edge, passedPairInfo);
22942 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22943 _this.findStraightEdgePoints(_edge);
22944 } else {
22945 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22946 }
22947
22948 _this.findEndpoints(_edge);
22949
22950 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22951
22952 _this.checkForInvalidEdgeWarning(_edge);
22953
22954 _this.storeAllpts(_edge);
22955
22956 _this.storeEdgeProjections(_edge);
22957
22958 _this.calculateArrowAngles(_edge);
22959
22960 _this.recalculateEdgeLabelProjections(_edge);
22961
22962 _this.calculateLabelAngles(_edge);
22963 } // for pair edges
22964
22965 };
22966
22967 for (var p = 0; p < pairIds.length; p++) {
22968 _loop(p);
22969 } // for pair ids
22970 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22971
22972
22973 this.findHaystackPoints(haystackEdges);
22974};
22975
22976function getPts(pts) {
22977 var retPts = [];
22978
22979 if (pts == null) {
22980 return;
22981 }
22982
22983 for (var i = 0; i < pts.length; i += 2) {
22984 var x = pts[i];
22985 var y = pts[i + 1];
22986 retPts.push({
22987 x: x,
22988 y: y
22989 });
22990 }
22991
22992 return retPts;
22993}
22994
22995BRp$3.getSegmentPoints = function (edge) {
22996 var rs = edge[0]._private.rscratch;
22997 var type = rs.edgeType;
22998
22999 if (type === 'segments') {
23000 this.recalculateRenderedStyle(edge);
23001 return getPts(rs.segpts);
23002 }
23003};
23004
23005BRp$3.getControlPoints = function (edge) {
23006 var rs = edge[0]._private.rscratch;
23007 var type = rs.edgeType;
23008
23009 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23010 this.recalculateRenderedStyle(edge);
23011 return getPts(rs.ctrlpts);
23012 }
23013};
23014
23015BRp$3.getEdgeMidpoint = function (edge) {
23016 var rs = edge[0]._private.rscratch;
23017 this.recalculateRenderedStyle(edge);
23018 return {
23019 x: rs.midX,
23020 y: rs.midY
23021 };
23022};
23023
23024var BRp$4 = {};
23025
23026BRp$4.manualEndptToPx = function (node, prop) {
23027 var r = this;
23028 var npos = node.position();
23029 var w = node.outerWidth();
23030 var h = node.outerHeight();
23031
23032 if (prop.value.length === 2) {
23033 var p = [prop.pfValue[0], prop.pfValue[1]];
23034
23035 if (prop.units[0] === '%') {
23036 p[0] = p[0] * w;
23037 }
23038
23039 if (prop.units[1] === '%') {
23040 p[1] = p[1] * h;
23041 }
23042
23043 p[0] += npos.x;
23044 p[1] += npos.y;
23045 return p;
23046 } else {
23047 var angle = prop.pfValue[0];
23048 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23049
23050 var l = 2 * Math.max(w, h);
23051 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23052 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23053 }
23054};
23055
23056BRp$4.findEndpoints = function (edge) {
23057 var r = this;
23058 var intersect;
23059 var source = edge.source()[0];
23060 var target = edge.target()[0];
23061 var srcPos = source.position();
23062 var tgtPos = target.position();
23063 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23064 var srcArShape = edge.pstyle('source-arrow-shape').value;
23065 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23066 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23067 var curveStyle = edge.pstyle('curve-style').value;
23068 var rs = edge._private.rscratch;
23069 var et = rs.edgeType;
23070 var taxi = curveStyle === 'taxi';
23071 var self = et === 'self' || et === 'compound';
23072 var bezier = et === 'bezier' || et === 'multibezier' || self;
23073 var multi = et !== 'bezier';
23074 var lines = et === 'straight' || et === 'segments';
23075 var segments = et === 'segments';
23076 var hasEndpts = bezier || multi || lines;
23077 var overrideEndpts = self || taxi;
23078 var srcManEndpt = edge.pstyle('source-endpoint');
23079 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23080 var tgtManEndpt = edge.pstyle('target-endpoint');
23081 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23082 rs.srcManEndpt = srcManEndpt;
23083 rs.tgtManEndpt = tgtManEndpt;
23084 var p1; // last known point of edge on target side
23085
23086 var p2; // last known point of edge on source side
23087
23088 var p1_i; // point to intersect with target shape
23089
23090 var p2_i; // point to intersect with source shape
23091
23092 if (bezier) {
23093 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23094 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23095 p1 = cpEnd;
23096 p2 = cpStart;
23097 } else if (lines) {
23098 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23099 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23100 p1 = tgtArrowFromPt;
23101 p2 = srcArrowFromPt;
23102 }
23103
23104 if (tgtManEndptVal === 'inside-to-node') {
23105 intersect = [tgtPos.x, tgtPos.y];
23106 } else if (tgtManEndpt.units) {
23107 intersect = this.manualEndptToPx(target, tgtManEndpt);
23108 } else if (tgtManEndptVal === 'outside-to-line') {
23109 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23110 } else {
23111 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23112 p1_i = p1;
23113 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23114 p1_i = [srcPos.x, srcPos.y];
23115 }
23116
23117 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23118
23119 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23120 var trs = target._private.rscratch;
23121 var lw = trs.labelWidth;
23122 var lh = trs.labelHeight;
23123 var lx = trs.labelX;
23124 var ly = trs.labelY;
23125 var lw2 = lw / 2;
23126 var lh2 = lh / 2;
23127 var va = target.pstyle('text-valign').value;
23128
23129 if (va === 'top') {
23130 ly -= lh2;
23131 } else if (va === 'bottom') {
23132 ly += lh2;
23133 }
23134
23135 var ha = target.pstyle('text-halign').value;
23136
23137 if (ha === 'left') {
23138 lx -= lw2;
23139 } else if (ha === 'right') {
23140 lx += lw2;
23141 }
23142
23143 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);
23144
23145 if (labelIntersect.length > 0) {
23146 var refPt = srcPos;
23147 var intSqdist = sqdist(refPt, array2point(intersect));
23148 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23149 var minSqDist = intSqdist;
23150
23151 if (labIntSqdist < intSqdist) {
23152 intersect = labelIntersect;
23153 minSqDist = labIntSqdist;
23154 }
23155
23156 if (labelIntersect.length > 2) {
23157 var labInt2SqDist = sqdist(refPt, {
23158 x: labelIntersect[2],
23159 y: labelIntersect[3]
23160 });
23161
23162 if (labInt2SqDist < minSqDist) {
23163 intersect = [labelIntersect[2], labelIntersect[3]];
23164 }
23165 }
23166 }
23167 }
23168 }
23169
23170 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23171 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23172 rs.endX = edgeEnd[0];
23173 rs.endY = edgeEnd[1];
23174 rs.arrowEndX = arrowEnd[0];
23175 rs.arrowEndY = arrowEnd[1];
23176
23177 if (srcManEndptVal === 'inside-to-node') {
23178 intersect = [srcPos.x, srcPos.y];
23179 } else if (srcManEndpt.units) {
23180 intersect = this.manualEndptToPx(source, srcManEndpt);
23181 } else if (srcManEndptVal === 'outside-to-line') {
23182 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23183 } else {
23184 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23185 p2_i = p2;
23186 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23187 p2_i = [tgtPos.x, tgtPos.y];
23188 }
23189
23190 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23191
23192 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23193 var srs = source._private.rscratch;
23194 var _lw = srs.labelWidth;
23195 var _lh = srs.labelHeight;
23196 var _lx = srs.labelX;
23197 var _ly = srs.labelY;
23198
23199 var _lw2 = _lw / 2;
23200
23201 var _lh2 = _lh / 2;
23202
23203 var _va = source.pstyle('text-valign').value;
23204
23205 if (_va === 'top') {
23206 _ly -= _lh2;
23207 } else if (_va === 'bottom') {
23208 _ly += _lh2;
23209 }
23210
23211 var _ha = source.pstyle('text-halign').value;
23212
23213 if (_ha === 'left') {
23214 _lx -= _lw2;
23215 } else if (_ha === 'right') {
23216 _lx += _lw2;
23217 }
23218
23219 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);
23220
23221 if (_labelIntersect.length > 0) {
23222 var _refPt = tgtPos;
23223
23224 var _intSqdist = sqdist(_refPt, array2point(intersect));
23225
23226 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23227
23228 var _minSqDist = _intSqdist;
23229
23230 if (_labIntSqdist < _intSqdist) {
23231 intersect = [_labelIntersect[0], _labelIntersect[1]];
23232 _minSqDist = _labIntSqdist;
23233 }
23234
23235 if (_labelIntersect.length > 2) {
23236 var _labInt2SqDist = sqdist(_refPt, {
23237 x: _labelIntersect[2],
23238 y: _labelIntersect[3]
23239 });
23240
23241 if (_labInt2SqDist < _minSqDist) {
23242 intersect = [_labelIntersect[2], _labelIntersect[3]];
23243 }
23244 }
23245 }
23246 }
23247 }
23248
23249 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23250 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23251 rs.startX = edgeStart[0];
23252 rs.startY = edgeStart[1];
23253 rs.arrowStartX = arrowStart[0];
23254 rs.arrowStartY = arrowStart[1];
23255
23256 if (hasEndpts) {
23257 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23258 rs.badLine = true;
23259 } else {
23260 rs.badLine = false;
23261 }
23262 }
23263};
23264
23265BRp$4.getSourceEndpoint = function (edge) {
23266 var rs = edge[0]._private.rscratch;
23267 this.recalculateRenderedStyle(edge);
23268
23269 switch (rs.edgeType) {
23270 case 'haystack':
23271 return {
23272 x: rs.haystackPts[0],
23273 y: rs.haystackPts[1]
23274 };
23275
23276 default:
23277 return {
23278 x: rs.arrowStartX,
23279 y: rs.arrowStartY
23280 };
23281 }
23282};
23283
23284BRp$4.getTargetEndpoint = function (edge) {
23285 var rs = edge[0]._private.rscratch;
23286 this.recalculateRenderedStyle(edge);
23287
23288 switch (rs.edgeType) {
23289 case 'haystack':
23290 return {
23291 x: rs.haystackPts[2],
23292 y: rs.haystackPts[3]
23293 };
23294
23295 default:
23296 return {
23297 x: rs.arrowEndX,
23298 y: rs.arrowEndY
23299 };
23300 }
23301};
23302
23303var BRp$5 = {};
23304
23305function pushBezierPts(r, edge, pts) {
23306 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23307 return qbezierAt(p1, p2, p3, t);
23308 };
23309
23310 var _p = edge._private;
23311 var bpts = _p.rstyle.bezierPts;
23312
23313 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23314 var p = r.bezierProjPcts[i];
23315 bpts.push({
23316 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23317 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23318 });
23319 }
23320}
23321
23322BRp$5.storeEdgeProjections = function (edge) {
23323 var _p = edge._private;
23324 var rs = _p.rscratch;
23325 var et = rs.edgeType; // clear the cached points state
23326
23327 _p.rstyle.bezierPts = null;
23328 _p.rstyle.linePts = null;
23329 _p.rstyle.haystackPts = null;
23330
23331 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23332 _p.rstyle.bezierPts = [];
23333
23334 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23335 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23336 }
23337 } else if (et === 'segments') {
23338 var lpts = _p.rstyle.linePts = [];
23339
23340 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23341 lpts.push({
23342 x: rs.allpts[i],
23343 y: rs.allpts[i + 1]
23344 });
23345 }
23346 } else if (et === 'haystack') {
23347 var hpts = rs.haystackPts;
23348 _p.rstyle.haystackPts = [{
23349 x: hpts[0],
23350 y: hpts[1]
23351 }, {
23352 x: hpts[2],
23353 y: hpts[3]
23354 }];
23355 }
23356
23357 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23358};
23359
23360BRp$5.recalculateEdgeProjections = function (edges) {
23361 this.findEdgeControlPoints(edges);
23362};
23363
23364/* global document */
23365
23366var BRp$6 = {};
23367
23368BRp$6.recalculateNodeLabelProjection = function (node) {
23369 var content = node.pstyle('label').strValue;
23370
23371 if (emptyString(content)) {
23372 return;
23373 }
23374
23375 var textX, textY;
23376 var _p = node._private;
23377 var nodeWidth = node.width();
23378 var nodeHeight = node.height();
23379 var padding = node.padding();
23380 var nodePos = node.position();
23381 var textHalign = node.pstyle('text-halign').strValue;
23382 var textValign = node.pstyle('text-valign').strValue;
23383 var rs = _p.rscratch;
23384 var rstyle = _p.rstyle;
23385
23386 switch (textHalign) {
23387 case 'left':
23388 textX = nodePos.x - nodeWidth / 2 - padding;
23389 break;
23390
23391 case 'right':
23392 textX = nodePos.x + nodeWidth / 2 + padding;
23393 break;
23394
23395 default:
23396 // e.g. center
23397 textX = nodePos.x;
23398 }
23399
23400 switch (textValign) {
23401 case 'top':
23402 textY = nodePos.y - nodeHeight / 2 - padding;
23403 break;
23404
23405 case 'bottom':
23406 textY = nodePos.y + nodeHeight / 2 + padding;
23407 break;
23408
23409 default:
23410 // e.g. middle
23411 textY = nodePos.y;
23412 }
23413
23414 rs.labelX = textX;
23415 rs.labelY = textY;
23416 rstyle.labelX = textX;
23417 rstyle.labelY = textY;
23418 this.calculateLabelAngles(node);
23419 this.applyLabelDimensions(node);
23420};
23421
23422var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23423 var angle = Math.atan(dy / dx);
23424
23425 if (dx === 0 && angle < 0) {
23426 angle = angle * -1;
23427 }
23428
23429 return angle;
23430};
23431
23432var lineAngle = function lineAngle(p0, p1) {
23433 var dx = p1.x - p0.x;
23434 var dy = p1.y - p0.y;
23435 return lineAngleFromDelta(dx, dy);
23436};
23437
23438var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23439 var t0 = bound(0, t - 0.001, 1);
23440 var t1 = bound(0, t + 0.001, 1);
23441 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23442 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23443 return lineAngle(lp0, lp1);
23444};
23445
23446BRp$6.recalculateEdgeLabelProjections = function (edge) {
23447 var p;
23448 var _p = edge._private;
23449 var rs = _p.rscratch;
23450 var r = this;
23451 var content = {
23452 mid: edge.pstyle('label').strValue,
23453 source: edge.pstyle('source-label').strValue,
23454 target: edge.pstyle('target-label').strValue
23455 };
23456
23457 if (content.mid || content.source || content.target) ; else {
23458 return; // no labels => no calcs
23459 } // add center point to style so bounding box calculations can use it
23460 //
23461
23462
23463 p = {
23464 x: rs.midX,
23465 y: rs.midY
23466 };
23467
23468 var setRs = function setRs(propName, prefix, value) {
23469 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23470 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23471 };
23472
23473 setRs('labelX', null, p.x);
23474 setRs('labelY', null, p.y);
23475 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23476 setRs('labelAutoAngle', null, midAngle);
23477
23478 var createControlPointInfo = function createControlPointInfo() {
23479 if (createControlPointInfo.cache) {
23480 return createControlPointInfo.cache;
23481 } // use cache so only 1x per edge
23482
23483
23484 var ctrlpts = []; // store each ctrlpt info init
23485
23486 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23487 var p0 = {
23488 x: rs.allpts[i],
23489 y: rs.allpts[i + 1]
23490 };
23491 var p1 = {
23492 x: rs.allpts[i + 2],
23493 y: rs.allpts[i + 3]
23494 }; // ctrlpt
23495
23496 var p2 = {
23497 x: rs.allpts[i + 4],
23498 y: rs.allpts[i + 5]
23499 };
23500 ctrlpts.push({
23501 p0: p0,
23502 p1: p1,
23503 p2: p2,
23504 startDist: 0,
23505 length: 0,
23506 segments: []
23507 });
23508 }
23509
23510 var bpts = _p.rstyle.bezierPts;
23511 var nProjs = r.bezierProjPcts.length;
23512
23513 function addSegment(cp, p0, p1, t0, t1) {
23514 var length = dist(p0, p1);
23515 var prevSegment = cp.segments[cp.segments.length - 1];
23516 var segment = {
23517 p0: p0,
23518 p1: p1,
23519 t0: t0,
23520 t1: t1,
23521 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23522 length: length
23523 };
23524 cp.segments.push(segment);
23525 cp.length += length;
23526 } // update each ctrlpt with segment info
23527
23528
23529 for (var _i = 0; _i < ctrlpts.length; _i++) {
23530 var cp = ctrlpts[_i];
23531 var prevCp = ctrlpts[_i - 1];
23532
23533 if (prevCp) {
23534 cp.startDist = prevCp.startDist + prevCp.length;
23535 }
23536
23537 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23538
23539 for (var j = 0; j < nProjs - 1; j++) {
23540 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23541 }
23542
23543 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23544 }
23545
23546 return createControlPointInfo.cache = ctrlpts;
23547 };
23548
23549 var calculateEndProjection = function calculateEndProjection(prefix) {
23550 var angle;
23551 var isSrc = prefix === 'source';
23552
23553 if (!content[prefix]) {
23554 return;
23555 }
23556
23557 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23558
23559 switch (rs.edgeType) {
23560 case 'self':
23561 case 'compound':
23562 case 'bezier':
23563 case 'multibezier':
23564 {
23565 var cps = createControlPointInfo();
23566 var selected;
23567 var startDist = 0;
23568 var totalDist = 0; // find the segment we're on
23569
23570 for (var i = 0; i < cps.length; i++) {
23571 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23572
23573 for (var j = 0; j < _cp.segments.length; j++) {
23574 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23575 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23576 startDist = totalDist;
23577 totalDist += _seg.length;
23578
23579 if (totalDist >= offset || lastSeg) {
23580 selected = {
23581 cp: _cp,
23582 segment: _seg
23583 };
23584 break;
23585 }
23586 }
23587
23588 if (selected) {
23589 break;
23590 }
23591 }
23592
23593 var cp = selected.cp;
23594 var seg = selected.segment;
23595 var tSegment = (offset - startDist) / seg.length;
23596 var segDt = seg.t1 - seg.t0;
23597 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23598 t = bound(0, t, 1);
23599 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23600 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23601 break;
23602 }
23603
23604 case 'straight':
23605 case 'segments':
23606 case 'haystack':
23607 {
23608 var d = 0,
23609 di,
23610 d0;
23611 var p0, p1;
23612 var l = rs.allpts.length;
23613
23614 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23615 if (isSrc) {
23616 p0 = {
23617 x: rs.allpts[_i2],
23618 y: rs.allpts[_i2 + 1]
23619 };
23620 p1 = {
23621 x: rs.allpts[_i2 + 2],
23622 y: rs.allpts[_i2 + 3]
23623 };
23624 } else {
23625 p0 = {
23626 x: rs.allpts[l - 2 - _i2],
23627 y: rs.allpts[l - 1 - _i2]
23628 };
23629 p1 = {
23630 x: rs.allpts[l - 4 - _i2],
23631 y: rs.allpts[l - 3 - _i2]
23632 };
23633 }
23634
23635 di = dist(p0, p1);
23636 d0 = d;
23637 d += di;
23638
23639 if (d >= offset) {
23640 break;
23641 }
23642 }
23643
23644 var pD = offset - d0;
23645
23646 var _t = pD / di;
23647
23648 _t = bound(0, _t, 1);
23649 p = lineAt(p0, p1, _t);
23650 angle = lineAngle(p0, p1);
23651 break;
23652 }
23653 }
23654
23655 setRs('labelX', prefix, p.x);
23656 setRs('labelY', prefix, p.y);
23657 setRs('labelAutoAngle', prefix, angle);
23658 };
23659
23660 calculateEndProjection('source');
23661 calculateEndProjection('target');
23662 this.applyLabelDimensions(edge);
23663};
23664
23665BRp$6.applyLabelDimensions = function (ele) {
23666 this.applyPrefixedLabelDimensions(ele);
23667
23668 if (ele.isEdge()) {
23669 this.applyPrefixedLabelDimensions(ele, 'source');
23670 this.applyPrefixedLabelDimensions(ele, 'target');
23671 }
23672};
23673
23674BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23675 var _p = ele._private;
23676 var text = this.getLabelText(ele, prefix);
23677 var labelDims = this.calculateLabelDimensions(ele, text);
23678 var lineHeight = ele.pstyle('line-height').pfValue;
23679 var textWrap = ele.pstyle('text-wrap').strValue;
23680 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23681 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23682 var normPerLineHeight = labelDims.height / numLines;
23683 var labelLineHeight = normPerLineHeight * lineHeight;
23684 var width = labelDims.width;
23685 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23686 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23687 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23688 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23689 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23690 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23691};
23692
23693BRp$6.getLabelText = function (ele, prefix) {
23694 var _p = ele._private;
23695 var pfd = prefix ? prefix + '-' : '';
23696 var text = ele.pstyle(pfd + 'label').strValue;
23697 var textTransform = ele.pstyle('text-transform').value;
23698
23699 var rscratch = function rscratch(propName, value) {
23700 if (value) {
23701 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23702 return value;
23703 } else {
23704 return getPrefixedProperty(_p.rscratch, propName, prefix);
23705 }
23706 }; // for empty text, skip all processing
23707
23708
23709 if (!text) {
23710 return '';
23711 }
23712
23713 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23714 text = text.toUpperCase();
23715 } else if (textTransform == 'lowercase') {
23716 text = text.toLowerCase();
23717 }
23718
23719 var wrapStyle = ele.pstyle('text-wrap').value;
23720
23721 if (wrapStyle === 'wrap') {
23722 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23723
23724 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23725 return rscratch('labelWrapCachedText');
23726 }
23727
23728 var zwsp = "\u200B";
23729 var lines = text.split('\n');
23730 var maxW = ele.pstyle('text-max-width').pfValue;
23731 var overflow = ele.pstyle('text-overflow-wrap').value;
23732 var overflowAny = overflow === 'anywhere';
23733 var wrappedLines = [];
23734 var wordsRegex = /[\s\u200b]+/;
23735 var wordSeparator = overflowAny ? '' : ' ';
23736
23737 for (var l = 0; l < lines.length; l++) {
23738 var line = lines[l];
23739 var lineDims = this.calculateLabelDimensions(ele, line);
23740 var lineW = lineDims.width;
23741
23742 if (overflowAny) {
23743 var processedLine = line.split('').join(zwsp);
23744 line = processedLine;
23745 }
23746
23747 if (lineW > maxW) {
23748 // line is too long
23749 var words = line.split(wordsRegex);
23750 var subline = '';
23751
23752 for (var w = 0; w < words.length; w++) {
23753 var word = words[w];
23754 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23755 var testDims = this.calculateLabelDimensions(ele, testLine);
23756 var testW = testDims.width;
23757
23758 if (testW <= maxW) {
23759 // word fits on current line
23760 subline += word + wordSeparator;
23761 } else {
23762 // word starts new line
23763 if (subline) {
23764 wrappedLines.push(subline);
23765 }
23766
23767 subline = word + wordSeparator;
23768 }
23769 } // if there's remaining text, put it in a wrapped line
23770
23771
23772 if (!subline.match(/^[\s\u200b]+$/)) {
23773 wrappedLines.push(subline);
23774 }
23775 } else {
23776 // line is already short enough
23777 wrappedLines.push(line);
23778 }
23779 } // for
23780
23781
23782 rscratch('labelWrapCachedLines', wrappedLines);
23783 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23784 rscratch('labelWrapKey', labelKey);
23785 } else if (wrapStyle === 'ellipsis') {
23786 var _maxW = ele.pstyle('text-max-width').pfValue;
23787 var ellipsized = '';
23788 var ellipsis = "\u2026";
23789 var incLastCh = false;
23790
23791 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23792 // the label already fits
23793 return text;
23794 }
23795
23796 for (var i = 0; i < text.length; i++) {
23797 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23798
23799 if (widthWithNextCh > _maxW) {
23800 break;
23801 }
23802
23803 ellipsized += text[i];
23804
23805 if (i === text.length - 1) {
23806 incLastCh = true;
23807 }
23808 }
23809
23810 if (!incLastCh) {
23811 ellipsized += ellipsis;
23812 }
23813
23814 return ellipsized;
23815 } // if ellipsize
23816
23817
23818 return text;
23819};
23820
23821BRp$6.getLabelJustification = function (ele) {
23822 var justification = ele.pstyle('text-justification').strValue;
23823 var textHalign = ele.pstyle('text-halign').strValue;
23824
23825 if (justification === 'auto') {
23826 if (ele.isNode()) {
23827 switch (textHalign) {
23828 case 'left':
23829 return 'right';
23830
23831 case 'right':
23832 return 'left';
23833
23834 default:
23835 return 'center';
23836 }
23837 } else {
23838 return 'center';
23839 }
23840 } else {
23841 return justification;
23842 }
23843};
23844
23845BRp$6.calculateLabelDimensions = function (ele, text) {
23846 var r = this;
23847 var cacheKey = hashString(text, ele._private.labelDimsKey);
23848 var cache = r.labelDimCache || (r.labelDimCache = []);
23849 var existingVal = cache[cacheKey];
23850
23851 if (existingVal != null) {
23852 return existingVal;
23853 }
23854
23855 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23856
23857 var fStyle = ele.pstyle('font-style').strValue;
23858 var size = ele.pstyle('font-size').pfValue;
23859 var family = ele.pstyle('font-family').strValue;
23860 var weight = ele.pstyle('font-weight').strValue;
23861 var canvas = this.labelCalcCanvas;
23862 var c2d = this.labelCalcCanvasContext;
23863
23864 if (!canvas) {
23865 canvas = this.labelCalcCanvas = document.createElement('canvas');
23866 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23867 var ds = canvas.style;
23868 ds.position = 'absolute';
23869 ds.left = '-9999px';
23870 ds.top = '-9999px';
23871 ds.zIndex = '-1';
23872 ds.visibility = 'hidden';
23873 ds.pointerEvents = 'none';
23874 }
23875
23876 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23877 var width = 0;
23878 var height = 0;
23879 var lines = text.split('\n');
23880
23881 for (var i = 0; i < lines.length; i++) {
23882 var line = lines[i];
23883 var metrics = c2d.measureText(line);
23884 var w = Math.ceil(metrics.width);
23885 var h = size;
23886 width = Math.max(w, width);
23887 height += h;
23888 }
23889
23890 width += padding;
23891 height += padding;
23892 return cache[cacheKey] = {
23893 width: width,
23894 height: height
23895 };
23896};
23897
23898BRp$6.calculateLabelAngle = function (ele, prefix) {
23899 var _p = ele._private;
23900 var rs = _p.rscratch;
23901 var isEdge = ele.isEdge();
23902 var prefixDash = prefix ? prefix + '-' : '';
23903 var rot = ele.pstyle(prefixDash + 'text-rotation');
23904 var rotStr = rot.strValue;
23905
23906 if (rotStr === 'none') {
23907 return 0;
23908 } else if (isEdge && rotStr === 'autorotate') {
23909 return rs.labelAutoAngle;
23910 } else if (rotStr === 'autorotate') {
23911 return 0;
23912 } else {
23913 return rot.pfValue;
23914 }
23915};
23916
23917BRp$6.calculateLabelAngles = function (ele) {
23918 var r = this;
23919 var isEdge = ele.isEdge();
23920 var _p = ele._private;
23921 var rs = _p.rscratch;
23922 rs.labelAngle = r.calculateLabelAngle(ele);
23923
23924 if (isEdge) {
23925 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23926 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23927 }
23928};
23929
23930var BRp$7 = {};
23931var TOO_SMALL_CUT_RECT = 28;
23932var warnedCutRect = false;
23933
23934BRp$7.getNodeShape = function (node) {
23935 var r = this;
23936 var shape = node.pstyle('shape').value;
23937
23938 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23939 if (!warnedCutRect) {
23940 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23941 warnedCutRect = true;
23942 }
23943
23944 return 'rectangle';
23945 }
23946
23947 if (node.isParent()) {
23948 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23949 return shape;
23950 } else {
23951 return 'rectangle';
23952 }
23953 }
23954
23955 if (shape === 'polygon') {
23956 var points = node.pstyle('shape-polygon-points').value;
23957 return r.nodeShapes.makePolygon(points).name;
23958 }
23959
23960 return shape;
23961};
23962
23963var BRp$8 = {};
23964
23965BRp$8.registerCalculationListeners = function () {
23966 var cy = this.cy;
23967 var elesToUpdate = cy.collection();
23968 var r = this;
23969
23970 var enqueue = function enqueue(eles) {
23971 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23972 elesToUpdate.merge(eles);
23973
23974 if (dirtyStyleCaches) {
23975 for (var i = 0; i < eles.length; i++) {
23976 var ele = eles[i];
23977 var _p = ele._private;
23978 var rstyle = _p.rstyle;
23979 rstyle.clean = false;
23980 rstyle.cleanConnected = false;
23981 }
23982 }
23983 };
23984
23985 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23986 var ele = e.target;
23987 enqueue(ele);
23988 }).on('style.* background.*', function onDirtyStyle(e) {
23989 var ele = e.target;
23990 enqueue(ele, false);
23991 });
23992
23993 var updateEleCalcs = function updateEleCalcs(willDraw) {
23994 if (willDraw) {
23995 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
23996 // before calculating rendered style (and pstyle might not be called yet)
23997
23998 elesToUpdate.cleanStyle();
23999
24000 for (var i = 0; i < elesToUpdate.length; i++) {
24001 var ele = elesToUpdate[i];
24002 var rstyle = ele._private.rstyle;
24003
24004 if (ele.isNode() && !rstyle.cleanConnected) {
24005 enqueue(ele.connectedEdges());
24006 rstyle.cleanConnected = true;
24007 }
24008 }
24009
24010 if (fns) {
24011 for (var _i = 0; _i < fns.length; _i++) {
24012 var fn = fns[_i];
24013 fn(willDraw, elesToUpdate);
24014 }
24015 }
24016
24017 r.recalculateRenderedStyle(elesToUpdate);
24018 elesToUpdate = cy.collection();
24019 }
24020 };
24021
24022 r.flushRenderedStyleQueue = function () {
24023 updateEleCalcs(true);
24024 };
24025
24026 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24027};
24028
24029BRp$8.onUpdateEleCalcs = function (fn) {
24030 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24031 fns.push(fn);
24032};
24033
24034BRp$8.recalculateRenderedStyle = function (eles, useCache) {
24035 var isCleanConnected = function isCleanConnected(ele) {
24036 return ele._private.rstyle.cleanConnected;
24037 };
24038
24039 var edges = [];
24040 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24041
24042 if (this.destroyed) {
24043 return;
24044 } // use cache by default for perf
24045
24046
24047 if (useCache === undefined) {
24048 useCache = true;
24049 }
24050
24051 for (var i = 0; i < eles.length; i++) {
24052 var ele = eles[i];
24053 var _p = ele._private;
24054 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24055 // (and a request for recalc may come in between frames)
24056
24057 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24058 rstyle.clean = false;
24059 } // only update if dirty and in graph
24060
24061
24062 if (useCache && rstyle.clean || ele.removed()) {
24063 continue;
24064 } // only update if not display: none
24065
24066
24067 if (ele.pstyle('display').value === 'none') {
24068 continue;
24069 }
24070
24071 if (_p.group === 'nodes') {
24072 nodes.push(ele);
24073 } else {
24074 // edges
24075 edges.push(ele);
24076 }
24077
24078 rstyle.clean = true;
24079 } // update node data from projections
24080
24081
24082 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24083 var _ele = nodes[_i2];
24084 var _p2 = _ele._private;
24085 var _rstyle = _p2.rstyle;
24086
24087 var pos = _ele.position();
24088
24089 this.recalculateNodeLabelProjection(_ele);
24090 _rstyle.nodeX = pos.x;
24091 _rstyle.nodeY = pos.y;
24092 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24093 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24094 }
24095
24096 this.recalculateEdgeProjections(edges); // update edge data from projections
24097
24098 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24099 var _ele2 = edges[_i3];
24100 var _p3 = _ele2._private;
24101 var _rstyle2 = _p3.rstyle;
24102 var rs = _p3.rscratch; // update rstyle positions
24103
24104 _rstyle2.srcX = rs.arrowStartX;
24105 _rstyle2.srcY = rs.arrowStartY;
24106 _rstyle2.tgtX = rs.arrowEndX;
24107 _rstyle2.tgtY = rs.arrowEndY;
24108 _rstyle2.midX = rs.midX;
24109 _rstyle2.midY = rs.midY;
24110 _rstyle2.labelAngle = rs.labelAngle;
24111 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24112 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24113 }
24114};
24115
24116var BRp$9 = {};
24117
24118BRp$9.updateCachedGrabbedEles = function () {
24119 var eles = this.cachedZSortedEles;
24120
24121 if (!eles) {
24122 // just let this be recalculated on the next z sort tick
24123 return;
24124 }
24125
24126 eles.drag = [];
24127 eles.nondrag = [];
24128 var grabTargets = [];
24129
24130 for (var i = 0; i < eles.length; i++) {
24131 var ele = eles[i];
24132 var rs = ele._private.rscratch;
24133
24134 if (ele.grabbed() && !ele.isParent()) {
24135 grabTargets.push(ele);
24136 } else if (rs.inDragLayer) {
24137 eles.drag.push(ele);
24138 } else {
24139 eles.nondrag.push(ele);
24140 }
24141 } // put the grab target nodes last so it's on top of its neighbourhood
24142
24143
24144 for (var i = 0; i < grabTargets.length; i++) {
24145 var ele = grabTargets[i];
24146 eles.drag.push(ele);
24147 }
24148};
24149
24150BRp$9.invalidateCachedZSortedEles = function () {
24151 this.cachedZSortedEles = null;
24152};
24153
24154BRp$9.getCachedZSortedEles = function (forceRecalc) {
24155 if (forceRecalc || !this.cachedZSortedEles) {
24156 var eles = this.cy.mutableElements().toArray();
24157 eles.sort(zIndexSort);
24158 eles.interactive = eles.filter(function (ele) {
24159 return ele.interactive();
24160 });
24161 this.cachedZSortedEles = eles;
24162 this.updateCachedGrabbedEles();
24163 } else {
24164 eles = this.cachedZSortedEles;
24165 }
24166
24167 return eles;
24168};
24169
24170var BRp$a = {};
24171[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24172 extend(BRp$a, props);
24173});
24174
24175var BRp$b = {};
24176
24177BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24178 var r = this;
24179 var imageCache = r.imageCache = r.imageCache || {};
24180 var cache = imageCache[url];
24181
24182 if (cache) {
24183 if (!cache.image.complete) {
24184 cache.image.addEventListener('load', onLoad);
24185 }
24186
24187 return cache.image;
24188 } else {
24189 cache = imageCache[url] = imageCache[url] || {};
24190 var image = cache.image = new Image(); // eslint-disable-line no-undef
24191
24192 image.addEventListener('load', onLoad);
24193 image.addEventListener('error', function () {
24194 image.error = true;
24195 }); // #1582 safari doesn't load data uris with crossOrigin properly
24196 // https://bugs.webkit.org/show_bug.cgi?id=123978
24197
24198 var dataUriPrefix = 'data:';
24199 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24200
24201 if (!isDataUri) {
24202 image.crossOrigin = crossOrigin; // prevent tainted canvas
24203 }
24204
24205 image.src = url;
24206 return image;
24207 }
24208};
24209
24210var BRp$c = {};
24211/* global document, window, ResizeObserver, MutationObserver */
24212
24213BRp$c.registerBinding = function (target, event, handler, useCapture) {
24214 // eslint-disable-line no-unused-vars
24215 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24216
24217 var b = this.binder(target);
24218 return b.on.apply(b, args);
24219};
24220
24221BRp$c.binder = function (tgt) {
24222 var r = this;
24223 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24224
24225 if (r.supportsPassiveEvents == null) {
24226 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24227 var supportsPassive = false;
24228
24229 try {
24230 var opts = Object.defineProperty({}, 'passive', {
24231 get: function get() {
24232 supportsPassive = true;
24233 return true;
24234 }
24235 });
24236 window.addEventListener('test', null, opts);
24237 } catch (err) {// not supported
24238 }
24239
24240 r.supportsPassiveEvents = supportsPassive;
24241 }
24242
24243 var on = function on(event, handler, useCapture) {
24244 var args = Array.prototype.slice.call(arguments);
24245
24246 if (tgtIsDom && r.supportsPassiveEvents) {
24247 // replace useCapture w/ opts obj
24248 args[2] = {
24249 capture: useCapture != null ? useCapture : false,
24250 passive: false,
24251 once: false
24252 };
24253 }
24254
24255 r.bindings.push({
24256 target: tgt,
24257 args: args
24258 });
24259 (tgt.addEventListener || tgt.on).apply(tgt, args);
24260 return this;
24261 };
24262
24263 return {
24264 on: on,
24265 addEventListener: on,
24266 addListener: on,
24267 bind: on
24268 };
24269};
24270
24271BRp$c.nodeIsDraggable = function (node) {
24272 return node && node.isNode() && !node.locked() && node.grabbable();
24273};
24274
24275BRp$c.nodeIsGrabbable = function (node) {
24276 return this.nodeIsDraggable(node) && node.interactive();
24277};
24278
24279BRp$c.load = function () {
24280 var r = this;
24281
24282 var isSelected = function isSelected(ele) {
24283 return ele.selected();
24284 };
24285
24286 var triggerEvents = function triggerEvents(target, names, e, position) {
24287 if (target == null) {
24288 target = r.cy;
24289 }
24290
24291 for (var i = 0; i < names.length; i++) {
24292 var name = names[i];
24293 target.emit({
24294 originalEvent: e,
24295 type: name,
24296 position: position
24297 });
24298 }
24299 };
24300
24301 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24302 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24303 };
24304
24305 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24306 var allowPassthrough = true;
24307
24308 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24309 // a grabbable compound node below the ele => no passthrough panning
24310 for (var i = 0; downs && i < downs.length; i++) {
24311 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24312
24313 if (down.isNode() && down.isParent() && !down.pannable()) {
24314 allowPassthrough = false;
24315 break;
24316 }
24317 }
24318 } else {
24319 allowPassthrough = true;
24320 }
24321
24322 return allowPassthrough;
24323 };
24324
24325 var setGrabbed = function setGrabbed(ele) {
24326 ele[0]._private.grabbed = true;
24327 };
24328
24329 var setFreed = function setFreed(ele) {
24330 ele[0]._private.grabbed = false;
24331 };
24332
24333 var setInDragLayer = function setInDragLayer(ele) {
24334 ele[0]._private.rscratch.inDragLayer = true;
24335 };
24336
24337 var setOutDragLayer = function setOutDragLayer(ele) {
24338 ele[0]._private.rscratch.inDragLayer = false;
24339 };
24340
24341 var setGrabTarget = function setGrabTarget(ele) {
24342 ele[0]._private.rscratch.isGrabTarget = true;
24343 };
24344
24345 var removeGrabTarget = function removeGrabTarget(ele) {
24346 ele[0]._private.rscratch.isGrabTarget = false;
24347 };
24348
24349 var addToDragList = function addToDragList(ele, opts) {
24350 var list = opts.addToList;
24351 var listHasEle = list.has(ele);
24352
24353 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24354 list.merge(ele);
24355 setGrabbed(ele);
24356 }
24357 }; // helper function to determine which child nodes and inner edges
24358 // of a compound node to be dragged as well as the grabbed and selected nodes
24359
24360
24361 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24362 if (!node.cy().hasCompoundNodes()) {
24363 return;
24364 }
24365
24366 if (opts.inDragLayer == null && opts.addToList == null) {
24367 return;
24368 } // nothing to do
24369
24370
24371 var innerNodes = node.descendants();
24372
24373 if (opts.inDragLayer) {
24374 innerNodes.forEach(setInDragLayer);
24375 innerNodes.connectedEdges().forEach(setInDragLayer);
24376 }
24377
24378 if (opts.addToList) {
24379 addToDragList(innerNodes, opts);
24380 }
24381 }; // adds the given nodes and its neighbourhood to the drag layer
24382
24383
24384 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24385 opts = opts || {};
24386 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24387
24388 if (opts.inDragLayer) {
24389 nodes.forEach(setInDragLayer);
24390 nodes.neighborhood().stdFilter(function (ele) {
24391 return !hasCompoundNodes || ele.isEdge();
24392 }).forEach(setInDragLayer);
24393 }
24394
24395 if (opts.addToList) {
24396 nodes.forEach(function (ele) {
24397 addToDragList(ele, opts);
24398 });
24399 }
24400
24401 addDescendantsToDrag(nodes, opts); // always add to drag
24402 // also add nodes and edges related to the topmost ancestor
24403
24404 updateAncestorsInDragLayer(nodes, {
24405 inDragLayer: opts.inDragLayer
24406 });
24407 r.updateCachedGrabbedEles();
24408 };
24409
24410 var addNodeToDrag = addNodesToDrag;
24411
24412 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24413 if (!grabbedEles) {
24414 return;
24415 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24416
24417
24418 r.getCachedZSortedEles().forEach(function (ele) {
24419 setFreed(ele);
24420 setOutDragLayer(ele);
24421 removeGrabTarget(ele);
24422 });
24423 r.updateCachedGrabbedEles();
24424 }; // helper function to determine which ancestor nodes and edges should go
24425 // to the drag layer (or should be removed from drag layer).
24426
24427
24428 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24429 if (opts.inDragLayer == null && opts.addToList == null) {
24430 return;
24431 } // nothing to do
24432
24433
24434 if (!node.cy().hasCompoundNodes()) {
24435 return;
24436 } // find top-level parent
24437
24438
24439 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24440
24441 if (parent.same(node)) {
24442 return;
24443 }
24444
24445 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24446 var edges = nodes.connectedEdges();
24447
24448 if (opts.inDragLayer) {
24449 edges.forEach(setInDragLayer);
24450 nodes.forEach(setInDragLayer);
24451 }
24452
24453 if (opts.addToList) {
24454 nodes.forEach(function (ele) {
24455 addToDragList(ele, opts);
24456 });
24457 }
24458 };
24459
24460 var blurActiveDomElement = function blurActiveDomElement() {
24461 if (document.activeElement != null && document.activeElement.blur != null) {
24462 document.activeElement.blur();
24463 }
24464 };
24465
24466 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24467 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24468
24469 if (haveMutationsApi) {
24470 r.removeObserver = new MutationObserver(function (mutns) {
24471 // eslint-disable-line no-undef
24472 for (var i = 0; i < mutns.length; i++) {
24473 var mutn = mutns[i];
24474 var rNodes = mutn.removedNodes;
24475
24476 if (rNodes) {
24477 for (var j = 0; j < rNodes.length; j++) {
24478 var rNode = rNodes[j];
24479
24480 if (rNode === r.container) {
24481 r.destroy();
24482 break;
24483 }
24484 }
24485 }
24486 }
24487 });
24488
24489 if (r.container.parentNode) {
24490 r.removeObserver.observe(r.container.parentNode, {
24491 childList: true
24492 });
24493 }
24494 } else {
24495 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24496 // eslint-disable-line no-unused-vars
24497 r.destroy();
24498 });
24499 }
24500
24501 var onResize = util(function () {
24502 r.cy.resize();
24503 }, 100);
24504
24505 if (haveMutationsApi) {
24506 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24507
24508 r.styleObserver.observe(r.container, {
24509 attributes: true
24510 });
24511 } // auto resize
24512
24513
24514 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24515
24516 if (haveResizeObserverApi) {
24517 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24518
24519 r.resizeObserver.observe(r.container);
24520 }
24521
24522 var forEachUp = function forEachUp(domEle, fn) {
24523 while (domEle != null) {
24524 fn(domEle);
24525 domEle = domEle.parentNode;
24526 }
24527 };
24528
24529 var invalidateCoords = function invalidateCoords() {
24530 r.invalidateContainerClientCoordsCache();
24531 };
24532
24533 forEachUp(r.container, function (domEle) {
24534 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24535 r.registerBinding(domEle, 'animationend', invalidateCoords);
24536 r.registerBinding(domEle, 'scroll', invalidateCoords);
24537 }); // stop right click menu from appearing on cy
24538
24539 r.registerBinding(r.container, 'contextmenu', function (e) {
24540 e.preventDefault();
24541 });
24542
24543 var inBoxSelection = function inBoxSelection() {
24544 return r.selection[4] !== 0;
24545 };
24546
24547 var eventInContainer = function eventInContainer(e) {
24548 // save cycles if mouse events aren't to be captured
24549 var containerPageCoords = r.findContainerClientCoords();
24550 var x = containerPageCoords[0];
24551 var y = containerPageCoords[1];
24552 var width = containerPageCoords[2];
24553 var height = containerPageCoords[3];
24554 var positions = e.touches ? e.touches : [e];
24555 var atLeastOnePosInside = false;
24556
24557 for (var i = 0; i < positions.length; i++) {
24558 var p = positions[i];
24559
24560 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24561 atLeastOnePosInside = true;
24562 break;
24563 }
24564 }
24565
24566 if (!atLeastOnePosInside) {
24567 return false;
24568 }
24569
24570 var container = r.container;
24571 var target = e.target;
24572 var tParent = target.parentNode;
24573 var containerIsTarget = false;
24574
24575 while (tParent) {
24576 if (tParent === container) {
24577 containerIsTarget = true;
24578 break;
24579 }
24580
24581 tParent = tParent.parentNode;
24582 }
24583
24584 if (!containerIsTarget) {
24585 return false;
24586 } // if target is outisde cy container, then this event is not for us
24587
24588
24589 return true;
24590 }; // Primary key
24591
24592
24593 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24594 if (!eventInContainer(e)) {
24595 return;
24596 }
24597
24598 e.preventDefault();
24599 blurActiveDomElement();
24600 r.hoverData.capture = true;
24601 r.hoverData.which = e.which;
24602 var cy = r.cy;
24603 var gpos = [e.clientX, e.clientY];
24604 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24605 var select = r.selection;
24606 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24607 var near = nears[0];
24608 var draggedElements = r.dragData.possibleDragElements;
24609 r.hoverData.mdownPos = pos;
24610 r.hoverData.mdownGPos = gpos;
24611
24612 var checkForTaphold = function checkForTaphold() {
24613 r.hoverData.tapholdCancelled = false;
24614 clearTimeout(r.hoverData.tapholdTimeout);
24615 r.hoverData.tapholdTimeout = setTimeout(function () {
24616 if (r.hoverData.tapholdCancelled) {
24617 return;
24618 } else {
24619 var ele = r.hoverData.down;
24620
24621 if (ele) {
24622 ele.emit({
24623 originalEvent: e,
24624 type: 'taphold',
24625 position: {
24626 x: pos[0],
24627 y: pos[1]
24628 }
24629 });
24630 } else {
24631 cy.emit({
24632 originalEvent: e,
24633 type: 'taphold',
24634 position: {
24635 x: pos[0],
24636 y: pos[1]
24637 }
24638 });
24639 }
24640 }
24641 }, r.tapholdDuration);
24642 }; // Right click button
24643
24644
24645 if (e.which == 3) {
24646 r.hoverData.cxtStarted = true;
24647 var cxtEvt = {
24648 originalEvent: e,
24649 type: 'cxttapstart',
24650 position: {
24651 x: pos[0],
24652 y: pos[1]
24653 }
24654 };
24655
24656 if (near) {
24657 near.activate();
24658 near.emit(cxtEvt);
24659 r.hoverData.down = near;
24660 } else {
24661 cy.emit(cxtEvt);
24662 }
24663
24664 r.hoverData.downTime = new Date().getTime();
24665 r.hoverData.cxtDragged = false; // Primary button
24666 } else if (e.which == 1) {
24667 if (near) {
24668 near.activate();
24669 } // Element dragging
24670
24671
24672 {
24673 // If something is under the cursor and it is draggable, prepare to grab it
24674 if (near != null) {
24675 if (r.nodeIsGrabbable(near)) {
24676 var makeEvent = function makeEvent(type) {
24677 return {
24678 originalEvent: e,
24679 type: type,
24680 position: {
24681 x: pos[0],
24682 y: pos[1]
24683 }
24684 };
24685 };
24686
24687 var triggerGrab = function triggerGrab(ele) {
24688 ele.emit(makeEvent('grab'));
24689 };
24690
24691 setGrabTarget(near);
24692
24693 if (!near.selected()) {
24694 draggedElements = r.dragData.possibleDragElements = cy.collection();
24695 addNodeToDrag(near, {
24696 addToList: draggedElements
24697 });
24698 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24699 } else {
24700 draggedElements = r.dragData.possibleDragElements = cy.collection();
24701 var selectedNodes = cy.$(function (ele) {
24702 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24703 });
24704 addNodesToDrag(selectedNodes, {
24705 addToList: draggedElements
24706 });
24707 near.emit(makeEvent('grabon'));
24708 selectedNodes.forEach(triggerGrab);
24709 }
24710
24711 r.redrawHint('eles', true);
24712 r.redrawHint('drag', true);
24713 }
24714 }
24715
24716 r.hoverData.down = near;
24717 r.hoverData.downs = nears;
24718 r.hoverData.downTime = new Date().getTime();
24719 }
24720 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24721 x: pos[0],
24722 y: pos[1]
24723 });
24724
24725 if (near == null) {
24726 select[4] = 1;
24727 r.data.bgActivePosistion = {
24728 x: pos[0],
24729 y: pos[1]
24730 };
24731 r.redrawHint('select', true);
24732 r.redraw();
24733 } else if (near.pannable()) {
24734 select[4] = 1; // for future pan
24735 }
24736
24737 checkForTaphold();
24738 } // Initialize selection box coordinates
24739
24740
24741 select[0] = select[2] = pos[0];
24742 select[1] = select[3] = pos[1];
24743 }, false);
24744 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24745 // eslint-disable-line no-undef
24746 var capture = r.hoverData.capture;
24747
24748 if (!capture && !eventInContainer(e)) {
24749 return;
24750 }
24751
24752 var preventDefault = false;
24753 var cy = r.cy;
24754 var zoom = cy.zoom();
24755 var gpos = [e.clientX, e.clientY];
24756 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24757 var mdownPos = r.hoverData.mdownPos;
24758 var mdownGPos = r.hoverData.mdownGPos;
24759 var select = r.selection;
24760 var near = null;
24761
24762 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24763 near = r.findNearestElement(pos[0], pos[1], true, false);
24764 }
24765
24766 var last = r.hoverData.last;
24767 var down = r.hoverData.down;
24768 var disp = [pos[0] - select[2], pos[1] - select[3]];
24769 var draggedElements = r.dragData.possibleDragElements;
24770 var isOverThresholdDrag;
24771
24772 if (mdownGPos) {
24773 var dx = gpos[0] - mdownGPos[0];
24774 var dx2 = dx * dx;
24775 var dy = gpos[1] - mdownGPos[1];
24776 var dy2 = dy * dy;
24777 var dist2 = dx2 + dy2;
24778 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24779 }
24780
24781 var multSelKeyDown = isMultSelKeyDown(e);
24782
24783 if (isOverThresholdDrag) {
24784 r.hoverData.tapholdCancelled = true;
24785 }
24786
24787 var updateDragDelta = function updateDragDelta() {
24788 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24789
24790 if (dragDelta.length === 0) {
24791 dragDelta.push(disp[0]);
24792 dragDelta.push(disp[1]);
24793 } else {
24794 dragDelta[0] += disp[0];
24795 dragDelta[1] += disp[1];
24796 }
24797 };
24798
24799 preventDefault = true;
24800 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24801 x: pos[0],
24802 y: pos[1]
24803 });
24804
24805 var goIntoBoxMode = function goIntoBoxMode() {
24806 r.data.bgActivePosistion = undefined;
24807
24808 if (!r.hoverData.selecting) {
24809 cy.emit({
24810 originalEvent: e,
24811 type: 'boxstart',
24812 position: {
24813 x: pos[0],
24814 y: pos[1]
24815 }
24816 });
24817 }
24818
24819 select[4] = 1;
24820 r.hoverData.selecting = true;
24821 r.redrawHint('select', true);
24822 r.redraw();
24823 }; // trigger context drag if rmouse down
24824
24825
24826 if (r.hoverData.which === 3) {
24827 // but only if over threshold
24828 if (isOverThresholdDrag) {
24829 var cxtEvt = {
24830 originalEvent: e,
24831 type: 'cxtdrag',
24832 position: {
24833 x: pos[0],
24834 y: pos[1]
24835 }
24836 };
24837
24838 if (down) {
24839 down.emit(cxtEvt);
24840 } else {
24841 cy.emit(cxtEvt);
24842 }
24843
24844 r.hoverData.cxtDragged = true;
24845
24846 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24847 if (r.hoverData.cxtOver) {
24848 r.hoverData.cxtOver.emit({
24849 originalEvent: e,
24850 type: 'cxtdragout',
24851 position: {
24852 x: pos[0],
24853 y: pos[1]
24854 }
24855 });
24856 }
24857
24858 r.hoverData.cxtOver = near;
24859
24860 if (near) {
24861 near.emit({
24862 originalEvent: e,
24863 type: 'cxtdragover',
24864 position: {
24865 x: pos[0],
24866 y: pos[1]
24867 }
24868 });
24869 }
24870 }
24871 } // Check if we are drag panning the entire graph
24872
24873 } else if (r.hoverData.dragging) {
24874 preventDefault = true;
24875
24876 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24877 var deltaP;
24878
24879 if (r.hoverData.justStartedPan) {
24880 var mdPos = r.hoverData.mdownPos;
24881 deltaP = {
24882 x: (pos[0] - mdPos[0]) * zoom,
24883 y: (pos[1] - mdPos[1]) * zoom
24884 };
24885 r.hoverData.justStartedPan = false;
24886 } else {
24887 deltaP = {
24888 x: disp[0] * zoom,
24889 y: disp[1] * zoom
24890 };
24891 }
24892
24893 cy.panBy(deltaP);
24894 cy.emit('dragpan');
24895 r.hoverData.dragged = true;
24896 } // Needs reproject due to pan changing viewport
24897
24898
24899 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24900 } else if (select[4] == 1 && (down == null || down.pannable())) {
24901 if (isOverThresholdDrag) {
24902 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24903 goIntoBoxMode();
24904 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24905 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24906
24907 if (allowPassthrough) {
24908 r.hoverData.dragging = true;
24909 r.hoverData.justStartedPan = true;
24910 select[4] = 0;
24911 r.data.bgActivePosistion = array2point(mdownPos);
24912 r.redrawHint('select', true);
24913 r.redraw();
24914 }
24915 }
24916
24917 if (down && down.pannable() && down.active()) {
24918 down.unactivate();
24919 }
24920 }
24921 } else {
24922 if (down && down.pannable() && down.active()) {
24923 down.unactivate();
24924 }
24925
24926 if ((!down || !down.grabbed()) && near != last) {
24927 if (last) {
24928 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24929 x: pos[0],
24930 y: pos[1]
24931 });
24932 }
24933
24934 if (near) {
24935 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24936 x: pos[0],
24937 y: pos[1]
24938 });
24939 }
24940
24941 r.hoverData.last = near;
24942 }
24943
24944 if (down) {
24945 if (isOverThresholdDrag) {
24946 // then we can take action
24947 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24948 // then selection overrides
24949 if (down && down.grabbed()) {
24950 freeDraggedElements(draggedElements);
24951 down.emit('freeon');
24952 draggedElements.emit('free');
24953
24954 if (r.dragData.didDrag) {
24955 down.emit('dragfreeon');
24956 draggedElements.emit('dragfree');
24957 }
24958 }
24959
24960 goIntoBoxMode();
24961 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24962 // drag node
24963 var justStartedDrag = !r.dragData.didDrag;
24964
24965 if (justStartedDrag) {
24966 r.redrawHint('eles', true);
24967 }
24968
24969 r.dragData.didDrag = true; // indicate that we actually did drag the node
24970 // now, add the elements to the drag layer if not done already
24971
24972 if (!r.hoverData.draggingEles) {
24973 addNodesToDrag(draggedElements, {
24974 inDragLayer: true
24975 });
24976 }
24977
24978 var totalShift = {
24979 x: 0,
24980 y: 0
24981 };
24982
24983 if (number(disp[0]) && number(disp[1])) {
24984 totalShift.x += disp[0];
24985 totalShift.y += disp[1];
24986
24987 if (justStartedDrag) {
24988 var dragDelta = r.hoverData.dragDelta;
24989
24990 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24991 totalShift.x += dragDelta[0];
24992 totalShift.y += dragDelta[1];
24993 }
24994 }
24995 }
24996
24997 r.hoverData.draggingEles = true;
24998 draggedElements.silentShift(totalShift).emit('position drag');
24999 r.redrawHint('drag', true);
25000 r.redraw();
25001 }
25002 } else {
25003 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25004 updateDragDelta();
25005 }
25006 } // prevent the dragging from triggering text selection on the page
25007
25008
25009 preventDefault = true;
25010 }
25011
25012 select[2] = pos[0];
25013 select[3] = pos[1];
25014
25015 if (preventDefault) {
25016 if (e.stopPropagation) e.stopPropagation();
25017 if (e.preventDefault) e.preventDefault();
25018 return false;
25019 }
25020 }, false);
25021 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25022 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25023 // eslint-disable-line no-undef
25024 var capture = r.hoverData.capture;
25025
25026 if (!capture) {
25027 return;
25028 }
25029
25030 r.hoverData.capture = false;
25031 var cy = r.cy;
25032 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25033 var select = r.selection;
25034 var near = r.findNearestElement(pos[0], pos[1], true, false);
25035 var draggedElements = r.dragData.possibleDragElements;
25036 var down = r.hoverData.down;
25037 var multSelKeyDown = isMultSelKeyDown(e);
25038
25039 if (r.data.bgActivePosistion) {
25040 r.redrawHint('select', true);
25041 r.redraw();
25042 }
25043
25044 r.hoverData.tapholdCancelled = true;
25045 r.data.bgActivePosistion = undefined; // not active bg now
25046
25047 if (down) {
25048 down.unactivate();
25049 }
25050
25051 if (r.hoverData.which === 3) {
25052 var cxtEvt = {
25053 originalEvent: e,
25054 type: 'cxttapend',
25055 position: {
25056 x: pos[0],
25057 y: pos[1]
25058 }
25059 };
25060
25061 if (down) {
25062 down.emit(cxtEvt);
25063 } else {
25064 cy.emit(cxtEvt);
25065 }
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
25077 if (down) {
25078 down.emit(cxtTap);
25079 } else {
25080 cy.emit(cxtTap);
25081 }
25082 }
25083
25084 r.hoverData.cxtDragged = false;
25085 r.hoverData.which = null;
25086 } else if (r.hoverData.which === 1) {
25087 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25088 x: pos[0],
25089 y: pos[1]
25090 });
25091
25092 if (!r.dragData.didDrag && // didn't move a node around
25093 !r.hoverData.dragged && // didn't pan
25094 !r.hoverData.selecting && // 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
25103 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25104 clickTimeout && clearTimeout(clickTimeout);
25105 didDoubleClick = true;
25106 prevClickTimeStamp = null;
25107 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25108 x: pos[0],
25109 y: pos[1]
25110 });
25111 } else {
25112 clickTimeout = setTimeout(function () {
25113 if (didDoubleClick) return;
25114 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25115 x: pos[0],
25116 y: pos[1]
25117 });
25118 }, cy.multiClickDebounceTime());
25119 prevClickTimeStamp = e.timeStamp;
25120 }
25121 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25122
25123
25124 if (down == null && // not mousedown on node
25125 !r.dragData.didDrag // didn't move the node around
25126 && !r.hoverData.selecting // not box selection
25127 && !r.hoverData.dragged // didn't pan
25128 && !isMultSelKeyDown(e)) {
25129 cy.$(isSelected).unselect(['tapunselect']);
25130
25131 if (draggedElements.length > 0) {
25132 r.redrawHint('eles', true);
25133 }
25134
25135 r.dragData.possibleDragElements = draggedElements = cy.collection();
25136 } // Single selection
25137
25138
25139 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25140 if (near != null && near._private.selectable) {
25141 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25142 if (near.selected()) {
25143 near.unselect(['tapunselect']);
25144 } else {
25145 near.select(['tapselect']);
25146 }
25147 } else {
25148 if (!multSelKeyDown) {
25149 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25150 near.select(['tapselect']);
25151 }
25152 }
25153
25154 r.redrawHint('eles', true);
25155 }
25156 }
25157
25158 if (r.hoverData.selecting) {
25159 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25160 r.redrawHint('select', true);
25161
25162 if (box.length > 0) {
25163 r.redrawHint('eles', true);
25164 }
25165
25166 cy.emit({
25167 type: 'boxend',
25168 originalEvent: e,
25169 position: {
25170 x: pos[0],
25171 y: pos[1]
25172 }
25173 });
25174
25175 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25176 return ele.selectable() && !ele.selected();
25177 };
25178
25179 if (cy.selectionType() === 'additive') {
25180 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25181 } else {
25182 if (!multSelKeyDown) {
25183 cy.$(isSelected).unmerge(box).unselect();
25184 }
25185
25186 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25187 } // always need redraw in case eles unselectable
25188
25189
25190 r.redraw();
25191 } // Cancel drag pan
25192
25193
25194 if (r.hoverData.dragging) {
25195 r.hoverData.dragging = false;
25196 r.redrawHint('select', true);
25197 r.redrawHint('eles', true);
25198 r.redraw();
25199 }
25200
25201 if (!select[4]) {
25202 r.redrawHint('drag', true);
25203 r.redrawHint('eles', true);
25204 var downWasGrabbed = down && down.grabbed();
25205 freeDraggedElements(draggedElements);
25206
25207 if (downWasGrabbed) {
25208 down.emit('freeon');
25209 draggedElements.emit('free');
25210
25211 if (r.dragData.didDrag) {
25212 down.emit('dragfreeon');
25213 draggedElements.emit('dragfree');
25214 }
25215 }
25216 }
25217 } // else not right mouse
25218
25219
25220 select[4] = 0;
25221 r.hoverData.down = null;
25222 r.hoverData.cxtStarted = false;
25223 r.hoverData.draggingEles = false;
25224 r.hoverData.selecting = false;
25225 r.hoverData.isOverThresholdDrag = false;
25226 r.dragData.didDrag = false;
25227 r.hoverData.dragged = false;
25228 r.hoverData.dragDelta = [];
25229 r.hoverData.mdownPos = null;
25230 r.hoverData.mdownGPos = null;
25231 }, false);
25232
25233 var wheelHandler = function wheelHandler(e) {
25234 if (r.scrollingPage) {
25235 return;
25236 } // while scrolling, ignore wheel-to-zoom
25237
25238
25239 var cy = r.cy;
25240 var zoom = cy.zoom();
25241 var pan = cy.pan();
25242 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25243 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25244
25245 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25246 // if pan dragging or cxt dragging, wheel movements make no zoom
25247 e.preventDefault();
25248 return;
25249 }
25250
25251 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25252 e.preventDefault();
25253 r.data.wheelZooming = true;
25254 clearTimeout(r.data.wheelTimeout);
25255 r.data.wheelTimeout = setTimeout(function () {
25256 r.data.wheelZooming = false;
25257 r.redrawHint('eles', true);
25258 r.redraw();
25259 }, 150);
25260 var diff;
25261
25262 if (e.deltaY != null) {
25263 diff = e.deltaY / -250;
25264 } else if (e.wheelDeltaY != null) {
25265 diff = e.wheelDeltaY / 1000;
25266 } else {
25267 diff = e.wheelDelta / 1000;
25268 }
25269
25270 diff = diff * r.wheelSensitivity;
25271 var needsWheelFix = e.deltaMode === 1;
25272
25273 if (needsWheelFix) {
25274 // fixes slow wheel events on ff/linux and ff/windows
25275 diff *= 33;
25276 }
25277
25278 var newZoom = cy.zoom() * Math.pow(10, diff);
25279
25280 if (e.type === 'gesturechange') {
25281 newZoom = r.gestureStartZoom * e.scale;
25282 }
25283
25284 cy.zoom({
25285 level: newZoom,
25286 renderedPosition: {
25287 x: rpos[0],
25288 y: rpos[1]
25289 }
25290 });
25291 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25292 }
25293 }; // Functions to help with whether mouse wheel should trigger zooming
25294 // --
25295
25296
25297 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25298 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25299 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25300 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25301
25302 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25303 // eslint-disable-line no-unused-vars
25304 r.scrollingPage = true;
25305 clearTimeout(r.scrollingPageTimeout);
25306 r.scrollingPageTimeout = setTimeout(function () {
25307 r.scrollingPage = false;
25308 }, 250);
25309 }, true); // desktop safari pinch to zoom start
25310
25311 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25312 r.gestureStartZoom = r.cy.zoom();
25313
25314 if (!r.hasTouchStarted) {
25315 // don't affect touch devices like iphone
25316 e.preventDefault();
25317 }
25318 }, true);
25319 r.registerBinding(r.container, 'gesturechange', function (e) {
25320 if (!r.hasTouchStarted) {
25321 // don't affect touch devices like iphone
25322 wheelHandler(e);
25323 }
25324 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25325 // Handle mouseout on Cytoscape container
25326
25327 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25328 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25329 r.cy.emit({
25330 originalEvent: e,
25331 type: 'mouseout',
25332 position: {
25333 x: pos[0],
25334 y: pos[1]
25335 }
25336 });
25337 }, false);
25338 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25339 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25340 r.cy.emit({
25341 originalEvent: e,
25342 type: 'mouseover',
25343 position: {
25344 x: pos[0],
25345 y: pos[1]
25346 }
25347 });
25348 }, false);
25349 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25350
25351 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25352
25353 var center1, modelCenter1; // center point on start pinch to zoom
25354
25355 var offsetLeft, offsetTop;
25356 var containerWidth, containerHeight;
25357 var twoFingersStartInside;
25358
25359 var distance = function distance(x1, y1, x2, y2) {
25360 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25361 };
25362
25363 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25364 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25365 };
25366
25367 var touchstartHandler;
25368 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25369 r.hasTouchStarted = true;
25370
25371 if (!eventInContainer(e)) {
25372 return;
25373 }
25374
25375 blurActiveDomElement();
25376 r.touchData.capture = true;
25377 r.data.bgActivePosistion = undefined;
25378 var cy = r.cy;
25379 var now = r.touchData.now;
25380 var earlier = r.touchData.earlier;
25381
25382 if (e.touches[0]) {
25383 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25384 now[0] = pos[0];
25385 now[1] = pos[1];
25386 }
25387
25388 if (e.touches[1]) {
25389 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25390 now[2] = pos[0];
25391 now[3] = pos[1];
25392 }
25393
25394 if (e.touches[2]) {
25395 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25396 now[4] = pos[0];
25397 now[5] = pos[1];
25398 } // record starting points for pinch-to-zoom
25399
25400
25401 if (e.touches[1]) {
25402 r.touchData.singleTouchMoved = true;
25403 freeDraggedElements(r.dragData.touchDragEles);
25404 var offsets = r.findContainerClientCoords();
25405 offsetLeft = offsets[0];
25406 offsetTop = offsets[1];
25407 containerWidth = offsets[2];
25408 containerHeight = offsets[3];
25409 f1x1 = e.touches[0].clientX - offsetLeft;
25410 f1y1 = e.touches[0].clientY - offsetTop;
25411 f2x1 = e.touches[1].clientX - offsetLeft;
25412 f2y1 = e.touches[1].clientY - offsetTop;
25413 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25414 var pan = cy.pan();
25415 var zoom = cy.zoom();
25416 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25417 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25418 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25419 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25420
25421 var cxtDistThreshold = 200;
25422 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25423
25424 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25425 var near1 = r.findNearestElement(now[0], now[1], true, true);
25426 var near2 = r.findNearestElement(now[2], now[3], true, true);
25427
25428 if (near1 && near1.isNode()) {
25429 near1.activate().emit({
25430 originalEvent: e,
25431 type: 'cxttapstart',
25432 position: {
25433 x: now[0],
25434 y: now[1]
25435 }
25436 });
25437 r.touchData.start = near1;
25438 } else if (near2 && near2.isNode()) {
25439 near2.activate().emit({
25440 originalEvent: e,
25441 type: 'cxttapstart',
25442 position: {
25443 x: now[0],
25444 y: now[1]
25445 }
25446 });
25447 r.touchData.start = near2;
25448 } else {
25449 cy.emit({
25450 originalEvent: e,
25451 type: 'cxttapstart',
25452 position: {
25453 x: now[0],
25454 y: now[1]
25455 }
25456 });
25457 }
25458
25459 if (r.touchData.start) {
25460 r.touchData.start._private.grabbed = false;
25461 }
25462
25463 r.touchData.cxt = true;
25464 r.touchData.cxtDragged = false;
25465 r.data.bgActivePosistion = undefined;
25466 r.redraw();
25467 return;
25468 }
25469 }
25470
25471 if (e.touches[2]) {
25472 // ignore
25473 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25474 if (cy.boxSelectionEnabled()) {
25475 e.preventDefault();
25476 }
25477 } else if (e.touches[1]) ; else if (e.touches[0]) {
25478 var nears = r.findNearestElements(now[0], now[1], true, true);
25479 var near = nears[0];
25480
25481 if (near != null) {
25482 near.activate();
25483 r.touchData.start = near;
25484 r.touchData.starts = nears;
25485
25486 if (r.nodeIsGrabbable(near)) {
25487 var draggedEles = r.dragData.touchDragEles = cy.collection();
25488 var selectedNodes = null;
25489 r.redrawHint('eles', true);
25490 r.redrawHint('drag', true);
25491
25492 if (near.selected()) {
25493 // reset drag elements, since near will be added again
25494 selectedNodes = cy.$(function (ele) {
25495 return ele.selected() && r.nodeIsGrabbable(ele);
25496 });
25497 addNodesToDrag(selectedNodes, {
25498 addToList: draggedEles
25499 });
25500 } else {
25501 addNodeToDrag(near, {
25502 addToList: draggedEles
25503 });
25504 }
25505
25506 setGrabTarget(near);
25507
25508 var makeEvent = function makeEvent(type) {
25509 return {
25510 originalEvent: e,
25511 type: type,
25512 position: {
25513 x: now[0],
25514 y: now[1]
25515 }
25516 };
25517 };
25518
25519 near.emit(makeEvent('grabon'));
25520
25521 if (selectedNodes) {
25522 selectedNodes.forEach(function (n) {
25523 n.emit(makeEvent('grab'));
25524 });
25525 } else {
25526 near.emit(makeEvent('grab'));
25527 }
25528 }
25529 }
25530
25531 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25532 x: now[0],
25533 y: now[1]
25534 });
25535
25536 if (near == null) {
25537 r.data.bgActivePosistion = {
25538 x: pos[0],
25539 y: pos[1]
25540 };
25541 r.redrawHint('select', true);
25542 r.redraw();
25543 } // Tap, taphold
25544 // -----
25545
25546
25547 r.touchData.singleTouchMoved = false;
25548 r.touchData.singleTouchStartTime = +new Date();
25549 clearTimeout(r.touchData.tapholdTimeout);
25550 r.touchData.tapholdTimeout = setTimeout(function () {
25551 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25552 && !r.touchData.selecting // box selection shouldn't allow taphold through
25553 ) {
25554 triggerEvents(r.touchData.start, ['taphold'], e, {
25555 x: now[0],
25556 y: now[1]
25557 });
25558 }
25559 }, r.tapholdDuration);
25560 }
25561
25562 if (e.touches.length >= 1) {
25563 var sPos = r.touchData.startPosition = [];
25564
25565 for (var i = 0; i < now.length; i++) {
25566 sPos[i] = earlier[i] = now[i];
25567 }
25568
25569 var touch0 = e.touches[0];
25570 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25571 }
25572 }, false);
25573 var touchmoveHandler;
25574 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25575 // eslint-disable-line no-undef
25576 var capture = r.touchData.capture;
25577
25578 if (!capture && !eventInContainer(e)) {
25579 return;
25580 }
25581
25582 var select = r.selection;
25583 var cy = r.cy;
25584 var now = r.touchData.now;
25585 var earlier = r.touchData.earlier;
25586 var zoom = cy.zoom();
25587
25588 if (e.touches[0]) {
25589 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25590 now[0] = pos[0];
25591 now[1] = pos[1];
25592 }
25593
25594 if (e.touches[1]) {
25595 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25596 now[2] = pos[0];
25597 now[3] = pos[1];
25598 }
25599
25600 if (e.touches[2]) {
25601 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25602 now[4] = pos[0];
25603 now[5] = pos[1];
25604 }
25605
25606 var startGPos = r.touchData.startGPosition;
25607 var isOverThresholdDrag;
25608
25609 if (capture && e.touches[0] && startGPos) {
25610 var disp = [];
25611
25612 for (var j = 0; j < now.length; j++) {
25613 disp[j] = now[j] - earlier[j];
25614 }
25615
25616 var dx = e.touches[0].clientX - startGPos[0];
25617 var dx2 = dx * dx;
25618 var dy = e.touches[0].clientY - startGPos[1];
25619 var dy2 = dy * dy;
25620 var dist2 = dx2 + dy2;
25621 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25622 } // context swipe cancelling
25623
25624
25625 if (capture && r.touchData.cxt) {
25626 e.preventDefault();
25627 var f1x2 = e.touches[0].clientX - offsetLeft,
25628 f1y2 = e.touches[0].clientY - offsetTop;
25629 var f2x2 = e.touches[1].clientX - offsetLeft,
25630 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25631
25632 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25633 var factorSq = distance2Sq / distance1Sq;
25634 var distThreshold = 150;
25635 var distThresholdSq = distThreshold * distThreshold;
25636 var factorThreshold = 1.5;
25637 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25638
25639 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25640 r.touchData.cxt = false;
25641 r.data.bgActivePosistion = undefined;
25642 r.redrawHint('select', true);
25643 var cxtEvt = {
25644 originalEvent: e,
25645 type: 'cxttapend',
25646 position: {
25647 x: now[0],
25648 y: now[1]
25649 }
25650 };
25651
25652 if (r.touchData.start) {
25653 r.touchData.start.unactivate().emit(cxtEvt);
25654 r.touchData.start = null;
25655 } else {
25656 cy.emit(cxtEvt);
25657 }
25658 }
25659 } // context swipe
25660
25661
25662 if (capture && r.touchData.cxt) {
25663 var cxtEvt = {
25664 originalEvent: e,
25665 type: 'cxtdrag',
25666 position: {
25667 x: now[0],
25668 y: now[1]
25669 }
25670 };
25671 r.data.bgActivePosistion = undefined;
25672 r.redrawHint('select', true);
25673
25674 if (r.touchData.start) {
25675 r.touchData.start.emit(cxtEvt);
25676 } else {
25677 cy.emit(cxtEvt);
25678 }
25679
25680 if (r.touchData.start) {
25681 r.touchData.start._private.grabbed = false;
25682 }
25683
25684 r.touchData.cxtDragged = true;
25685 var near = r.findNearestElement(now[0], now[1], true, true);
25686
25687 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25688 if (r.touchData.cxtOver) {
25689 r.touchData.cxtOver.emit({
25690 originalEvent: e,
25691 type: 'cxtdragout',
25692 position: {
25693 x: now[0],
25694 y: now[1]
25695 }
25696 });
25697 }
25698
25699 r.touchData.cxtOver = near;
25700
25701 if (near) {
25702 near.emit({
25703 originalEvent: e,
25704 type: 'cxtdragover',
25705 position: {
25706 x: now[0],
25707 y: now[1]
25708 }
25709 });
25710 }
25711 } // box selection
25712
25713 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25714 e.preventDefault();
25715 r.data.bgActivePosistion = undefined;
25716 this.lastThreeTouch = +new Date();
25717
25718 if (!r.touchData.selecting) {
25719 cy.emit({
25720 originalEvent: e,
25721 type: 'boxstart',
25722 position: {
25723 x: now[0],
25724 y: now[1]
25725 }
25726 });
25727 }
25728
25729 r.touchData.selecting = true;
25730 r.touchData.didSelect = true;
25731 select[4] = 1;
25732
25733 if (!select || select.length === 0 || select[0] === undefined) {
25734 select[0] = (now[0] + now[2] + now[4]) / 3;
25735 select[1] = (now[1] + now[3] + now[5]) / 3;
25736 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25737 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25738 } else {
25739 select[2] = (now[0] + now[2] + now[4]) / 3;
25740 select[3] = (now[1] + now[3] + now[5]) / 3;
25741 }
25742
25743 r.redrawHint('select', true);
25744 r.redraw(); // pinch to zoom
25745 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25746 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25747 // two fingers => pinch to zoom
25748 e.preventDefault();
25749 r.data.bgActivePosistion = undefined;
25750 r.redrawHint('select', true);
25751 var draggedEles = r.dragData.touchDragEles;
25752
25753 if (draggedEles) {
25754 r.redrawHint('drag', true);
25755
25756 for (var i = 0; i < draggedEles.length; i++) {
25757 var de_p = draggedEles[i]._private;
25758 de_p.grabbed = false;
25759 de_p.rscratch.inDragLayer = false;
25760 }
25761 }
25762
25763 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25764
25765 var f1x2 = e.touches[0].clientX - offsetLeft,
25766 f1y2 = e.touches[0].clientY - offsetTop;
25767 var f2x2 = e.touches[1].clientX - offsetLeft,
25768 f2y2 = e.touches[1].clientY - offsetTop;
25769 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25770 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25771
25772 var factor = distance2 / distance1;
25773
25774 if (twoFingersStartInside) {
25775 // delta finger1
25776 var df1x = f1x2 - f1x1;
25777 var df1y = f1y2 - f1y1; // delta finger 2
25778
25779 var df2x = f2x2 - f2x1;
25780 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25781 // i.e. so pinching cancels out and moving together pans
25782
25783 var tx = (df1x + df2x) / 2;
25784 var ty = (df1y + df2y) / 2; // now calculate the zoom
25785
25786 var zoom1 = cy.zoom();
25787 var zoom2 = zoom1 * factor;
25788 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25789
25790 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25791 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25792 var pan2 = {
25793 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25794 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25795 }; // remove dragged eles
25796
25797 if (_start && _start.active()) {
25798 var draggedEles = r.dragData.touchDragEles;
25799 freeDraggedElements(draggedEles);
25800 r.redrawHint('drag', true);
25801 r.redrawHint('eles', true);
25802
25803 _start.unactivate().emit('freeon');
25804
25805 draggedEles.emit('free');
25806
25807 if (r.dragData.didDrag) {
25808 _start.emit('dragfreeon');
25809
25810 draggedEles.emit('dragfree');
25811 }
25812 }
25813
25814 cy.viewport({
25815 zoom: zoom2,
25816 pan: pan2,
25817 cancelOnFailedZoom: true
25818 });
25819 cy.emit('pinchzoom');
25820 distance1 = distance2;
25821 f1x1 = f1x2;
25822 f1y1 = f1y2;
25823 f2x1 = f2x2;
25824 f2y1 = f2y2;
25825 r.pinching = true;
25826 } // Re-project
25827
25828
25829 if (e.touches[0]) {
25830 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25831 now[0] = pos[0];
25832 now[1] = pos[1];
25833 }
25834
25835 if (e.touches[1]) {
25836 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25837 now[2] = pos[0];
25838 now[3] = pos[1];
25839 }
25840
25841 if (e.touches[2]) {
25842 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25843 now[4] = pos[0];
25844 now[5] = pos[1];
25845 }
25846 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25847 ) {
25848 var start = r.touchData.start;
25849 var last = r.touchData.last;
25850 var near;
25851
25852 if (!r.hoverData.draggingEles && !r.swipePanning) {
25853 near = r.findNearestElement(now[0], now[1], true, true);
25854 }
25855
25856 if (capture && start != null) {
25857 e.preventDefault();
25858 } // dragging nodes
25859
25860
25861 if (capture && start != null && r.nodeIsDraggable(start)) {
25862 if (isOverThresholdDrag) {
25863 // then dragging can happen
25864 var draggedEles = r.dragData.touchDragEles;
25865 var justStartedDrag = !r.dragData.didDrag;
25866
25867 if (justStartedDrag) {
25868 addNodesToDrag(draggedEles, {
25869 inDragLayer: true
25870 });
25871 }
25872
25873 r.dragData.didDrag = true;
25874 var totalShift = {
25875 x: 0,
25876 y: 0
25877 };
25878
25879 if (number(disp[0]) && number(disp[1])) {
25880 totalShift.x += disp[0];
25881 totalShift.y += disp[1];
25882
25883 if (justStartedDrag) {
25884 r.redrawHint('eles', true);
25885 var dragDelta = r.touchData.dragDelta;
25886
25887 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25888 totalShift.x += dragDelta[0];
25889 totalShift.y += dragDelta[1];
25890 }
25891 }
25892 }
25893
25894 r.hoverData.draggingEles = true;
25895 draggedEles.silentShift(totalShift).emit('position drag');
25896 r.redrawHint('drag', true);
25897
25898 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25899 r.redrawHint('eles', true);
25900 }
25901
25902 r.redraw();
25903 } else {
25904 // otherise keep track of drag delta for later
25905 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25906
25907 if (dragDelta.length === 0) {
25908 dragDelta.push(disp[0]);
25909 dragDelta.push(disp[1]);
25910 } else {
25911 dragDelta[0] += disp[0];
25912 dragDelta[1] += disp[1];
25913 }
25914 }
25915 } // touchmove
25916
25917
25918 {
25919 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25920 x: now[0],
25921 y: now[1]
25922 });
25923
25924 if ((!start || !start.grabbed()) && near != last) {
25925 if (last) {
25926 last.emit({
25927 originalEvent: e,
25928 type: 'tapdragout',
25929 position: {
25930 x: now[0],
25931 y: now[1]
25932 }
25933 });
25934 }
25935
25936 if (near) {
25937 near.emit({
25938 originalEvent: e,
25939 type: 'tapdragover',
25940 position: {
25941 x: now[0],
25942 y: now[1]
25943 }
25944 });
25945 }
25946 }
25947
25948 r.touchData.last = near;
25949 } // check to cancel taphold
25950
25951 if (capture) {
25952 for (var i = 0; i < now.length; i++) {
25953 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25954 r.touchData.singleTouchMoved = true;
25955 }
25956 }
25957 } // panning
25958
25959
25960 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25961 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25962
25963 if (allowPassthrough) {
25964 e.preventDefault();
25965
25966 if (!r.data.bgActivePosistion) {
25967 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25968 }
25969
25970 if (r.swipePanning) {
25971 cy.panBy({
25972 x: disp[0] * zoom,
25973 y: disp[1] * zoom
25974 });
25975 cy.emit('dragpan');
25976 } else if (isOverThresholdDrag) {
25977 r.swipePanning = true;
25978 cy.panBy({
25979 x: dx * zoom,
25980 y: dy * zoom
25981 });
25982 cy.emit('dragpan');
25983
25984 if (start) {
25985 start.unactivate();
25986 r.redrawHint('select', true);
25987 r.touchData.start = null;
25988 }
25989 }
25990 } // Re-project
25991
25992
25993 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25994 now[0] = pos[0];
25995 now[1] = pos[1];
25996 }
25997 }
25998
25999 for (var j = 0; j < now.length; j++) {
26000 earlier[j] = now[j];
26001 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26002
26003
26004 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26005 r.data.bgActivePosistion = undefined;
26006 r.redrawHint('select', true);
26007 r.redraw();
26008 }
26009 }, false);
26010 var touchcancelHandler;
26011 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26012 // eslint-disable-line no-unused-vars
26013 var start = r.touchData.start;
26014 r.touchData.capture = false;
26015
26016 if (start) {
26017 start.unactivate();
26018 }
26019 });
26020 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
26021 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26022 // eslint-disable-line no-unused-vars
26023 var start = r.touchData.start;
26024 var capture = r.touchData.capture;
26025
26026 if (capture) {
26027 if (e.touches.length === 0) {
26028 r.touchData.capture = false;
26029 }
26030
26031 e.preventDefault();
26032 } else {
26033 return;
26034 }
26035
26036 var select = r.selection;
26037 r.swipePanning = false;
26038 r.hoverData.draggingEles = false;
26039 var cy = r.cy;
26040 var zoom = cy.zoom();
26041 var now = r.touchData.now;
26042 var earlier = r.touchData.earlier;
26043
26044 if (e.touches[0]) {
26045 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26046 now[0] = pos[0];
26047 now[1] = pos[1];
26048 }
26049
26050 if (e.touches[1]) {
26051 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26052 now[2] = pos[0];
26053 now[3] = pos[1];
26054 }
26055
26056 if (e.touches[2]) {
26057 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26058 now[4] = pos[0];
26059 now[5] = pos[1];
26060 }
26061
26062 if (start) {
26063 start.unactivate();
26064 }
26065
26066 var ctxTapend;
26067
26068 if (r.touchData.cxt) {
26069 ctxTapend = {
26070 originalEvent: e,
26071 type: 'cxttapend',
26072 position: {
26073 x: now[0],
26074 y: now[1]
26075 }
26076 };
26077
26078 if (start) {
26079 start.emit(ctxTapend);
26080 } else {
26081 cy.emit(ctxTapend);
26082 }
26083
26084 if (!r.touchData.cxtDragged) {
26085 var ctxTap = {
26086 originalEvent: e,
26087 type: 'cxttap',
26088 position: {
26089 x: now[0],
26090 y: now[1]
26091 }
26092 };
26093
26094 if (start) {
26095 start.emit(ctxTap);
26096 } else {
26097 cy.emit(ctxTap);
26098 }
26099 }
26100
26101 if (r.touchData.start) {
26102 r.touchData.start._private.grabbed = false;
26103 }
26104
26105 r.touchData.cxt = false;
26106 r.touchData.start = null;
26107 r.redraw();
26108 return;
26109 } // no more box selection if we don't have three fingers
26110
26111
26112 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26113 r.touchData.selecting = false;
26114 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26115 select[0] = undefined;
26116 select[1] = undefined;
26117 select[2] = undefined;
26118 select[3] = undefined;
26119 select[4] = 0;
26120 r.redrawHint('select', true);
26121 cy.emit({
26122 type: 'boxend',
26123 originalEvent: e,
26124 position: {
26125 x: now[0],
26126 y: now[1]
26127 }
26128 });
26129
26130 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26131 return ele.selectable() && !ele.selected();
26132 };
26133
26134 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26135
26136 if (box.nonempty()) {
26137 r.redrawHint('eles', true);
26138 }
26139
26140 r.redraw();
26141 }
26142
26143 if (start != null) {
26144 start.unactivate();
26145 }
26146
26147 if (e.touches[2]) {
26148 r.data.bgActivePosistion = undefined;
26149 r.redrawHint('select', true);
26150 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26151 r.data.bgActivePosistion = undefined;
26152 r.redrawHint('select', true);
26153 var draggedEles = r.dragData.touchDragEles;
26154
26155 if (start != null) {
26156 var startWasGrabbed = start._private.grabbed;
26157 freeDraggedElements(draggedEles);
26158 r.redrawHint('drag', true);
26159 r.redrawHint('eles', true);
26160
26161 if (startWasGrabbed) {
26162 start.emit('freeon');
26163 draggedEles.emit('free');
26164
26165 if (r.dragData.didDrag) {
26166 start.emit('dragfreeon');
26167 draggedEles.emit('dragfree');
26168 }
26169 }
26170
26171 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26172 x: now[0],
26173 y: now[1]
26174 });
26175 start.unactivate();
26176 r.touchData.start = null;
26177 } else {
26178 var near = r.findNearestElement(now[0], now[1], true, true);
26179 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26180 x: now[0],
26181 y: now[1]
26182 });
26183 }
26184
26185 var dx = r.touchData.startPosition[0] - now[0];
26186 var dx2 = dx * dx;
26187 var dy = r.touchData.startPosition[1] - now[1];
26188 var dy2 = dy * dy;
26189 var dist2 = dx2 + dy2;
26190 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26191
26192 if (!r.touchData.singleTouchMoved) {
26193 if (!start) {
26194 cy.$(':selected').unselect(['tapunselect']);
26195 }
26196
26197 triggerEvents(start, ['tap', 'vclick'], e, {
26198 x: now[0],
26199 y: now[1]
26200 });
26201 didDoubleTouch = false;
26202
26203 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26204 touchTimeout && clearTimeout(touchTimeout);
26205 didDoubleTouch = true;
26206 prevTouchTimeStamp = null;
26207 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26208 x: now[0],
26209 y: now[1]
26210 });
26211 } else {
26212 touchTimeout = setTimeout(function () {
26213 if (didDoubleTouch) return;
26214 triggerEvents(start, ['onetap', 'voneclick'], e, {
26215 x: now[0],
26216 y: now[1]
26217 });
26218 }, cy.multiClickDebounceTime());
26219 prevTouchTimeStamp = e.timeStamp;
26220 }
26221 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26222
26223
26224 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26225 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26226 ) {
26227 if (cy.selectionType() === 'single') {
26228 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26229 start.select(['tapselect']);
26230 } else {
26231 if (start.selected()) {
26232 start.unselect(['tapunselect']);
26233 } else {
26234 start.select(['tapselect']);
26235 }
26236 }
26237
26238 r.redrawHint('eles', true);
26239 }
26240
26241 r.touchData.singleTouchMoved = true;
26242 }
26243
26244 for (var j = 0; j < now.length; j++) {
26245 earlier[j] = now[j];
26246 }
26247
26248 r.dragData.didDrag = false; // reset for next touchstart
26249
26250 if (e.touches.length === 0) {
26251 r.touchData.dragDelta = [];
26252 r.touchData.startPosition = null;
26253 r.touchData.startGPosition = null;
26254 r.touchData.didSelect = false;
26255 }
26256
26257 if (e.touches.length < 2) {
26258 if (e.touches.length === 1) {
26259 // the old start global pos'n may not be the same finger that remains
26260 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26261 }
26262
26263 r.pinching = false;
26264 r.redrawHint('eles', true);
26265 r.redraw();
26266 } //r.redraw();
26267
26268 }, false); // fallback compatibility layer for ms pointer events
26269
26270 if (typeof TouchEvent === 'undefined') {
26271 var pointers = [];
26272
26273 var makeTouch = function makeTouch(e) {
26274 return {
26275 clientX: e.clientX,
26276 clientY: e.clientY,
26277 force: 1,
26278 identifier: e.pointerId,
26279 pageX: e.pageX,
26280 pageY: e.pageY,
26281 radiusX: e.width / 2,
26282 radiusY: e.height / 2,
26283 screenX: e.screenX,
26284 screenY: e.screenY,
26285 target: e.target
26286 };
26287 };
26288
26289 var makePointer = function makePointer(e) {
26290 return {
26291 event: e,
26292 touch: makeTouch(e)
26293 };
26294 };
26295
26296 var addPointer = function addPointer(e) {
26297 pointers.push(makePointer(e));
26298 };
26299
26300 var removePointer = function removePointer(e) {
26301 for (var i = 0; i < pointers.length; i++) {
26302 var p = pointers[i];
26303
26304 if (p.event.pointerId === e.pointerId) {
26305 pointers.splice(i, 1);
26306 return;
26307 }
26308 }
26309 };
26310
26311 var updatePointer = function updatePointer(e) {
26312 var p = pointers.filter(function (p) {
26313 return p.event.pointerId === e.pointerId;
26314 })[0];
26315 p.event = e;
26316 p.touch = makeTouch(e);
26317 };
26318
26319 var addTouchesToEvent = function addTouchesToEvent(e) {
26320 e.touches = pointers.map(function (p) {
26321 return p.touch;
26322 });
26323 };
26324
26325 var pointerIsMouse = function pointerIsMouse(e) {
26326 return e.pointerType === 'mouse' || e.pointerType === 4;
26327 };
26328
26329 r.registerBinding(r.container, 'pointerdown', function (e) {
26330 if (pointerIsMouse(e)) {
26331 return;
26332 } // mouse already handled
26333
26334
26335 e.preventDefault();
26336 addPointer(e);
26337 addTouchesToEvent(e);
26338 touchstartHandler(e);
26339 });
26340 r.registerBinding(r.container, 'pointerup', function (e) {
26341 if (pointerIsMouse(e)) {
26342 return;
26343 } // mouse already handled
26344
26345
26346 removePointer(e);
26347 addTouchesToEvent(e);
26348 touchendHandler(e);
26349 });
26350 r.registerBinding(r.container, 'pointercancel', function (e) {
26351 if (pointerIsMouse(e)) {
26352 return;
26353 } // mouse already handled
26354
26355
26356 removePointer(e);
26357 addTouchesToEvent(e);
26358 touchcancelHandler(e);
26359 });
26360 r.registerBinding(r.container, 'pointermove', function (e) {
26361 if (pointerIsMouse(e)) {
26362 return;
26363 } // mouse already handled
26364
26365
26366 e.preventDefault();
26367 updatePointer(e);
26368 addTouchesToEvent(e);
26369 touchmoveHandler(e);
26370 });
26371 }
26372};
26373
26374var BRp$d = {};
26375
26376BRp$d.generatePolygon = function (name, points) {
26377 return this.nodeShapes[name] = {
26378 renderer: this,
26379 name: name,
26380 points: points,
26381 draw: function draw(context, centerX, centerY, width, height) {
26382 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26383 },
26384 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26385 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26386 },
26387 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26388 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26389 }
26390 };
26391};
26392
26393BRp$d.generateEllipse = function () {
26394 return this.nodeShapes['ellipse'] = {
26395 renderer: this,
26396 name: 'ellipse',
26397 draw: function draw(context, centerX, centerY, width, height) {
26398 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26399 },
26400 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26401 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26402 },
26403 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26404 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26405 }
26406 };
26407};
26408
26409BRp$d.generateRoundPolygon = function (name, points) {
26410 // Pre-compute control points
26411 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26412 // the unit vectors.
26413 // For simplicity the layout will be:
26414 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26415 var allPoints = new Array(points.length * 2);
26416
26417 for (var i = 0; i < points.length / 2; i++) {
26418 var sourceIndex = i * 2;
26419 var destIndex = void 0;
26420
26421 if (i < points.length / 2 - 1) {
26422 destIndex = (i + 1) * 2;
26423 } else {
26424 destIndex = 0;
26425 }
26426
26427 allPoints[i * 4] = points[sourceIndex];
26428 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26429 var xDest = points[destIndex] - points[sourceIndex];
26430 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26431 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26432 allPoints[i * 4 + 2] = xDest / norm;
26433 allPoints[i * 4 + 3] = yDest / norm;
26434 }
26435
26436 return this.nodeShapes[name] = {
26437 renderer: this,
26438 name: name,
26439 points: allPoints,
26440 draw: function draw(context, centerX, centerY, width, height) {
26441 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26442 },
26443 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26444 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26445 },
26446 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26447 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26448 }
26449 };
26450};
26451
26452BRp$d.generateRoundRectangle = function () {
26453 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26454 renderer: this,
26455 name: 'round-rectangle',
26456 points: generateUnitNgonPointsFitToSquare(4, 0),
26457 draw: function draw(context, centerX, centerY, width, height) {
26458 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26459 },
26460 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26461 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26462 },
26463 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26464 var cornerRadius = getRoundRectangleRadius(width, height);
26465 var diam = cornerRadius * 2; // Check hBox
26466
26467 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26468 return true;
26469 } // Check vBox
26470
26471
26472 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26473 return true;
26474 } // Check top left quarter circle
26475
26476
26477 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26478 return true;
26479 } // Check top right quarter circle
26480
26481
26482 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26483 return true;
26484 } // Check bottom right quarter circle
26485
26486
26487 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26488 return true;
26489 } // Check bottom left quarter circle
26490
26491
26492 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26493 return true;
26494 }
26495
26496 return false;
26497 }
26498 };
26499};
26500
26501BRp$d.generateCutRectangle = function () {
26502 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26503 renderer: this,
26504 name: 'cut-rectangle',
26505 cornerLength: getCutRectangleCornerLength(),
26506 points: generateUnitNgonPointsFitToSquare(4, 0),
26507 draw: function draw(context, centerX, centerY, width, height) {
26508 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26509 },
26510 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26511 var cl = this.cornerLength;
26512 var hh = height / 2;
26513 var hw = width / 2;
26514 var xBegin = centerX - hw;
26515 var xEnd = centerX + hw;
26516 var yBegin = centerY - hh;
26517 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26518
26519 return {
26520 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26521 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26522 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26523 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26524 };
26525 },
26526 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26527 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26528 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26529 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26530 },
26531 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26532 // Check hBox
26533 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26534 return true;
26535 } // Check vBox
26536
26537
26538 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26539 return true;
26540 }
26541
26542 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26543 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26544 }
26545 };
26546};
26547
26548BRp$d.generateBarrel = function () {
26549 return this.nodeShapes['barrel'] = {
26550 renderer: this,
26551 name: 'barrel',
26552 points: generateUnitNgonPointsFitToSquare(4, 0),
26553 draw: function draw(context, centerX, centerY, width, height) {
26554 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26555 },
26556 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26557 // use two fixed t values for the bezier curve approximation
26558 var t0 = 0.15;
26559 var t1 = 0.5;
26560 var t2 = 0.85;
26561 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26562
26563 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26564 // approximate curve pts based on the two t values
26565 var m0 = qbezierPtAt({
26566 x: pts[0],
26567 y: pts[1]
26568 }, {
26569 x: pts[2],
26570 y: pts[3]
26571 }, {
26572 x: pts[4],
26573 y: pts[5]
26574 }, t0);
26575 var m1 = qbezierPtAt({
26576 x: pts[0],
26577 y: pts[1]
26578 }, {
26579 x: pts[2],
26580 y: pts[3]
26581 }, {
26582 x: pts[4],
26583 y: pts[5]
26584 }, t1);
26585 var m2 = qbezierPtAt({
26586 x: pts[0],
26587 y: pts[1]
26588 }, {
26589 x: pts[2],
26590 y: pts[3]
26591 }, {
26592 x: pts[4],
26593 y: pts[5]
26594 }, t2);
26595 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26596 };
26597
26598 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26599 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26600 },
26601 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26602 var hh = height / 2;
26603 var hw = width / 2;
26604 var xBegin = centerX - hw;
26605 var xEnd = centerX + hw;
26606 var yBegin = centerY - hh;
26607 var yEnd = centerY + hh;
26608 var curveConstants = getBarrelCurveConstants(width, height);
26609 var hOffset = curveConstants.heightOffset;
26610 var wOffset = curveConstants.widthOffset;
26611 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26612
26613 var pts = {
26614 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26615 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26616 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26617 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26618 };
26619 pts.topLeft.isTop = true;
26620 pts.topRight.isTop = true;
26621 pts.bottomLeft.isBottom = true;
26622 pts.bottomRight.isBottom = true;
26623 return pts;
26624 },
26625 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26626 var curveConstants = getBarrelCurveConstants(width, height);
26627 var hOffset = curveConstants.heightOffset;
26628 var wOffset = curveConstants.widthOffset; // Check hBox
26629
26630 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26631 return true;
26632 } // Check vBox
26633
26634
26635 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26636 return true;
26637 }
26638
26639 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26640
26641 var getCurveT = function getCurveT(x, y, curvePts) {
26642 var x0 = curvePts[4];
26643 var x1 = curvePts[2];
26644 var x2 = curvePts[0];
26645 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26646
26647 var y2 = curvePts[1];
26648 var xMin = Math.min(x0, x2);
26649 var xMax = Math.max(x0, x2);
26650 var yMin = Math.min(y0, y2);
26651 var yMax = Math.max(y0, y2);
26652
26653 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26654 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26655 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26656 var validRoots = roots.filter(function (r) {
26657 return 0 <= r && r <= 1;
26658 });
26659
26660 if (validRoots.length > 0) {
26661 return validRoots[0];
26662 }
26663 }
26664
26665 return null;
26666 };
26667
26668 var curveRegions = Object.keys(barrelCurvePts);
26669
26670 for (var i = 0; i < curveRegions.length; i++) {
26671 var corner = curveRegions[i];
26672 var cornerPts = barrelCurvePts[corner];
26673 var t = getCurveT(x, y, cornerPts);
26674
26675 if (t == null) {
26676 continue;
26677 }
26678
26679 var y0 = cornerPts[5];
26680 var y1 = cornerPts[3];
26681 var y2 = cornerPts[1];
26682 var bezY = qbezierAt(y0, y1, y2, t);
26683
26684 if (cornerPts.isTop && bezY <= y) {
26685 return true;
26686 }
26687
26688 if (cornerPts.isBottom && y <= bezY) {
26689 return true;
26690 }
26691 }
26692
26693 return false;
26694 }
26695 };
26696};
26697
26698BRp$d.generateBottomRoundrectangle = function () {
26699 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26700 renderer: this,
26701 name: 'bottom-round-rectangle',
26702 points: generateUnitNgonPointsFitToSquare(4, 0),
26703 draw: function draw(context, centerX, centerY, width, height) {
26704 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26705 },
26706 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26707 var topStartX = nodeX - (width / 2 + padding);
26708 var topStartY = nodeY - (height / 2 + padding);
26709 var topEndY = topStartY;
26710 var topEndX = nodeX + (width / 2 + padding);
26711 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26712
26713 if (topIntersections.length > 0) {
26714 return topIntersections;
26715 }
26716
26717 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26718 },
26719 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26720 var cornerRadius = getRoundRectangleRadius(width, height);
26721 var diam = 2 * cornerRadius; // Check hBox
26722
26723 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26724 return true;
26725 } // Check vBox
26726
26727
26728 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26729 return true;
26730 } // check non-rounded top side
26731
26732
26733 var outerWidth = width / 2 + 2 * padding;
26734 var outerHeight = height / 2 + 2 * padding;
26735 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26736
26737 if (pointInsidePolygonPoints(x, y, points)) {
26738 return true;
26739 } // Check bottom right quarter circle
26740
26741
26742 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26743 return true;
26744 } // Check bottom left quarter circle
26745
26746
26747 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26748 return true;
26749 }
26750
26751 return false;
26752 }
26753 };
26754};
26755
26756BRp$d.registerNodeShapes = function () {
26757 var nodeShapes = this.nodeShapes = {};
26758 var renderer = this;
26759 this.generateEllipse();
26760 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26761 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26762 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26763 nodeShapes['square'] = nodeShapes['rectangle'];
26764 this.generateRoundRectangle();
26765 this.generateCutRectangle();
26766 this.generateBarrel();
26767 this.generateBottomRoundrectangle();
26768 {
26769 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26770 this.generatePolygon('diamond', diamondPoints);
26771 this.generateRoundPolygon('round-diamond', diamondPoints);
26772 }
26773 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26774 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26775 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26776 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26777 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26778 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26779 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26780 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26781 var star5Points = new Array(20);
26782 {
26783 var outerPoints = generateUnitNgonPoints(5, 0);
26784 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26785
26786 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26787 innerRadius *= 1.57;
26788
26789 for (var i = 0; i < innerPoints.length / 2; i++) {
26790 innerPoints[i * 2] *= innerRadius;
26791 innerPoints[i * 2 + 1] *= innerRadius;
26792 }
26793
26794 for (var i = 0; i < 20 / 4; i++) {
26795 star5Points[i * 4] = outerPoints[i * 2];
26796 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26797 star5Points[i * 4 + 2] = innerPoints[i * 2];
26798 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26799 }
26800 }
26801 star5Points = fitPolygonToSquare(star5Points);
26802 this.generatePolygon('star', star5Points);
26803 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26804 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26805 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]);
26806 {
26807 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26808 this.generatePolygon('tag', tagPoints);
26809 this.generateRoundPolygon('round-tag', tagPoints);
26810 }
26811
26812 nodeShapes.makePolygon = function (points) {
26813 // use caching on user-specified polygons so they are as fast as native shapes
26814 var key = points.join('$');
26815 var name = 'polygon-' + key;
26816 var shape;
26817
26818 if (shape = this[name]) {
26819 // got cached shape
26820 return shape;
26821 } // create and cache new shape
26822
26823
26824 return renderer.generatePolygon(name, points);
26825 };
26826};
26827
26828var BRp$e = {};
26829
26830BRp$e.timeToRender = function () {
26831 return this.redrawTotalTime / this.redrawCount;
26832};
26833
26834BRp$e.redraw = function (options) {
26835 options = options || staticEmptyObject();
26836 var r = this;
26837
26838 if (r.averageRedrawTime === undefined) {
26839 r.averageRedrawTime = 0;
26840 }
26841
26842 if (r.lastRedrawTime === undefined) {
26843 r.lastRedrawTime = 0;
26844 }
26845
26846 if (r.lastDrawTime === undefined) {
26847 r.lastDrawTime = 0;
26848 }
26849
26850 r.requestedFrame = true;
26851 r.renderOptions = options;
26852};
26853
26854BRp$e.beforeRender = function (fn, priority) {
26855 // the renderer can't add tick callbacks when destroyed
26856 if (this.destroyed) {
26857 return;
26858 }
26859
26860 if (priority == null) {
26861 error('Priority is not optional for beforeRender');
26862 }
26863
26864 var cbs = this.beforeRenderCallbacks;
26865 cbs.push({
26866 fn: fn,
26867 priority: priority
26868 }); // higher priority callbacks executed first
26869
26870 cbs.sort(function (a, b) {
26871 return b.priority - a.priority;
26872 });
26873};
26874
26875var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26876 var cbs = r.beforeRenderCallbacks;
26877
26878 for (var i = 0; i < cbs.length; i++) {
26879 cbs[i].fn(willDraw, startTime);
26880 }
26881};
26882
26883BRp$e.startRenderLoop = function () {
26884 var r = this;
26885 var cy = r.cy;
26886
26887 if (r.renderLoopStarted) {
26888 return;
26889 } else {
26890 r.renderLoopStarted = true;
26891 }
26892
26893 var renderFn = function renderFn(requestTime) {
26894 if (r.destroyed) {
26895 return;
26896 }
26897
26898 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26899 beforeRenderCallbacks(r, true, requestTime);
26900 var startTime = performanceNow();
26901 r.render(r.renderOptions);
26902 var endTime = r.lastDrawTime = performanceNow();
26903
26904 if (r.averageRedrawTime === undefined) {
26905 r.averageRedrawTime = endTime - startTime;
26906 }
26907
26908 if (r.redrawCount === undefined) {
26909 r.redrawCount = 0;
26910 }
26911
26912 r.redrawCount++;
26913
26914 if (r.redrawTotalTime === undefined) {
26915 r.redrawTotalTime = 0;
26916 }
26917
26918 var duration = endTime - startTime;
26919 r.redrawTotalTime += duration;
26920 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26921
26922 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26923 r.requestedFrame = false;
26924 } else {
26925 beforeRenderCallbacks(r, false, requestTime);
26926 }
26927
26928 r.skipFrame = false;
26929 requestAnimationFrame(renderFn);
26930 };
26931
26932 requestAnimationFrame(renderFn);
26933};
26934
26935var BaseRenderer = function BaseRenderer(options) {
26936 this.init(options);
26937};
26938
26939var BR = BaseRenderer;
26940var BRp$f = BR.prototype;
26941BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26942
26943BRp$f.init = function (options) {
26944 var r = this;
26945 r.options = options;
26946 r.cy = options.cy;
26947 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26948
26949 if (window$1) {
26950 var document = window$1.document;
26951 var head = document.head;
26952 var stylesheetId = '__________cytoscape_stylesheet';
26953 var className = '__________cytoscape_container';
26954 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26955
26956 if (ctr.className.indexOf(className) < 0) {
26957 ctr.className = (ctr.className || '') + ' ' + className;
26958 }
26959
26960 if (!stylesheetAlreadyExists) {
26961 var stylesheet = document.createElement('style');
26962 stylesheet.id = stylesheetId;
26963 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26964 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26965 }
26966
26967 var computedStyle = window$1.getComputedStyle(ctr);
26968 var position = computedStyle.getPropertyValue('position');
26969
26970 if (position === 'static') {
26971 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26972 }
26973 }
26974
26975 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26976
26977 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26978
26979 r.hoverData = {
26980 down: null,
26981 last: null,
26982 downTime: null,
26983 triggerMode: null,
26984 dragging: false,
26985 initialPan: [null, null],
26986 capture: false
26987 };
26988 r.dragData = {
26989 possibleDragElements: []
26990 };
26991 r.touchData = {
26992 start: null,
26993 capture: false,
26994 // These 3 fields related to tap, taphold events
26995 startPosition: [null, null, null, null, null, null],
26996 singleTouchStartTime: null,
26997 singleTouchMoved: true,
26998 now: [null, null, null, null, null, null],
26999 earlier: [null, null, null, null, null, null]
27000 };
27001 r.redraws = 0;
27002 r.showFps = options.showFps;
27003 r.debug = options.debug;
27004 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27005 r.textureOnViewport = options.textureOnViewport;
27006 r.wheelSensitivity = options.wheelSensitivity;
27007 r.motionBlurEnabled = options.motionBlur; // on by default
27008
27009 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
27010 r.motionBlur = options.motionBlur; // for initial kick off
27011
27012 r.motionBlurOpacity = options.motionBlurOpacity;
27013 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27014 r.motionBlurPxRatio = 1;
27015 r.mbPxRBlurry = 1; //0.8;
27016
27017 r.minMbLowQualFrames = 4;
27018 r.fullQualityMb = false;
27019 r.clearedForMotionBlur = [];
27020 r.desktopTapThreshold = options.desktopTapThreshold;
27021 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27022 r.touchTapThreshold = options.touchTapThreshold;
27023 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27024 r.tapholdDuration = 500;
27025 r.bindings = [];
27026 r.beforeRenderCallbacks = [];
27027 r.beforeRenderPriorities = {
27028 // higher priority execs before lower one
27029 animations: 400,
27030 eleCalcs: 300,
27031 eleTxrDeq: 200,
27032 lyrTxrDeq: 150,
27033 lyrTxrSkip: 100
27034 };
27035 r.registerNodeShapes();
27036 r.registerArrowShapes();
27037 r.registerCalculationListeners();
27038};
27039
27040BRp$f.notify = function (eventName, eles) {
27041 var r = this;
27042 var cy = r.cy; // the renderer can't be notified after it's destroyed
27043
27044 if (this.destroyed) {
27045 return;
27046 }
27047
27048 if (eventName === 'init') {
27049 r.load();
27050 return;
27051 }
27052
27053 if (eventName === 'destroy') {
27054 r.destroy();
27055 return;
27056 }
27057
27058 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27059 r.invalidateCachedZSortedEles();
27060 }
27061
27062 if (eventName === 'viewport') {
27063 r.redrawHint('select', true);
27064 }
27065
27066 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27067 r.invalidateContainerClientCoordsCache();
27068 r.matchCanvasSize(r.container);
27069 }
27070
27071 r.redrawHint('eles', true);
27072 r.redrawHint('drag', true);
27073 this.startRenderLoop();
27074 this.redraw();
27075};
27076
27077BRp$f.destroy = function () {
27078 var r = this;
27079 r.destroyed = true;
27080 r.cy.stopAnimationLoop();
27081
27082 for (var i = 0; i < r.bindings.length; i++) {
27083 var binding = r.bindings[i];
27084 var b = binding;
27085 var tgt = b.target;
27086 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27087 }
27088
27089 r.bindings = [];
27090 r.beforeRenderCallbacks = [];
27091 r.onUpdateEleCalcsFns = [];
27092
27093 if (r.removeObserver) {
27094 r.removeObserver.disconnect();
27095 }
27096
27097 if (r.styleObserver) {
27098 r.styleObserver.disconnect();
27099 }
27100
27101 if (r.resizeObserver) {
27102 r.resizeObserver.disconnect();
27103 }
27104
27105 if (r.labelCalcDiv) {
27106 try {
27107 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27108 } catch (e) {// ie10 issue #1014
27109 }
27110 }
27111};
27112
27113BRp$f.isHeadless = function () {
27114 return false;
27115};
27116
27117[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
27118 extend(BRp$f, props);
27119});
27120
27121var fullFpsTime = 1000 / 60; // assume 60 frames per second
27122
27123var defs = {
27124 setupDequeueing: function setupDequeueing(opts) {
27125 return function setupDequeueingImpl() {
27126 var self = this;
27127 var r = this.renderer;
27128
27129 if (self.dequeueingSetup) {
27130 return;
27131 } else {
27132 self.dequeueingSetup = true;
27133 }
27134
27135 var queueRedraw = util(function () {
27136 r.redrawHint('eles', true);
27137 r.redrawHint('drag', true);
27138 r.redraw();
27139 }, opts.deqRedrawThreshold);
27140
27141 var dequeue = function dequeue(willDraw, frameStartTime) {
27142 var startTime = performanceNow();
27143 var avgRenderTime = r.averageRedrawTime;
27144 var renderTime = r.lastRedrawTime;
27145 var deqd = [];
27146 var extent = r.cy.extent();
27147 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27148 // queue won't automatically be flushed before dequeueing starts
27149
27150 if (!willDraw) {
27151 r.flushRenderedStyleQueue();
27152 }
27153
27154 while (true) {
27155 // eslint-disable-line no-constant-condition
27156 var now = performanceNow();
27157 var duration = now - startTime;
27158 var frameDuration = now - frameStartTime;
27159
27160 if (renderTime < fullFpsTime) {
27161 // if we're rendering faster than the ideal fps, then do dequeueing
27162 // during all of the remaining frame time
27163 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27164
27165 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27166 break;
27167 }
27168 } else {
27169 if (willDraw) {
27170 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27171 break;
27172 }
27173 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27174 break;
27175 }
27176 }
27177
27178 var thisDeqd = opts.deq(self, pixelRatio, extent);
27179
27180 if (thisDeqd.length > 0) {
27181 for (var i = 0; i < thisDeqd.length; i++) {
27182 deqd.push(thisDeqd[i]);
27183 }
27184 } else {
27185 break;
27186 }
27187 } // callbacks on dequeue
27188
27189
27190 if (deqd.length > 0) {
27191 opts.onDeqd(self, deqd);
27192
27193 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27194 queueRedraw();
27195 }
27196 }
27197 };
27198
27199 var priority = opts.priority || noop;
27200 r.beforeRender(dequeue, priority(self));
27201 };
27202 }
27203};
27204
27205// Uses keys so elements may share the same cache.
27206
27207var ElementTextureCacheLookup =
27208/*#__PURE__*/
27209function () {
27210 function ElementTextureCacheLookup(getKey) {
27211 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27212
27213 _classCallCheck(this, ElementTextureCacheLookup);
27214
27215 this.idsByKey = new Map$1();
27216 this.keyForId = new Map$1();
27217 this.cachesByLvl = new Map$1();
27218 this.lvls = [];
27219 this.getKey = getKey;
27220 this.doesEleInvalidateKey = doesEleInvalidateKey;
27221 }
27222
27223 _createClass(ElementTextureCacheLookup, [{
27224 key: "getIdsFor",
27225 value: function getIdsFor(key) {
27226 if (key == null) {
27227 error("Can not get id list for null key");
27228 }
27229
27230 var idsByKey = this.idsByKey;
27231 var ids = this.idsByKey.get(key);
27232
27233 if (!ids) {
27234 ids = new Set$1();
27235 idsByKey.set(key, ids);
27236 }
27237
27238 return ids;
27239 }
27240 }, {
27241 key: "addIdForKey",
27242 value: function addIdForKey(key, id) {
27243 if (key != null) {
27244 this.getIdsFor(key).add(id);
27245 }
27246 }
27247 }, {
27248 key: "deleteIdForKey",
27249 value: function deleteIdForKey(key, id) {
27250 if (key != null) {
27251 this.getIdsFor(key)["delete"](id);
27252 }
27253 }
27254 }, {
27255 key: "getNumberOfIdsForKey",
27256 value: function getNumberOfIdsForKey(key) {
27257 if (key == null) {
27258 return 0;
27259 } else {
27260 return this.getIdsFor(key).size;
27261 }
27262 }
27263 }, {
27264 key: "updateKeyMappingFor",
27265 value: function updateKeyMappingFor(ele) {
27266 var id = ele.id();
27267 var prevKey = this.keyForId.get(id);
27268 var currKey = this.getKey(ele);
27269 this.deleteIdForKey(prevKey, id);
27270 this.addIdForKey(currKey, id);
27271 this.keyForId.set(id, currKey);
27272 }
27273 }, {
27274 key: "deleteKeyMappingFor",
27275 value: function deleteKeyMappingFor(ele) {
27276 var id = ele.id();
27277 var prevKey = this.keyForId.get(id);
27278 this.deleteIdForKey(prevKey, id);
27279 this.keyForId["delete"](id);
27280 }
27281 }, {
27282 key: "keyHasChangedFor",
27283 value: function keyHasChangedFor(ele) {
27284 var id = ele.id();
27285 var prevKey = this.keyForId.get(id);
27286 var newKey = this.getKey(ele);
27287 return prevKey !== newKey;
27288 }
27289 }, {
27290 key: "isInvalid",
27291 value: function isInvalid(ele) {
27292 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27293 }
27294 }, {
27295 key: "getCachesAt",
27296 value: function getCachesAt(lvl) {
27297 var cachesByLvl = this.cachesByLvl,
27298 lvls = this.lvls;
27299 var caches = cachesByLvl.get(lvl);
27300
27301 if (!caches) {
27302 caches = new Map$1();
27303 cachesByLvl.set(lvl, caches);
27304 lvls.push(lvl);
27305 }
27306
27307 return caches;
27308 }
27309 }, {
27310 key: "getCache",
27311 value: function getCache(key, lvl) {
27312 return this.getCachesAt(lvl).get(key);
27313 }
27314 }, {
27315 key: "get",
27316 value: function get(ele, lvl) {
27317 var key = this.getKey(ele);
27318 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27319
27320 if (cache != null) {
27321 this.updateKeyMappingFor(ele);
27322 }
27323
27324 return cache;
27325 }
27326 }, {
27327 key: "getForCachedKey",
27328 value: function getForCachedKey(ele, lvl) {
27329 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27330
27331 var cache = this.getCache(key, lvl);
27332 return cache;
27333 }
27334 }, {
27335 key: "hasCache",
27336 value: function hasCache(key, lvl) {
27337 return this.getCachesAt(lvl).has(key);
27338 }
27339 }, {
27340 key: "has",
27341 value: function has(ele, lvl) {
27342 var key = this.getKey(ele);
27343 return this.hasCache(key, lvl);
27344 }
27345 }, {
27346 key: "setCache",
27347 value: function setCache(key, lvl, cache) {
27348 cache.key = key;
27349 this.getCachesAt(lvl).set(key, cache);
27350 }
27351 }, {
27352 key: "set",
27353 value: function set(ele, lvl, cache) {
27354 var key = this.getKey(ele);
27355 this.setCache(key, lvl, cache);
27356 this.updateKeyMappingFor(ele);
27357 }
27358 }, {
27359 key: "deleteCache",
27360 value: function deleteCache(key, lvl) {
27361 this.getCachesAt(lvl)["delete"](key);
27362 }
27363 }, {
27364 key: "delete",
27365 value: function _delete(ele, lvl) {
27366 var key = this.getKey(ele);
27367 this.deleteCache(key, lvl);
27368 }
27369 }, {
27370 key: "invalidateKey",
27371 value: function invalidateKey(key) {
27372 var _this = this;
27373
27374 this.lvls.forEach(function (lvl) {
27375 return _this.deleteCache(key, lvl);
27376 });
27377 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27378
27379 }, {
27380 key: "invalidate",
27381 value: function invalidate(ele) {
27382 var id = ele.id();
27383 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27384
27385 this.deleteKeyMappingFor(ele);
27386 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27387
27388 if (entireKeyInvalidated) {
27389 // clear mapping for current key
27390 this.invalidateKey(key);
27391 }
27392
27393 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27394 }
27395 }]);
27396
27397 return ElementTextureCacheLookup;
27398}();
27399
27400var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27401
27402var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27403
27404var minLvl = -4; // when scaling smaller than that we don't need to re-render
27405
27406var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27407
27408var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27409
27410var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27411
27412var defTxrWidth = 1024; // default/minimum texture width
27413
27414var maxTxrW = 1024; // the maximum width of a texture
27415
27416var maxTxrH = 1024; // the maximum height of a texture
27417
27418var minUtility = 0.2; // if usage of texture is less than this, it is retired
27419
27420var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27421
27422var maxFullnessChecks = 10; // dequeued after this many checks
27423
27424var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27425
27426var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27427
27428var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27429
27430var deqFastCost = 0.9; // % of frame time to be used when >60fps
27431
27432var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27433
27434var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27435
27436var getTxrReasons = {
27437 dequeue: 'dequeue',
27438 downscale: 'downscale',
27439 highQuality: 'highQuality'
27440};
27441var initDefaults = defaults({
27442 getKey: null,
27443 doesEleInvalidateKey: falsify,
27444 drawElement: null,
27445 getBoundingBox: null,
27446 getRotationPoint: null,
27447 getRotationOffset: null,
27448 isVisible: trueify,
27449 allowEdgeTxrCaching: true,
27450 allowParentTxrCaching: true
27451});
27452
27453var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27454 var self = this;
27455 self.renderer = renderer;
27456 self.onDequeues = [];
27457 var opts = initDefaults(initOptions);
27458 extend(self, opts);
27459 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27460 self.setupDequeueing();
27461};
27462
27463var ETCp = ElementTextureCache.prototype;
27464ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27465
27466ETCp.getTextureQueue = function (txrH) {
27467 var self = this;
27468 self.eleImgCaches = self.eleImgCaches || {};
27469 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27470}; // the list of usused textures which can be recycled (in use in texture queue)
27471
27472
27473ETCp.getRetiredTextureQueue = function (txrH) {
27474 var self = this;
27475 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27476 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27477 return rtxtrQ;
27478}; // queue of element draw requests at different scale levels
27479
27480
27481ETCp.getElementQueue = function () {
27482 var self = this;
27483 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27484 return b.reqs - a.reqs;
27485 });
27486 return q;
27487}; // queue of element draw requests at different scale levels (element id lookup)
27488
27489
27490ETCp.getElementKeyToQueue = function () {
27491 var self = this;
27492 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27493 return k2q;
27494};
27495
27496ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27497 var self = this;
27498 var r = this.renderer;
27499 var zoom = r.cy.zoom();
27500 var lookup = this.lookup;
27501
27502 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27503 return null;
27504 }
27505
27506 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27507 return null;
27508 }
27509
27510 if (lvl == null) {
27511 lvl = Math.ceil(log2(zoom * pxRatio));
27512 }
27513
27514 if (lvl < minLvl) {
27515 lvl = minLvl;
27516 } else if (zoom >= maxZoom || lvl > maxLvl) {
27517 return null;
27518 }
27519
27520 var scale = Math.pow(2, lvl);
27521 var eleScaledH = bb.h * scale;
27522 var eleScaledW = bb.w * scale;
27523 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27524
27525 if (!this.isVisible(ele, scaledLabelShown)) {
27526 return null;
27527 }
27528
27529 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27530
27531 if (eleCache && eleCache.invalidated) {
27532 eleCache.invalidated = false;
27533 eleCache.texture.invalidatedWidth -= eleCache.width;
27534 }
27535
27536 if (eleCache) {
27537 return eleCache;
27538 }
27539
27540 var txrH; // which texture height this ele belongs to
27541
27542 if (eleScaledH <= minTxrH) {
27543 txrH = minTxrH;
27544 } else if (eleScaledH <= txrStepH) {
27545 txrH = txrStepH;
27546 } else {
27547 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27548 }
27549
27550 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27551 return null; // caching large elements is not efficient
27552 }
27553
27554 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27555
27556 var txr = txrQ[txrQ.length - 2];
27557
27558 var addNewTxr = function addNewTxr() {
27559 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27560 }; // try the last one if there is no second last one
27561
27562
27563 if (!txr) {
27564 txr = txrQ[txrQ.length - 1];
27565 } // if the last one doesn't exist, we need a first one
27566
27567
27568 if (!txr) {
27569 txr = addNewTxr();
27570 } // if there's no room in the current texture, we need a new one
27571
27572
27573 if (txr.width - txr.usedWidth < eleScaledW) {
27574 txr = addNewTxr();
27575 }
27576
27577 var scalableFrom = function scalableFrom(otherCache) {
27578 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27579 };
27580
27581 var deqing = reason && reason === getTxrReasons.dequeue;
27582 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27583 var downscaleReq = reason && reason === getTxrReasons.downscale;
27584 var higherCache; // the nearest cache with a higher level
27585
27586 for (var l = lvl + 1; l <= maxLvl; l++) {
27587 var c = lookup.get(ele, l);
27588
27589 if (c) {
27590 higherCache = c;
27591 break;
27592 }
27593 }
27594
27595 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27596
27597 var downscale = function downscale() {
27598 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27599 }; // reset ele area in texture
27600
27601
27602 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27603 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27604
27605 if (scalableFrom(oneUpCache)) {
27606 // then we can relatively cheaply rescale the existing image w/o rerendering
27607 downscale();
27608 } else if (scalableFrom(higherCache)) {
27609 // then use the higher cache for now and queue the next level down
27610 // to cheaply scale towards the smaller level
27611 if (highQualityReq) {
27612 for (var _l = higherCache.level; _l > lvl; _l--) {
27613 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27614 }
27615
27616 downscale();
27617 } else {
27618 self.queueElement(ele, higherCache.level - 1);
27619 return higherCache;
27620 }
27621 } else {
27622 var lowerCache; // the nearest cache with a lower level
27623
27624 if (!deqing && !highQualityReq && !downscaleReq) {
27625 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27626 var _c = lookup.get(ele, _l2);
27627
27628 if (_c) {
27629 lowerCache = _c;
27630 break;
27631 }
27632 }
27633 }
27634
27635 if (scalableFrom(lowerCache)) {
27636 // then use the lower quality cache for now and queue the better one for later
27637 self.queueElement(ele, lvl);
27638 return lowerCache;
27639 }
27640
27641 txr.context.translate(txr.usedWidth, 0);
27642 txr.context.scale(scale, scale);
27643 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27644 txr.context.scale(1 / scale, 1 / scale);
27645 txr.context.translate(-txr.usedWidth, 0);
27646 }
27647
27648 eleCache = {
27649 x: txr.usedWidth,
27650 texture: txr,
27651 level: lvl,
27652 scale: scale,
27653 width: eleScaledW,
27654 height: eleScaledH,
27655 scaledLabelShown: scaledLabelShown
27656 };
27657 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27658 txr.eleCaches.push(eleCache);
27659 lookup.set(ele, lvl, eleCache);
27660 self.checkTextureFullness(txr);
27661 return eleCache;
27662};
27663
27664ETCp.invalidateElements = function (eles) {
27665 for (var i = 0; i < eles.length; i++) {
27666 this.invalidateElement(eles[i]);
27667 }
27668};
27669
27670ETCp.invalidateElement = function (ele) {
27671 var self = this;
27672 var lookup = self.lookup;
27673 var caches = [];
27674 var invalid = lookup.isInvalid(ele);
27675
27676 if (!invalid) {
27677 return; // override the invalidation request if the element key has not changed
27678 }
27679
27680 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27681 var cache = lookup.getForCachedKey(ele, lvl);
27682
27683 if (cache) {
27684 caches.push(cache);
27685 }
27686 }
27687
27688 var noOtherElesUseCache = lookup.invalidate(ele);
27689
27690 if (noOtherElesUseCache) {
27691 for (var i = 0; i < caches.length; i++) {
27692 var _cache = caches[i];
27693 var txr = _cache.texture; // remove space from the texture it belongs to
27694
27695 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27696
27697 _cache.invalidated = true; // retire the texture if its utility is low
27698
27699 self.checkTextureUtility(txr);
27700 }
27701 } // remove from queue since the old req was for the old state
27702
27703
27704 self.removeFromQueue(ele);
27705};
27706
27707ETCp.checkTextureUtility = function (txr) {
27708 // invalidate all entries in the cache if the cache size is small
27709 if (txr.invalidatedWidth >= minUtility * txr.width) {
27710 this.retireTexture(txr);
27711 }
27712};
27713
27714ETCp.checkTextureFullness = function (txr) {
27715 // if texture has been mostly filled and passed over several times, remove
27716 // it from the queue so we don't need to waste time looking at it to put new things
27717 var self = this;
27718 var txrQ = self.getTextureQueue(txr.height);
27719
27720 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27721 removeFromArray(txrQ, txr);
27722 } else {
27723 txr.fullnessChecks++;
27724 }
27725};
27726
27727ETCp.retireTexture = function (txr) {
27728 var self = this;
27729 var txrH = txr.height;
27730 var txrQ = self.getTextureQueue(txrH);
27731 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27732
27733 removeFromArray(txrQ, txr);
27734 txr.retired = true; // remove the refs from the eles to the caches:
27735
27736 var eleCaches = txr.eleCaches;
27737
27738 for (var i = 0; i < eleCaches.length; i++) {
27739 var eleCache = eleCaches[i];
27740 lookup.deleteCache(eleCache.key, eleCache.level);
27741 }
27742
27743 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27744
27745 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27746 rtxtrQ.push(txr);
27747};
27748
27749ETCp.addTexture = function (txrH, minW) {
27750 var self = this;
27751 var txrQ = self.getTextureQueue(txrH);
27752 var txr = {};
27753 txrQ.push(txr);
27754 txr.eleCaches = [];
27755 txr.height = txrH;
27756 txr.width = Math.max(defTxrWidth, minW);
27757 txr.usedWidth = 0;
27758 txr.invalidatedWidth = 0;
27759 txr.fullnessChecks = 0;
27760 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27761 txr.context = txr.canvas.getContext('2d');
27762 return txr;
27763};
27764
27765ETCp.recycleTexture = function (txrH, minW) {
27766 var self = this;
27767 var txrQ = self.getTextureQueue(txrH);
27768 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27769
27770 for (var i = 0; i < rtxtrQ.length; i++) {
27771 var txr = rtxtrQ[i];
27772
27773 if (txr.width >= minW) {
27774 txr.retired = false;
27775 txr.usedWidth = 0;
27776 txr.invalidatedWidth = 0;
27777 txr.fullnessChecks = 0;
27778 clearArray(txr.eleCaches);
27779 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27780 txr.context.clearRect(0, 0, txr.width, txr.height);
27781 removeFromArray(rtxtrQ, txr);
27782 txrQ.push(txr);
27783 return txr;
27784 }
27785 }
27786};
27787
27788ETCp.queueElement = function (ele, lvl) {
27789 var self = this;
27790 var q = self.getElementQueue();
27791 var k2q = self.getElementKeyToQueue();
27792 var key = this.getKey(ele);
27793 var existingReq = k2q[key];
27794
27795 if (existingReq) {
27796 // use the max lvl b/c in between lvls are cheap to make
27797 existingReq.level = Math.max(existingReq.level, lvl);
27798 existingReq.eles.merge(ele);
27799 existingReq.reqs++;
27800 q.updateItem(existingReq);
27801 } else {
27802 var req = {
27803 eles: ele.spawn().merge(ele),
27804 level: lvl,
27805 reqs: 1,
27806 key: key
27807 };
27808 q.push(req);
27809 k2q[key] = req;
27810 }
27811};
27812
27813ETCp.dequeue = function (pxRatio
27814/*, extent*/
27815) {
27816 var self = this;
27817 var q = self.getElementQueue();
27818 var k2q = self.getElementKeyToQueue();
27819 var dequeued = [];
27820 var lookup = self.lookup;
27821
27822 for (var i = 0; i < maxDeqSize; i++) {
27823 if (q.size() > 0) {
27824 var req = q.pop();
27825 var key = req.key;
27826 var ele = req.eles[0]; // all eles have the same key
27827
27828 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27829
27830 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27831
27832 if (cacheExists) {
27833 continue;
27834 }
27835
27836 dequeued.push(req);
27837 var bb = self.getBoundingBox(ele);
27838 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27839 } else {
27840 break;
27841 }
27842 }
27843
27844 return dequeued;
27845};
27846
27847ETCp.removeFromQueue = function (ele) {
27848 var self = this;
27849 var q = self.getElementQueue();
27850 var k2q = self.getElementKeyToQueue();
27851 var key = this.getKey(ele);
27852 var req = k2q[key];
27853
27854 if (req != null) {
27855 if (req.eles.length === 1) {
27856 // remove if last ele in the req
27857 // bring to front of queue
27858 req.reqs = MAX_INT;
27859 q.updateItem(req);
27860 q.pop(); // remove from queue
27861
27862 k2q[key] = null; // remove from lookup map
27863 } else {
27864 // otherwise just remove ele from req
27865 req.eles.unmerge(ele);
27866 }
27867 }
27868};
27869
27870ETCp.onDequeue = function (fn) {
27871 this.onDequeues.push(fn);
27872};
27873
27874ETCp.offDequeue = function (fn) {
27875 removeFromArray(this.onDequeues, fn);
27876};
27877
27878ETCp.setupDequeueing = defs.setupDequeueing({
27879 deqRedrawThreshold: deqRedrawThreshold,
27880 deqCost: deqCost,
27881 deqAvgCost: deqAvgCost,
27882 deqNoDrawCost: deqNoDrawCost,
27883 deqFastCost: deqFastCost,
27884 deq: function deq(self, pxRatio, extent) {
27885 return self.dequeue(pxRatio, extent);
27886 },
27887 onDeqd: function onDeqd(self, deqd) {
27888 for (var i = 0; i < self.onDequeues.length; i++) {
27889 var fn = self.onDequeues[i];
27890 fn(deqd);
27891 }
27892 },
27893 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27894 for (var i = 0; i < deqd.length; i++) {
27895 var eles = deqd[i].eles;
27896
27897 for (var j = 0; j < eles.length; j++) {
27898 var bb = eles[j].boundingBox();
27899
27900 if (boundingBoxesIntersect(bb, extent)) {
27901 return true;
27902 }
27903 }
27904 }
27905
27906 return false;
27907 },
27908 priority: function priority(self) {
27909 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27910 }
27911});
27912
27913var defNumLayers = 1; // default number of layers to use
27914
27915var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27916
27917var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27918
27919var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27920
27921var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27922
27923var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27924
27925var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27926
27927var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27928
27929var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27930
27931var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27932
27933var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27934
27935var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27936
27937var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27938
27939var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27940// var log = function(){ console.log.apply( console, arguments ); };
27941
27942var LayeredTextureCache = function LayeredTextureCache(renderer) {
27943 var self = this;
27944 var r = self.renderer = renderer;
27945 var cy = r.cy;
27946 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27947
27948 self.firstGet = true;
27949 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27950 self.skipping = false;
27951 self.eleTxrDeqs = cy.collection();
27952 self.scheduleElementRefinement = util(function () {
27953 self.refineElementTextures(self.eleTxrDeqs);
27954 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27955 }, refineEleDebounceTime);
27956 r.beforeRender(function (willDraw, now) {
27957 if (now - self.lastInvalidationTime <= invalidThreshold) {
27958 self.skipping = true;
27959 } else {
27960 self.skipping = false;
27961 }
27962 }, r.beforeRenderPriorities.lyrTxrSkip);
27963
27964 var qSort = function qSort(a, b) {
27965 return b.reqs - a.reqs;
27966 };
27967
27968 self.layersQueue = new Heap(qSort);
27969 self.setupDequeueing();
27970};
27971
27972var LTCp = LayeredTextureCache.prototype;
27973var layerIdPool = 0;
27974var MAX_INT$1 = Math.pow(2, 53) - 1;
27975
27976LTCp.makeLayer = function (bb, lvl) {
27977 var scale = Math.pow(2, lvl);
27978 var w = Math.ceil(bb.w * scale);
27979 var h = Math.ceil(bb.h * scale);
27980 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27981 var layer = {
27982 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27983 bb: bb,
27984 level: lvl,
27985 width: w,
27986 height: h,
27987 canvas: canvas,
27988 context: canvas.getContext('2d'),
27989 eles: [],
27990 elesQueue: [],
27991 reqs: 0
27992 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27993
27994 var cxt = layer.context;
27995 var dx = -layer.bb.x1;
27996 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27997
27998 cxt.scale(scale, scale);
27999 cxt.translate(dx, dy);
28000 return layer;
28001};
28002
28003LTCp.getLayers = function (eles, pxRatio, lvl) {
28004 var self = this;
28005 var r = self.renderer;
28006 var cy = r.cy;
28007 var zoom = cy.zoom();
28008 var firstGet = self.firstGet;
28009 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28010 //log eles.map(function(ele){ return ele.id() }) );
28011
28012 if (lvl == null) {
28013 lvl = Math.ceil(log2(zoom * pxRatio));
28014
28015 if (lvl < minLvl$1) {
28016 lvl = minLvl$1;
28017 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
28018 return null;
28019 }
28020 }
28021
28022 self.validateLayersElesOrdering(lvl, eles);
28023 var layersByLvl = self.layersByLevel;
28024 var scale = Math.pow(2, lvl);
28025 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28026 var bb;
28027 var lvlComplete = self.levelIsComplete(lvl, eles);
28028 var tmpLayers;
28029
28030 var checkTempLevels = function checkTempLevels() {
28031 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28032 self.validateLayersElesOrdering(l, eles);
28033
28034 if (self.levelIsComplete(l, eles)) {
28035 tmpLayers = layersByLvl[l];
28036 return true;
28037 }
28038 };
28039
28040 var checkLvls = function checkLvls(dir) {
28041 if (tmpLayers) {
28042 return;
28043 }
28044
28045 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
28046 if (canUseAsTmpLvl(l)) {
28047 break;
28048 }
28049 }
28050 };
28051
28052 checkLvls(+1);
28053 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28054
28055 for (var i = layers.length - 1; i >= 0; i--) {
28056 var layer = layers[i];
28057
28058 if (layer.invalid) {
28059 removeFromArray(layers, layer);
28060 }
28061 }
28062 };
28063
28064 if (!lvlComplete) {
28065 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28066 // and later queue the current layerset so we can get the proper quality level soon
28067 checkTempLevels();
28068 } else {
28069 // log('level complete, using existing layers\n--');
28070 return layers;
28071 }
28072
28073 var getBb = function getBb() {
28074 if (!bb) {
28075 bb = makeBoundingBox();
28076
28077 for (var i = 0; i < eles.length; i++) {
28078 updateBoundingBox(bb, eles[i].boundingBox());
28079 }
28080 }
28081
28082 return bb;
28083 };
28084
28085 var makeLayer = function makeLayer(opts) {
28086 opts = opts || {};
28087 var after = opts.after;
28088 getBb();
28089 var area = bb.w * scale * (bb.h * scale);
28090
28091 if (area > maxLayerArea) {
28092 return null;
28093 }
28094
28095 var layer = self.makeLayer(bb, lvl);
28096
28097 if (after != null) {
28098 var index = layers.indexOf(after) + 1;
28099 layers.splice(index, 0, layer);
28100 } else if (opts.insert === undefined || opts.insert) {
28101 // no after specified => first layer made so put at start
28102 layers.unshift(layer);
28103 } // if( tmpLayers ){
28104 //self.queueLayer( layer );
28105 // }
28106
28107
28108 return layer;
28109 };
28110
28111 if (self.skipping && !firstGet) {
28112 // log('skip layers');
28113 return null;
28114 } // log('do layers');
28115
28116
28117 var layer = null;
28118 var maxElesPerLayer = eles.length / defNumLayers;
28119 var allowLazyQueueing = !firstGet;
28120
28121 for (var i = 0; i < eles.length; i++) {
28122 var ele = eles[i];
28123 var rs = ele._private.rscratch;
28124 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28125
28126 var existingLayer = caches[lvl];
28127
28128 if (existingLayer) {
28129 // reuse layer for later eles
28130 // log('reuse layer for', ele.id());
28131 layer = existingLayer;
28132 continue;
28133 }
28134
28135 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28136 // log('make new layer for ele %s', ele.id());
28137 layer = makeLayer({
28138 insert: true,
28139 after: layer
28140 }); // if now layer can be built then we can't use layers at this level
28141
28142 if (!layer) {
28143 return null;
28144 } // log('new layer with id %s', layer.id);
28145
28146 }
28147
28148 if (tmpLayers || allowLazyQueueing) {
28149 // log('queue ele %s in layer %s', ele.id(), layer.id);
28150 self.queueLayer(layer, ele);
28151 } else {
28152 // log('draw ele %s in layer %s', ele.id(), layer.id);
28153 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28154 }
28155
28156 layer.eles.push(ele);
28157 caches[lvl] = layer;
28158 } // log('--');
28159
28160
28161 if (tmpLayers) {
28162 // then we only queued the current layerset and can't draw it yet
28163 return tmpLayers;
28164 }
28165
28166 if (allowLazyQueueing) {
28167 // log('lazy queue level', lvl);
28168 return null;
28169 }
28170
28171 return layers;
28172}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28173// so the layer level might not equal the ele level
28174
28175
28176LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28177 return lvl;
28178};
28179
28180LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28181 var self = this;
28182 var r = this.renderer;
28183 var context = layer.context;
28184 var bb = ele.boundingBox();
28185
28186 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28187 return;
28188 }
28189
28190 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28191
28192 {
28193 r.setImgSmoothing(context, false);
28194 }
28195
28196 {
28197 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28198 }
28199
28200 {
28201 r.setImgSmoothing(context, true);
28202 }
28203};
28204
28205LTCp.levelIsComplete = function (lvl, eles) {
28206 var self = this;
28207 var layers = self.layersByLevel[lvl];
28208
28209 if (!layers || layers.length === 0) {
28210 return false;
28211 }
28212
28213 var numElesInLayers = 0;
28214
28215 for (var i = 0; i < layers.length; i++) {
28216 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28217
28218 if (layer.reqs > 0) {
28219 return false;
28220 } // if the layer is invalid, the level is not complete
28221
28222
28223 if (layer.invalid) {
28224 return false;
28225 }
28226
28227 numElesInLayers += layer.eles.length;
28228 } // we should have exactly the number of eles passed in to be complete
28229
28230
28231 if (numElesInLayers !== eles.length) {
28232 return false;
28233 }
28234
28235 return true;
28236};
28237
28238LTCp.validateLayersElesOrdering = function (lvl, eles) {
28239 var layers = this.layersByLevel[lvl];
28240
28241 if (!layers) {
28242 return;
28243 } // if in a layer the eles are not in the same order, then the layer is invalid
28244 // (i.e. there is an ele in between the eles in the layer)
28245
28246
28247 for (var i = 0; i < layers.length; i++) {
28248 var layer = layers[i];
28249 var offset = -1; // find the offset
28250
28251 for (var j = 0; j < eles.length; j++) {
28252 if (layer.eles[0] === eles[j]) {
28253 offset = j;
28254 break;
28255 }
28256 }
28257
28258 if (offset < 0) {
28259 // then the layer has nonexistant elements and is invalid
28260 this.invalidateLayer(layer);
28261 continue;
28262 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28263
28264
28265 var o = offset;
28266
28267 for (var j = 0; j < layer.eles.length; j++) {
28268 if (layer.eles[j] !== eles[o + j]) {
28269 // log('invalidate based on ordering', layer.id);
28270 this.invalidateLayer(layer);
28271 break;
28272 }
28273 }
28274 }
28275};
28276
28277LTCp.updateElementsInLayers = function (eles, update) {
28278 var self = this;
28279 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28280 // layer itself along the way
28281
28282 for (var i = 0; i < eles.length; i++) {
28283 var req = isEles ? null : eles[i];
28284 var ele = isEles ? eles[i] : eles[i].ele;
28285 var rs = ele._private.rscratch;
28286 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28287
28288 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28289 var layer = caches[l];
28290
28291 if (!layer) {
28292 continue;
28293 } // if update is a request from the ele cache, then it affects only
28294 // the matching level
28295
28296
28297 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28298 continue;
28299 }
28300
28301 update(layer, ele, req);
28302 }
28303 }
28304};
28305
28306LTCp.haveLayers = function () {
28307 var self = this;
28308 var haveLayers = false;
28309
28310 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28311 var layers = self.layersByLevel[l];
28312
28313 if (layers && layers.length > 0) {
28314 haveLayers = true;
28315 break;
28316 }
28317 }
28318
28319 return haveLayers;
28320};
28321
28322LTCp.invalidateElements = function (eles) {
28323 var self = this;
28324
28325 if (eles.length === 0) {
28326 return;
28327 }
28328
28329 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28330
28331 if (eles.length === 0 || !self.haveLayers()) {
28332 return;
28333 }
28334
28335 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28336 self.invalidateLayer(layer);
28337 });
28338};
28339
28340LTCp.invalidateLayer = function (layer) {
28341 // log('update invalidate layer time');
28342 this.lastInvalidationTime = performanceNow();
28343
28344 if (layer.invalid) {
28345 return;
28346 } // save cycles
28347
28348
28349 var lvl = layer.level;
28350 var eles = layer.eles;
28351 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28352
28353 removeFromArray(layers, layer); // layer.eles = [];
28354
28355 layer.elesQueue = [];
28356 layer.invalid = true;
28357
28358 if (layer.replacement) {
28359 layer.replacement.invalid = true;
28360 }
28361
28362 for (var i = 0; i < eles.length; i++) {
28363 var caches = eles[i]._private.rscratch.imgLayerCaches;
28364
28365 if (caches) {
28366 caches[lvl] = null;
28367 }
28368 }
28369};
28370
28371LTCp.refineElementTextures = function (eles) {
28372 var self = this; // log('refine', eles.length);
28373
28374 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28375 var rLyr = layer.replacement;
28376
28377 if (!rLyr) {
28378 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28379 rLyr.replaces = layer;
28380 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28381 }
28382
28383 if (!rLyr.reqs) {
28384 for (var i = 0; i < rLyr.eles.length; i++) {
28385 self.queueLayer(rLyr, rLyr.eles[i]);
28386 } // log('queue replacement layer refinement', rLyr.id);
28387
28388 }
28389 });
28390};
28391
28392LTCp.enqueueElementRefinement = function (ele) {
28393
28394 this.eleTxrDeqs.merge(ele);
28395 this.scheduleElementRefinement();
28396};
28397
28398LTCp.queueLayer = function (layer, ele) {
28399 var self = this;
28400 var q = self.layersQueue;
28401 var elesQ = layer.elesQueue;
28402 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28403
28404 if (layer.replacement) {
28405 return;
28406 }
28407
28408 if (ele) {
28409 if (hasId[ele.id()]) {
28410 return;
28411 }
28412
28413 elesQ.push(ele);
28414 hasId[ele.id()] = true;
28415 }
28416
28417 if (layer.reqs) {
28418 layer.reqs++;
28419 q.updateItem(layer);
28420 } else {
28421 layer.reqs = 1;
28422 q.push(layer);
28423 }
28424};
28425
28426LTCp.dequeue = function (pxRatio) {
28427 var self = this;
28428 var q = self.layersQueue;
28429 var deqd = [];
28430 var eleDeqs = 0;
28431
28432 while (eleDeqs < maxDeqSize$1) {
28433 if (q.size() === 0) {
28434 break;
28435 }
28436
28437 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28438
28439 if (layer.replacement) {
28440 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28441 q.pop();
28442 continue;
28443 } // if this is a replacement layer that has been superceded, then forget it
28444
28445
28446 if (layer.replaces && layer !== layer.replaces.replacement) {
28447 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28448 q.pop();
28449 continue;
28450 }
28451
28452 if (layer.invalid) {
28453 // log('replacement layer %s is invalid; dequeued', layer.id);
28454 q.pop();
28455 continue;
28456 }
28457
28458 var ele = layer.elesQueue.shift();
28459
28460 if (ele) {
28461 // log('dequeue layer %s', layer.id);
28462 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28463 eleDeqs++;
28464 }
28465
28466 if (deqd.length === 0) {
28467 // we need only one entry in deqd to queue redrawing etc
28468 deqd.push(true);
28469 } // if the layer has all its eles done, then remove from the queue
28470
28471
28472 if (layer.elesQueue.length === 0) {
28473 q.pop();
28474 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28475 // when a replacement layer is dequeued, it replaces the old layer in the level
28476
28477 if (layer.replaces) {
28478 self.applyLayerReplacement(layer);
28479 }
28480
28481 self.requestRedraw();
28482 }
28483 }
28484
28485 return deqd;
28486};
28487
28488LTCp.applyLayerReplacement = function (layer) {
28489 var self = this;
28490 var layersInLevel = self.layersByLevel[layer.level];
28491 var replaced = layer.replaces;
28492 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28493 // refs would be a mistake (i.e. overwriting the true active layer)
28494
28495 if (index < 0 || replaced.invalid) {
28496 // log('replacement layer would have no effect', layer.id);
28497 return;
28498 }
28499
28500 layersInLevel[index] = layer; // replace level ref
28501 // replace refs in eles
28502
28503 for (var i = 0; i < layer.eles.length; i++) {
28504 var _p = layer.eles[i]._private;
28505 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28506
28507 if (cache) {
28508 cache[layer.level] = layer;
28509 }
28510 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28511
28512
28513 self.requestRedraw();
28514};
28515
28516LTCp.requestRedraw = util(function () {
28517 var r = this.renderer;
28518 r.redrawHint('eles', true);
28519 r.redrawHint('drag', true);
28520 r.redraw();
28521}, 100);
28522LTCp.setupDequeueing = defs.setupDequeueing({
28523 deqRedrawThreshold: deqRedrawThreshold$1,
28524 deqCost: deqCost$1,
28525 deqAvgCost: deqAvgCost$1,
28526 deqNoDrawCost: deqNoDrawCost$1,
28527 deqFastCost: deqFastCost$1,
28528 deq: function deq(self, pxRatio) {
28529 return self.dequeue(pxRatio);
28530 },
28531 onDeqd: noop,
28532 shouldRedraw: trueify,
28533 priority: function priority(self) {
28534 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28535 }
28536});
28537
28538var CRp = {};
28539var impl;
28540
28541function polygon(context, points) {
28542 for (var i = 0; i < points.length; i++) {
28543 var pt = points[i];
28544 context.lineTo(pt.x, pt.y);
28545 }
28546}
28547
28548function triangleBackcurve(context, points, controlPoint) {
28549 var firstPt;
28550
28551 for (var i = 0; i < points.length; i++) {
28552 var pt = points[i];
28553
28554 if (i === 0) {
28555 firstPt = pt;
28556 }
28557
28558 context.lineTo(pt.x, pt.y);
28559 }
28560
28561 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28562}
28563
28564function triangleTee(context, trianglePoints, teePoints) {
28565 if (context.beginPath) {
28566 context.beginPath();
28567 }
28568
28569 var triPts = trianglePoints;
28570
28571 for (var i = 0; i < triPts.length; i++) {
28572 var pt = triPts[i];
28573 context.lineTo(pt.x, pt.y);
28574 }
28575
28576 var teePts = teePoints;
28577 var firstTeePt = teePoints[0];
28578 context.moveTo(firstTeePt.x, firstTeePt.y);
28579
28580 for (var i = 1; i < teePts.length; i++) {
28581 var pt = teePts[i];
28582 context.lineTo(pt.x, pt.y);
28583 }
28584
28585 if (context.closePath) {
28586 context.closePath();
28587 }
28588}
28589
28590function circleTriangle(context, trianglePoints, rx, ry, r) {
28591 if (context.beginPath) {
28592 context.beginPath();
28593 }
28594
28595 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28596 var triPts = trianglePoints;
28597 var firstTrPt = triPts[0];
28598 context.moveTo(firstTrPt.x, firstTrPt.y);
28599
28600 for (var i = 0; i < triPts.length; i++) {
28601 var pt = triPts[i];
28602 context.lineTo(pt.x, pt.y);
28603 }
28604
28605 if (context.closePath) {
28606 context.closePath();
28607 }
28608}
28609
28610function circle(context, rx, ry, r) {
28611 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28612}
28613
28614CRp.arrowShapeImpl = function (name) {
28615 return (impl || (impl = {
28616 'polygon': polygon,
28617 'triangle-backcurve': triangleBackcurve,
28618 'triangle-tee': triangleTee,
28619 'circle-triangle': circleTriangle,
28620 'triangle-cross': triangleTee,
28621 'circle': circle
28622 }))[name];
28623};
28624
28625var CRp$1 = {};
28626
28627CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28628 var r = this;
28629
28630 if (ele.isNode()) {
28631 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28632 } else {
28633 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28634 }
28635};
28636
28637CRp$1.drawElementOverlay = function (context, ele) {
28638 var r = this;
28639
28640 if (ele.isNode()) {
28641 r.drawNodeOverlay(context, ele);
28642 } else {
28643 r.drawEdgeOverlay(context, ele);
28644 }
28645};
28646
28647CRp$1.drawElementUnderlay = function (context, ele) {
28648 var r = this;
28649
28650 if (ele.isNode()) {
28651 r.drawNodeUnderlay(context, ele);
28652 } else {
28653 r.drawEdgeUnderlay(context, ele);
28654 }
28655};
28656
28657CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28658 var r = this;
28659 var bb = eleTxrCache.getBoundingBox(ele);
28660
28661 if (bb.w === 0 || bb.h === 0) {
28662 return;
28663 } // ignore zero size case
28664
28665
28666 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28667
28668 if (eleCache != null) {
28669 var opacity = getOpacity(r, ele);
28670
28671 if (opacity === 0) {
28672 return;
28673 }
28674
28675 var theta = getRotation(r, ele);
28676 var x1 = bb.x1,
28677 y1 = bb.y1,
28678 w = bb.w,
28679 h = bb.h;
28680 var x, y, sx, sy, smooth;
28681
28682 if (theta !== 0) {
28683 var rotPt = eleTxrCache.getRotationPoint(ele);
28684 sx = rotPt.x;
28685 sy = rotPt.y;
28686 context.translate(sx, sy);
28687 context.rotate(theta);
28688 smooth = r.getImgSmoothing(context);
28689
28690 if (!smooth) {
28691 r.setImgSmoothing(context, true);
28692 }
28693
28694 var off = eleTxrCache.getRotationOffset(ele);
28695 x = off.x;
28696 y = off.y;
28697 } else {
28698 x = x1;
28699 y = y1;
28700 }
28701
28702 var oldGlobalAlpha;
28703
28704 if (opacity !== 1) {
28705 oldGlobalAlpha = context.globalAlpha;
28706 context.globalAlpha = oldGlobalAlpha * opacity;
28707 }
28708
28709 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28710
28711 if (opacity !== 1) {
28712 context.globalAlpha = oldGlobalAlpha;
28713 }
28714
28715 if (theta !== 0) {
28716 context.rotate(-theta);
28717 context.translate(-sx, -sy);
28718
28719 if (!smooth) {
28720 r.setImgSmoothing(context, false);
28721 }
28722 }
28723 } else {
28724 eleTxrCache.drawElement(context, ele); // direct draw fallback
28725 }
28726};
28727
28728var getZeroRotation = function getZeroRotation() {
28729 return 0;
28730};
28731
28732var getLabelRotation = function getLabelRotation(r, ele) {
28733 return r.getTextAngle(ele, null);
28734};
28735
28736var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28737 return r.getTextAngle(ele, 'source');
28738};
28739
28740var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28741 return r.getTextAngle(ele, 'target');
28742};
28743
28744var getOpacity = function getOpacity(r, ele) {
28745 return ele.effectiveOpacity();
28746};
28747
28748var getTextOpacity = function getTextOpacity(e, ele) {
28749 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28750};
28751
28752CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28753 var r = this;
28754 var _r$data = r.data,
28755 eleTxrCache = _r$data.eleTxrCache,
28756 lblTxrCache = _r$data.lblTxrCache,
28757 slbTxrCache = _r$data.slbTxrCache,
28758 tlbTxrCache = _r$data.tlbTxrCache;
28759 var bb = ele.boundingBox();
28760 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28761
28762 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28763 return;
28764 }
28765
28766 if (!extent || boundingBoxesIntersect(bb, extent)) {
28767 var isEdge = ele.isEdge();
28768
28769 var badLine = ele.element()._private.rscratch.badLine;
28770
28771 r.drawElementUnderlay(context, ele);
28772 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28773
28774 if (!isEdge || !badLine) {
28775 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28776 }
28777
28778 if (isEdge && !badLine) {
28779 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28780 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28781 }
28782
28783 r.drawElementOverlay(context, ele);
28784 }
28785};
28786
28787CRp$1.drawElements = function (context, eles) {
28788 var r = this;
28789
28790 for (var i = 0; i < eles.length; i++) {
28791 var ele = eles[i];
28792 r.drawElement(context, ele);
28793 }
28794};
28795
28796CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28797 var r = this;
28798
28799 for (var i = 0; i < eles.length; i++) {
28800 var ele = eles[i];
28801 r.drawCachedElement(context, ele, pxRatio, extent);
28802 }
28803};
28804
28805CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28806 var r = this;
28807
28808 for (var i = 0; i < eles.length; i++) {
28809 var ele = eles[i];
28810
28811 if (!ele.isNode()) {
28812 continue;
28813 }
28814
28815 r.drawCachedElement(context, ele, pxRatio, extent);
28816 }
28817};
28818
28819CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28820 var r = this;
28821 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28822
28823 if (layers) {
28824 for (var i = 0; i < layers.length; i++) {
28825 var layer = layers[i];
28826 var bb = layer.bb;
28827
28828 if (bb.w === 0 || bb.h === 0) {
28829 continue;
28830 }
28831
28832 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28833 }
28834 } else {
28835 // fall back on plain caching if no layers
28836 r.drawCachedElements(context, eles, pxRatio, extent);
28837 }
28838};
28839
28840/* global Path2D */
28841var CRp$2 = {};
28842
28843CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28844 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28845 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28846 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28847 var r = this;
28848 var rs = edge._private.rscratch;
28849
28850 if (shouldDrawOpacity && !edge.visible()) {
28851 return;
28852 } // if bezier ctrl pts can not be calculated, then die
28853
28854
28855 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28856 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28857 return;
28858 }
28859
28860 var bb;
28861
28862 if (shiftToOriginWithBb) {
28863 bb = shiftToOriginWithBb;
28864 context.translate(-bb.x1, -bb.y1);
28865 }
28866
28867 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28868 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28869 var curveStyle = edge.pstyle('curve-style').value;
28870 var lineStyle = edge.pstyle('line-style').value;
28871 var edgeWidth = edge.pstyle('width').pfValue;
28872 var lineCap = edge.pstyle('line-cap').value;
28873 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28874
28875 var effectiveArrowOpacity = opacity * lineOpacity;
28876
28877 var drawLine = function drawLine() {
28878 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28879
28880 if (curveStyle === 'straight-triangle') {
28881 r.eleStrokeStyle(context, edge, strokeOpacity);
28882 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28883 } else {
28884 context.lineWidth = edgeWidth;
28885 context.lineCap = lineCap;
28886 r.eleStrokeStyle(context, edge, strokeOpacity);
28887 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28888 context.lineCap = 'butt'; // reset for other drawing functions
28889 }
28890 };
28891
28892 var drawOverlay = function drawOverlay() {
28893 if (!shouldDrawOverlay) {
28894 return;
28895 }
28896
28897 r.drawEdgeOverlay(context, edge);
28898 };
28899
28900 var drawUnderlay = function drawUnderlay() {
28901 if (!shouldDrawOverlay) {
28902 return;
28903 }
28904
28905 r.drawEdgeUnderlay(context, edge);
28906 };
28907
28908 var drawArrows = function drawArrows() {
28909 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28910 r.drawArrowheads(context, edge, arrowOpacity);
28911 };
28912
28913 var drawText = function drawText() {
28914 r.drawElementText(context, edge, null, drawLabel);
28915 };
28916
28917 context.lineJoin = 'round';
28918 var ghost = edge.pstyle('ghost').value === 'yes';
28919
28920 if (ghost) {
28921 var gx = edge.pstyle('ghost-offset-x').pfValue;
28922 var gy = edge.pstyle('ghost-offset-y').pfValue;
28923 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28924 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28925 context.translate(gx, gy);
28926 drawLine(effectiveGhostOpacity);
28927 drawArrows(effectiveGhostOpacity);
28928 context.translate(-gx, -gy);
28929 }
28930
28931 drawUnderlay();
28932 drawLine();
28933 drawArrows();
28934 drawOverlay();
28935 drawText();
28936
28937 if (shiftToOriginWithBb) {
28938 context.translate(bb.x1, bb.y1);
28939 }
28940};
28941
28942var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28943 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28944 throw new Error('Invalid state');
28945 }
28946
28947 return function (context, edge) {
28948 if (!edge.visible()) {
28949 return;
28950 }
28951
28952 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28953
28954 if (opacity === 0) {
28955 return;
28956 }
28957
28958 var r = this;
28959 var usePaths = r.usePaths();
28960 var rs = edge._private.rscratch;
28961 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28962 var width = 2 * padding;
28963 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28964 context.lineWidth = width;
28965
28966 if (rs.edgeType === 'self' && !usePaths) {
28967 context.lineCap = 'butt';
28968 } else {
28969 context.lineCap = 'round';
28970 }
28971
28972 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28973 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28974 };
28975};
28976
28977CRp$2.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
28978CRp$2.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
28979
28980CRp$2.drawEdgePath = function (edge, context, pts, type) {
28981 var rs = edge._private.rscratch;
28982 var canvasCxt = context;
28983 var path;
28984 var pathCacheHit = false;
28985 var usePaths = this.usePaths();
28986 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28987 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28988
28989 if (usePaths) {
28990 var pathCacheKey = pts.join('$');
28991 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28992
28993 if (keyMatches) {
28994 path = context = rs.pathCache;
28995 pathCacheHit = true;
28996 } else {
28997 path = context = new Path2D();
28998 rs.pathCacheKey = pathCacheKey;
28999 rs.pathCache = path;
29000 }
29001 }
29002
29003 if (canvasCxt.setLineDash) {
29004 // for very outofdate browsers
29005 switch (type) {
29006 case 'dotted':
29007 canvasCxt.setLineDash([1, 1]);
29008 break;
29009
29010 case 'dashed':
29011 canvasCxt.setLineDash(lineDashPattern);
29012 canvasCxt.lineDashOffset = lineDashOffset;
29013 break;
29014
29015 case 'solid':
29016 canvasCxt.setLineDash([]);
29017 break;
29018 }
29019 }
29020
29021 if (!pathCacheHit && !rs.badLine) {
29022 if (context.beginPath) {
29023 context.beginPath();
29024 }
29025
29026 context.moveTo(pts[0], pts[1]);
29027
29028 switch (rs.edgeType) {
29029 case 'bezier':
29030 case 'self':
29031 case 'compound':
29032 case 'multibezier':
29033 for (var i = 2; i + 3 < pts.length; i += 4) {
29034 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29035 }
29036
29037 break;
29038
29039 case 'straight':
29040 case 'segments':
29041 case 'haystack':
29042 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29043 context.lineTo(pts[_i], pts[_i + 1]);
29044 }
29045
29046 break;
29047 }
29048 }
29049
29050 context = canvasCxt;
29051
29052 if (usePaths) {
29053 context.stroke(path);
29054 } else {
29055 context.stroke();
29056 } // reset any line dashes
29057
29058
29059 if (context.setLineDash) {
29060 // for very outofdate browsers
29061 context.setLineDash([]);
29062 }
29063};
29064
29065CRp$2.drawEdgeTrianglePath = function (edge, context, pts) {
29066 // use line stroke style for triangle fill style
29067 context.fillStyle = context.strokeStyle;
29068 var edgeWidth = edge.pstyle('width').pfValue;
29069
29070 for (var i = 0; i + 1 < pts.length; i += 2) {
29071 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
29072 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
29073 var normal = [vector[1] / length, -vector[0] / length];
29074 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
29075 context.beginPath();
29076 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
29077 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
29078 context.lineTo(pts[i + 2], pts[i + 3]);
29079 context.closePath();
29080 context.fill();
29081 }
29082};
29083
29084CRp$2.drawArrowheads = function (context, edge, opacity) {
29085 var rs = edge._private.rscratch;
29086 var isHaystack = rs.edgeType === 'haystack';
29087
29088 if (!isHaystack) {
29089 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29090 }
29091
29092 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29093 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29094
29095 if (!isHaystack) {
29096 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29097 }
29098};
29099
29100CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29101 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29102 return;
29103 }
29104
29105 var self = this;
29106 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29107
29108 if (arrowShape === 'none') {
29109 return;
29110 }
29111
29112 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29113 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29114 var edgeWidth = edge.pstyle('width').pfValue;
29115 var edgeOpacity = edge.pstyle('opacity').value;
29116
29117 if (opacity === undefined) {
29118 opacity = edgeOpacity;
29119 }
29120
29121 var gco = context.globalCompositeOperation;
29122
29123 if (opacity !== 1 || arrowFill === 'hollow') {
29124 // then extra clear is needed
29125 context.globalCompositeOperation = 'destination-out';
29126 self.colorFillStyle(context, 255, 255, 255, 1);
29127 self.colorStrokeStyle(context, 255, 255, 255, 1);
29128 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29129 context.globalCompositeOperation = gco;
29130 } // otherwise, the opaque arrow clears it for free :)
29131
29132
29133 var color = edge.pstyle(prefix + '-arrow-color').value;
29134 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29135 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29136 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29137};
29138
29139CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29140 var r = this;
29141 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29142 var pathCacheHit = false;
29143 var path;
29144 var canvasContext = context;
29145 var translation = {
29146 x: x,
29147 y: y
29148 };
29149 var scale = edge.pstyle('arrow-scale').value;
29150 var size = this.getArrowWidth(edgeWidth, scale);
29151 var shapeImpl = r.arrowShapes[shape];
29152
29153 if (usePaths) {
29154 var cache = r.arrowPathCache = r.arrowPathCache || [];
29155 var key = hashString(shape);
29156 var cachedPath = cache[key];
29157
29158 if (cachedPath != null) {
29159 path = context = cachedPath;
29160 pathCacheHit = true;
29161 } else {
29162 path = context = new Path2D();
29163 cache[key] = path;
29164 }
29165 }
29166
29167 if (!pathCacheHit) {
29168 if (context.beginPath) {
29169 context.beginPath();
29170 }
29171
29172 if (usePaths) {
29173 // store in the path cache with values easily manipulated later
29174 shapeImpl.draw(context, 1, 0, {
29175 x: 0,
29176 y: 0
29177 }, 1);
29178 } else {
29179 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29180 }
29181
29182 if (context.closePath) {
29183 context.closePath();
29184 }
29185 }
29186
29187 context = canvasContext;
29188
29189 if (usePaths) {
29190 // set transform to arrow position/orientation
29191 context.translate(x, y);
29192 context.rotate(angle);
29193 context.scale(size, size);
29194 }
29195
29196 if (fill === 'filled' || fill === 'both') {
29197 if (usePaths) {
29198 context.fill(path);
29199 } else {
29200 context.fill();
29201 }
29202 }
29203
29204 if (fill === 'hollow' || fill === 'both') {
29205 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29206 context.lineJoin = 'miter';
29207
29208 if (usePaths) {
29209 context.stroke(path);
29210 } else {
29211 context.stroke();
29212 }
29213 }
29214
29215 if (usePaths) {
29216 // reset transform by applying inverse
29217 context.scale(1 / size, 1 / size);
29218 context.rotate(-angle);
29219 context.translate(-x, -y);
29220 }
29221};
29222
29223var CRp$3 = {};
29224
29225CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29226 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29227 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29228 return;
29229 }
29230
29231 try {
29232 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29233 } catch (e) {
29234 warn(e);
29235 }
29236};
29237
29238CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29239 var r = this;
29240 var pos = node.position();
29241 var nodeX = pos.x;
29242 var nodeY = pos.y;
29243 var styleObj = node.cy().style();
29244 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29245 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29246 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29247 var nodeW = node.width();
29248 var nodeH = node.height();
29249 var paddingX2 = node.padding() * 2;
29250 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29251 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29252 var rs = node._private.rscratch;
29253 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29254 var shouldClip = clip === 'node';
29255 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29256 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29257 var imgW = img.width || img.cachedW;
29258 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29259
29260 if (null == imgW || null == imgH) {
29261 document.body.appendChild(img); // eslint-disable-line no-undef
29262
29263 imgW = img.cachedW = img.width || img.offsetWidth;
29264 imgH = img.cachedH = img.height || img.offsetHeight;
29265 document.body.removeChild(img); // eslint-disable-line no-undef
29266 }
29267
29268 var w = imgW;
29269 var h = imgH;
29270
29271 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29272 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29273 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29274 } else {
29275 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29276 }
29277 }
29278
29279 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29280 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29281 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29282 } else {
29283 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29284 }
29285 }
29286
29287 if (w === 0 || h === 0) {
29288 return; // no point in drawing empty image (and chrome is broken in this case)
29289 }
29290
29291 if (fit === 'contain') {
29292 var scale = Math.min(nodeTW / w, nodeTH / h);
29293 w *= scale;
29294 h *= scale;
29295 } else if (fit === 'cover') {
29296 var scale = Math.max(nodeTW / w, nodeTH / h);
29297 w *= scale;
29298 h *= scale;
29299 }
29300
29301 var x = nodeX - nodeTW / 2; // left
29302
29303 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29304 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29305
29306 if (posXUnits === '%') {
29307 x += (nodeTW - w) * posXPfVal;
29308 } else {
29309 x += posXPfVal;
29310 }
29311
29312 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29313 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29314
29315 if (offXUnits === '%') {
29316 x += (nodeTW - w) * offXPfVal;
29317 } else {
29318 x += offXPfVal;
29319 }
29320
29321 var y = nodeY - nodeTH / 2; // top
29322
29323 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29324 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29325
29326 if (posYUnits === '%') {
29327 y += (nodeTH - h) * posYPfVal;
29328 } else {
29329 y += posYPfVal;
29330 }
29331
29332 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29333 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29334
29335 if (offYUnits === '%') {
29336 y += (nodeTH - h) * offYPfVal;
29337 } else {
29338 y += offYPfVal;
29339 }
29340
29341 if (rs.pathCache) {
29342 x -= nodeX;
29343 y -= nodeY;
29344 nodeX = 0;
29345 nodeY = 0;
29346 }
29347
29348 var gAlpha = context.globalAlpha;
29349 context.globalAlpha = imgOpacity;
29350 var smoothingEnabled = r.getImgSmoothing(context);
29351 var isSmoothingSwitched = false;
29352
29353 if (smooth === 'no' && smoothingEnabled) {
29354 r.setImgSmoothing(context, false);
29355 isSmoothingSwitched = true;
29356 } else if (smooth === 'yes' && !smoothingEnabled) {
29357 r.setImgSmoothing(context, true);
29358 isSmoothingSwitched = true;
29359 }
29360
29361 if (repeat === 'no-repeat') {
29362 if (shouldClip) {
29363 context.save();
29364
29365 if (rs.pathCache) {
29366 context.clip(rs.pathCache);
29367 } else {
29368 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29369 context.clip();
29370 }
29371 }
29372
29373 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29374
29375 if (shouldClip) {
29376 context.restore();
29377 }
29378 } else {
29379 var pattern = context.createPattern(img, repeat);
29380 context.fillStyle = pattern;
29381 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29382 context.translate(x, y);
29383 context.fill();
29384 context.translate(-x, -y);
29385 }
29386
29387 context.globalAlpha = gAlpha;
29388
29389 if (isSmoothingSwitched) {
29390 r.setImgSmoothing(context, smoothingEnabled);
29391 }
29392};
29393
29394var CRp$4 = {};
29395
29396CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29397 if (!scale) {
29398 var zoom = ele.cy().zoom();
29399 var pxRatio = this.getPixelRatio();
29400 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29401
29402 scale = Math.pow(2, lvl);
29403 }
29404
29405 var computedSize = ele.pstyle('font-size').pfValue * scale;
29406 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29407
29408 if (computedSize < minSize) {
29409 return false;
29410 }
29411
29412 return true;
29413};
29414
29415CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29416 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29417 var r = this;
29418
29419 if (force == null) {
29420 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29421 return;
29422 }
29423 } else if (force === false) {
29424 return;
29425 }
29426
29427 if (ele.isNode()) {
29428 var label = ele.pstyle('label');
29429
29430 if (!label || !label.value) {
29431 return;
29432 }
29433
29434 var justification = r.getLabelJustification(ele);
29435 context.textAlign = justification;
29436 context.textBaseline = 'bottom';
29437 } else {
29438 var badLine = ele.element()._private.rscratch.badLine;
29439
29440 var _label = ele.pstyle('label');
29441
29442 var srcLabel = ele.pstyle('source-label');
29443 var tgtLabel = ele.pstyle('target-label');
29444
29445 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29446 return;
29447 }
29448
29449 context.textAlign = 'center';
29450 context.textBaseline = 'bottom';
29451 }
29452
29453 var applyRotation = !shiftToOriginWithBb;
29454 var bb;
29455
29456 if (shiftToOriginWithBb) {
29457 bb = shiftToOriginWithBb;
29458 context.translate(-bb.x1, -bb.y1);
29459 }
29460
29461 if (prefix == null) {
29462 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29463
29464 if (ele.isEdge()) {
29465 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29466 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29467 }
29468 } else {
29469 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29470 }
29471
29472 if (shiftToOriginWithBb) {
29473 context.translate(bb.x1, bb.y1);
29474 }
29475};
29476
29477CRp$4.getFontCache = function (context) {
29478 var cache;
29479 this.fontCaches = this.fontCaches || [];
29480
29481 for (var i = 0; i < this.fontCaches.length; i++) {
29482 cache = this.fontCaches[i];
29483
29484 if (cache.context === context) {
29485 return cache;
29486 }
29487 }
29488
29489 cache = {
29490 context: context
29491 };
29492 this.fontCaches.push(cache);
29493 return cache;
29494}; // set up canvas context with font
29495// returns transformed text string
29496
29497
29498CRp$4.setupTextStyle = function (context, ele) {
29499 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29500 // Font style
29501 var labelStyle = ele.pstyle('font-style').strValue;
29502 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29503 var labelFamily = ele.pstyle('font-family').strValue;
29504 var labelWeight = ele.pstyle('font-weight').strValue;
29505 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29506 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29507 var color = ele.pstyle('color').value;
29508 var outlineColor = ele.pstyle('text-outline-color').value;
29509 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29510 context.lineJoin = 'round'; // so text outlines aren't jagged
29511
29512 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29513 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29514}; // TODO ensure re-used
29515
29516
29517function roundRect(ctx, x, y, width, height) {
29518 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29519 ctx.beginPath();
29520 ctx.moveTo(x + radius, y);
29521 ctx.lineTo(x + width - radius, y);
29522 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29523 ctx.lineTo(x + width, y + height - radius);
29524 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29525 ctx.lineTo(x + radius, y + height);
29526 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29527 ctx.lineTo(x, y + radius);
29528 ctx.quadraticCurveTo(x, y, x + radius, y);
29529 ctx.closePath();
29530 ctx.fill();
29531}
29532
29533CRp$4.getTextAngle = function (ele, prefix) {
29534 var theta;
29535 var _p = ele._private;
29536 var rscratch = _p.rscratch;
29537 var pdash = prefix ? prefix + '-' : '';
29538 var rotation = ele.pstyle(pdash + 'text-rotation');
29539 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29540
29541 if (rotation.strValue === 'autorotate') {
29542 theta = ele.isEdge() ? textAngle : 0;
29543 } else if (rotation.strValue === 'none') {
29544 theta = 0;
29545 } else {
29546 theta = rotation.pfValue;
29547 }
29548
29549 return theta;
29550};
29551
29552CRp$4.drawText = function (context, ele, prefix) {
29553 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29554 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29555 var _p = ele._private;
29556 var rscratch = _p.rscratch;
29557 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29558
29559 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29560 return;
29561 } // use 'main' as an alias for the main label (i.e. null prefix)
29562
29563
29564 if (prefix === 'main') {
29565 prefix = null;
29566 }
29567
29568 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29569 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29570 var orgTextX, orgTextY; // used for rotation
29571
29572 var text = this.getLabelText(ele, prefix);
29573
29574 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29575 this.setupTextStyle(context, ele, useEleOpacity);
29576 var pdash = prefix ? prefix + '-' : '';
29577 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29578 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29579 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29580 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29581 var isEdge = ele.isEdge();
29582 var halign = ele.pstyle('text-halign').value;
29583 var valign = ele.pstyle('text-valign').value;
29584
29585 if (isEdge) {
29586 halign = 'center';
29587 valign = 'center';
29588 }
29589
29590 textX += marginX;
29591 textY += marginY;
29592 var theta;
29593
29594 if (!applyRotation) {
29595 theta = 0;
29596 } else {
29597 theta = this.getTextAngle(ele, prefix);
29598 }
29599
29600 if (theta !== 0) {
29601 orgTextX = textX;
29602 orgTextY = textY;
29603 context.translate(orgTextX, orgTextY);
29604 context.rotate(theta);
29605 textX = 0;
29606 textY = 0;
29607 }
29608
29609 switch (valign) {
29610 case 'top':
29611 break;
29612
29613 case 'center':
29614 textY += textH / 2;
29615 break;
29616
29617 case 'bottom':
29618 textY += textH;
29619 break;
29620 }
29621
29622 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29623 var borderOpacity = ele.pstyle('text-border-opacity').value;
29624 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29625 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29626
29627 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29628 var bgX = textX - backgroundPadding;
29629
29630 switch (halign) {
29631 case 'left':
29632 bgX -= textW;
29633 break;
29634
29635 case 'center':
29636 bgX -= textW / 2;
29637 break;
29638 }
29639
29640 var bgY = textY - textH - backgroundPadding;
29641 var bgW = textW + 2 * backgroundPadding;
29642 var bgH = textH + 2 * backgroundPadding;
29643
29644 if (backgroundOpacity > 0) {
29645 var textFill = context.fillStyle;
29646 var textBackgroundColor = ele.pstyle('text-background-color').value;
29647 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29648 var styleShape = ele.pstyle('text-background-shape').strValue;
29649
29650 if (styleShape.indexOf('round') === 0) {
29651 roundRect(context, bgX, bgY, bgW, bgH, 2);
29652 } else {
29653 context.fillRect(bgX, bgY, bgW, bgH);
29654 }
29655
29656 context.fillStyle = textFill;
29657 }
29658
29659 if (textBorderWidth > 0 && borderOpacity > 0) {
29660 var textStroke = context.strokeStyle;
29661 var textLineWidth = context.lineWidth;
29662 var textBorderColor = ele.pstyle('text-border-color').value;
29663 var textBorderStyle = ele.pstyle('text-border-style').value;
29664 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29665 context.lineWidth = textBorderWidth;
29666
29667 if (context.setLineDash) {
29668 // for very outofdate browsers
29669 switch (textBorderStyle) {
29670 case 'dotted':
29671 context.setLineDash([1, 1]);
29672 break;
29673
29674 case 'dashed':
29675 context.setLineDash([4, 2]);
29676 break;
29677
29678 case 'double':
29679 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29680
29681 context.setLineDash([]);
29682 break;
29683
29684 case 'solid':
29685 context.setLineDash([]);
29686 break;
29687 }
29688 }
29689
29690 context.strokeRect(bgX, bgY, bgW, bgH);
29691
29692 if (textBorderStyle === 'double') {
29693 var whiteWidth = textBorderWidth / 2;
29694 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29695 }
29696
29697 if (context.setLineDash) {
29698 // for very outofdate browsers
29699 context.setLineDash([]);
29700 }
29701
29702 context.lineWidth = textLineWidth;
29703 context.strokeStyle = textStroke;
29704 }
29705 }
29706
29707 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29708
29709 if (lineWidth > 0) {
29710 context.lineWidth = lineWidth;
29711 }
29712
29713 if (ele.pstyle('text-wrap').value === 'wrap') {
29714 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29715 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29716 var halfTextW = textW / 2;
29717 var justification = this.getLabelJustification(ele);
29718
29719 if (justification === 'auto') ; else if (halign === 'left') {
29720 // auto justification : right
29721 if (justification === 'left') {
29722 textX += -textW;
29723 } else if (justification === 'center') {
29724 textX += -halfTextW;
29725 } // else same as auto
29726
29727 } else if (halign === 'center') {
29728 // auto justfication : center
29729 if (justification === 'left') {
29730 textX += -halfTextW;
29731 } else if (justification === 'right') {
29732 textX += halfTextW;
29733 } // else same as auto
29734
29735 } else if (halign === 'right') {
29736 // auto justification : left
29737 if (justification === 'center') {
29738 textX += halfTextW;
29739 } else if (justification === 'right') {
29740 textX += textW;
29741 } // else same as auto
29742
29743 }
29744
29745 switch (valign) {
29746 case 'top':
29747 textY -= (lines.length - 1) * lineHeight;
29748 break;
29749
29750 case 'center':
29751 case 'bottom':
29752 textY -= (lines.length - 1) * lineHeight;
29753 break;
29754 }
29755
29756 for (var l = 0; l < lines.length; l++) {
29757 if (lineWidth > 0) {
29758 context.strokeText(lines[l], textX, textY);
29759 }
29760
29761 context.fillText(lines[l], textX, textY);
29762 textY += lineHeight;
29763 }
29764 } else {
29765 if (lineWidth > 0) {
29766 context.strokeText(text, textX, textY);
29767 }
29768
29769 context.fillText(text, textX, textY);
29770 }
29771
29772 if (theta !== 0) {
29773 context.rotate(-theta);
29774 context.translate(-orgTextX, -orgTextY);
29775 }
29776 }
29777};
29778
29779/* global Path2D */
29780var CRp$5 = {};
29781
29782CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29783 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29784 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29785 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29786 var r = this;
29787 var nodeWidth, nodeHeight;
29788 var _p = node._private;
29789 var rs = _p.rscratch;
29790 var pos = node.position();
29791
29792 if (!number(pos.x) || !number(pos.y)) {
29793 return; // can't draw node with undefined position
29794 }
29795
29796 if (shouldDrawOpacity && !node.visible()) {
29797 return;
29798 }
29799
29800 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29801 var usePaths = r.usePaths();
29802 var path;
29803 var pathCacheHit = false;
29804 var padding = node.padding();
29805 nodeWidth = node.width() + 2 * padding;
29806 nodeHeight = node.height() + 2 * padding; //
29807 // setup shift
29808
29809 var bb;
29810
29811 if (shiftToOriginWithBb) {
29812 bb = shiftToOriginWithBb;
29813 context.translate(-bb.x1, -bb.y1);
29814 } //
29815 // load bg image
29816
29817
29818 var bgImgProp = node.pstyle('background-image');
29819 var urls = bgImgProp.value;
29820 var urlDefined = new Array(urls.length);
29821 var image = new Array(urls.length);
29822 var numImages = 0;
29823
29824 for (var i = 0; i < urls.length; i++) {
29825 var url = urls[i];
29826 var defd = urlDefined[i] = url != null && url !== 'none';
29827
29828 if (defd) {
29829 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29830 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29831
29832 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29833 _p.backgroundTimestamp = Date.now();
29834 node.emitAndNotify('background');
29835 });
29836 }
29837 } //
29838 // setup styles
29839
29840
29841 var darkness = node.pstyle('background-blacken').value;
29842 var borderWidth = node.pstyle('border-width').pfValue;
29843 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29844 var borderColor = node.pstyle('border-color').value;
29845 var borderStyle = node.pstyle('border-style').value;
29846 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29847 context.lineJoin = 'miter'; // so borders are square with the node shape
29848
29849 var setupShapeColor = function setupShapeColor() {
29850 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29851 r.eleFillStyle(context, node, bgOpy);
29852 };
29853
29854 var setupBorderColor = function setupBorderColor() {
29855 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29856 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29857 }; //
29858 // setup shape
29859
29860
29861 var styleShape = node.pstyle('shape').strValue;
29862 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29863
29864 if (usePaths) {
29865 context.translate(pos.x, pos.y);
29866 var pathCache = r.nodePathCache = r.nodePathCache || [];
29867 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29868 var cachedPath = pathCache[key];
29869
29870 if (cachedPath != null) {
29871 path = cachedPath;
29872 pathCacheHit = true;
29873 rs.pathCache = path;
29874 } else {
29875 path = new Path2D();
29876 pathCache[key] = rs.pathCache = path;
29877 }
29878 }
29879
29880 var drawShape = function drawShape() {
29881 if (!pathCacheHit) {
29882 var npos = pos;
29883
29884 if (usePaths) {
29885 npos = {
29886 x: 0,
29887 y: 0
29888 };
29889 }
29890
29891 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29892 }
29893
29894 if (usePaths) {
29895 context.fill(path);
29896 } else {
29897 context.fill();
29898 }
29899 };
29900
29901 var drawImages = function drawImages() {
29902 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29903 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29904 var prevBging = _p.backgrounding;
29905 var totalCompleted = 0;
29906
29907 for (var _i = 0; _i < image.length; _i++) {
29908 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29909
29910 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29911 totalCompleted++;
29912 continue;
29913 }
29914
29915 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29916 totalCompleted++;
29917 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29918 }
29919 }
29920
29921 _p.backgrounding = !(totalCompleted === numImages);
29922
29923 if (prevBging !== _p.backgrounding) {
29924 // update style b/c :backgrounding state changed
29925 node.updateStyle(false);
29926 }
29927 };
29928
29929 var drawPie = function drawPie() {
29930 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29931 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29932
29933 if (r.hasPie(node)) {
29934 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29935
29936 if (redrawShape) {
29937 if (!usePaths) {
29938 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29939 }
29940 }
29941 }
29942 };
29943
29944 var darken = function darken() {
29945 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29946 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29947 var c = darkness > 0 ? 0 : 255;
29948
29949 if (darkness !== 0) {
29950 r.colorFillStyle(context, c, c, c, opacity);
29951
29952 if (usePaths) {
29953 context.fill(path);
29954 } else {
29955 context.fill();
29956 }
29957 }
29958 };
29959
29960 var drawBorder = function drawBorder() {
29961 if (borderWidth > 0) {
29962 context.lineWidth = borderWidth;
29963 context.lineCap = 'butt';
29964
29965 if (context.setLineDash) {
29966 // for very outofdate browsers
29967 switch (borderStyle) {
29968 case 'dotted':
29969 context.setLineDash([1, 1]);
29970 break;
29971
29972 case 'dashed':
29973 context.setLineDash([4, 2]);
29974 break;
29975
29976 case 'solid':
29977 case 'double':
29978 context.setLineDash([]);
29979 break;
29980 }
29981 }
29982
29983 if (usePaths) {
29984 context.stroke(path);
29985 } else {
29986 context.stroke();
29987 }
29988
29989 if (borderStyle === 'double') {
29990 context.lineWidth = borderWidth / 3;
29991 var gco = context.globalCompositeOperation;
29992 context.globalCompositeOperation = 'destination-out';
29993
29994 if (usePaths) {
29995 context.stroke(path);
29996 } else {
29997 context.stroke();
29998 }
29999
30000 context.globalCompositeOperation = gco;
30001 } // reset in case we changed the border style
30002
30003
30004 if (context.setLineDash) {
30005 // for very outofdate browsers
30006 context.setLineDash([]);
30007 }
30008 }
30009 };
30010
30011 var drawOverlay = function drawOverlay() {
30012 if (shouldDrawOverlay) {
30013 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30014 }
30015 };
30016
30017 var drawUnderlay = function drawUnderlay() {
30018 if (shouldDrawOverlay) {
30019 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
30020 }
30021 };
30022
30023 var drawText = function drawText() {
30024 r.drawElementText(context, node, null, drawLabel);
30025 };
30026
30027 var ghost = node.pstyle('ghost').value === 'yes';
30028
30029 if (ghost) {
30030 var gx = node.pstyle('ghost-offset-x').pfValue;
30031 var gy = node.pstyle('ghost-offset-y').pfValue;
30032 var ghostOpacity = node.pstyle('ghost-opacity').value;
30033 var effGhostOpacity = ghostOpacity * eleOpacity;
30034 context.translate(gx, gy);
30035 setupShapeColor(ghostOpacity * bgOpacity);
30036 drawShape();
30037 drawImages(effGhostOpacity, true);
30038 setupBorderColor(ghostOpacity * borderOpacity);
30039 drawBorder();
30040 drawPie(darkness !== 0 || borderWidth !== 0);
30041 drawImages(effGhostOpacity, false);
30042 darken(effGhostOpacity);
30043 context.translate(-gx, -gy);
30044 }
30045
30046 if (usePaths) {
30047 context.translate(-pos.x, -pos.y);
30048 }
30049
30050 drawUnderlay();
30051
30052 if (usePaths) {
30053 context.translate(pos.x, pos.y);
30054 }
30055
30056 setupShapeColor();
30057 drawShape();
30058 drawImages(eleOpacity, true);
30059 setupBorderColor();
30060 drawBorder();
30061 drawPie(darkness !== 0 || borderWidth !== 0);
30062 drawImages(eleOpacity, false);
30063 darken();
30064
30065 if (usePaths) {
30066 context.translate(-pos.x, -pos.y);
30067 }
30068
30069 drawText();
30070 drawOverlay(); //
30071 // clean up shift
30072
30073 if (shiftToOriginWithBb) {
30074 context.translate(bb.x1, bb.y1);
30075 }
30076};
30077
30078var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
30079 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
30080 throw new Error('Invalid state');
30081 }
30082
30083 return function (context, node, pos, nodeWidth, nodeHeight) {
30084 var r = this;
30085
30086 if (!node.visible()) {
30087 return;
30088 }
30089
30090 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
30091 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
30092 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
30093 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
30094
30095 if (opacity > 0) {
30096 pos = pos || node.position();
30097
30098 if (nodeWidth == null || nodeHeight == null) {
30099 var _padding = node.padding();
30100
30101 nodeWidth = node.width() + 2 * _padding;
30102 nodeHeight = node.height() + 2 * _padding;
30103 }
30104
30105 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
30106 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2);
30107 context.fill();
30108 }
30109 };
30110};
30111
30112CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
30113CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay'); // does the node have at least one pie piece?
30114
30115CRp$5.hasPie = function (node) {
30116 node = node[0]; // ensure ele ref
30117
30118 return node._private.hasPie;
30119};
30120
30121CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30122 node = node[0]; // ensure ele ref
30123
30124 pos = pos || node.position();
30125 var cyStyle = node.cy().style();
30126 var pieSize = node.pstyle('pie-size');
30127 var x = pos.x;
30128 var y = pos.y;
30129 var nodeW = node.width();
30130 var nodeH = node.height();
30131 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30132
30133 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30134
30135 var usePaths = this.usePaths();
30136
30137 if (usePaths) {
30138 x = 0;
30139 y = 0;
30140 }
30141
30142 if (pieSize.units === '%') {
30143 radius = radius * pieSize.pfValue;
30144 } else if (pieSize.pfValue !== undefined) {
30145 radius = pieSize.pfValue / 2;
30146 }
30147
30148 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30149 // 1..N
30150 var size = node.pstyle('pie-' + i + '-background-size').value;
30151 var color = node.pstyle('pie-' + i + '-background-color').value;
30152 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30153 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30154 // percent can't push beyond 1
30155
30156 if (percent + lastPercent > 1) {
30157 percent = 1 - lastPercent;
30158 }
30159
30160 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30161
30162 var angleDelta = 2 * Math.PI * percent;
30163 var angleEnd = angleStart + angleDelta; // ignore if
30164 // - zero size
30165 // - we're already beyond the full circle
30166 // - adding the current slice would go beyond the full circle
30167
30168 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30169 continue;
30170 }
30171
30172 context.beginPath();
30173 context.moveTo(x, y);
30174 context.arc(x, y, radius, angleStart, angleEnd);
30175 context.closePath();
30176 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30177 context.fill();
30178 lastPercent += percent;
30179 }
30180};
30181
30182var CRp$6 = {};
30183var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30184
30185CRp$6.getPixelRatio = function () {
30186 var context = this.data.contexts[0];
30187
30188 if (this.forcedPixelRatio != null) {
30189 return this.forcedPixelRatio;
30190 }
30191
30192 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30193 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30194};
30195
30196CRp$6.paintCache = function (context) {
30197 var caches = this.paintCaches = this.paintCaches || [];
30198 var needToCreateCache = true;
30199 var cache;
30200
30201 for (var i = 0; i < caches.length; i++) {
30202 cache = caches[i];
30203
30204 if (cache.context === context) {
30205 needToCreateCache = false;
30206 break;
30207 }
30208 }
30209
30210 if (needToCreateCache) {
30211 cache = {
30212 context: context
30213 };
30214 caches.push(cache);
30215 }
30216
30217 return cache;
30218};
30219
30220CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30221 var gradientStyle;
30222 var usePaths = this.usePaths();
30223 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30224 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30225
30226 if (fill === 'radial-gradient') {
30227 if (ele.isEdge()) {
30228 var start = ele.sourceEndpoint(),
30229 end = ele.targetEndpoint(),
30230 mid = ele.midpoint();
30231 var d1 = dist(start, mid);
30232 var d2 = dist(end, mid);
30233 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30234 } else {
30235 var pos = usePaths ? {
30236 x: 0,
30237 y: 0
30238 } : ele.position(),
30239 width = ele.paddedWidth(),
30240 height = ele.paddedHeight();
30241 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30242 }
30243 } else {
30244 if (ele.isEdge()) {
30245 var _start = ele.sourceEndpoint(),
30246 _end = ele.targetEndpoint();
30247
30248 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30249 } else {
30250 var _pos = usePaths ? {
30251 x: 0,
30252 y: 0
30253 } : ele.position(),
30254 _width = ele.paddedWidth(),
30255 _height = ele.paddedHeight(),
30256 halfWidth = _width / 2,
30257 halfHeight = _height / 2;
30258
30259 var direction = ele.pstyle('background-gradient-direction').value;
30260
30261 switch (direction) {
30262 case 'to-bottom':
30263 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30264 break;
30265
30266 case 'to-top':
30267 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30268 break;
30269
30270 case 'to-left':
30271 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30272 break;
30273
30274 case 'to-right':
30275 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30276 break;
30277
30278 case 'to-bottom-right':
30279 case 'to-right-bottom':
30280 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30281 break;
30282
30283 case 'to-top-right':
30284 case 'to-right-top':
30285 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30286 break;
30287
30288 case 'to-bottom-left':
30289 case 'to-left-bottom':
30290 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30291 break;
30292
30293 case 'to-top-left':
30294 case 'to-left-top':
30295 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30296 break;
30297 }
30298 }
30299 }
30300
30301 if (!gradientStyle) return null; // invalid gradient style
30302
30303 var hasPositions = positions.length === colors.length;
30304 var length = colors.length;
30305
30306 for (var i = 0; i < length; i++) {
30307 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30308 }
30309
30310 return gradientStyle;
30311};
30312
30313CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30314 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30315 if (!gradientStyle) return null; // error
30316
30317 context.fillStyle = gradientStyle;
30318};
30319
30320CRp$6.colorFillStyle = function (context, r, g, b, a) {
30321 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30322 // var cache = this.paintCache(context);
30323 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30324 // if( cache.fillStyle !== fillStyle ){
30325 // context.fillStyle = cache.fillStyle = fillStyle;
30326 // }
30327};
30328
30329CRp$6.eleFillStyle = function (context, ele, opacity) {
30330 var backgroundFill = ele.pstyle('background-fill').value;
30331
30332 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30333 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30334 } else {
30335 var backgroundColor = ele.pstyle('background-color').value;
30336 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30337 }
30338};
30339
30340CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30341 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30342 if (!gradientStyle) return null; // error
30343
30344 context.strokeStyle = gradientStyle;
30345};
30346
30347CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30348 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30349 // var cache = this.paintCache(context);
30350 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30351 // if( cache.strokeStyle !== strokeStyle ){
30352 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30353 // }
30354};
30355
30356CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30357 var lineFill = ele.pstyle('line-fill').value;
30358
30359 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30360 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30361 } else {
30362 var lineColor = ele.pstyle('line-color').value;
30363 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30364 }
30365}; // Resize canvas
30366
30367
30368CRp$6.matchCanvasSize = function (container) {
30369 var r = this;
30370 var data = r.data;
30371 var bb = r.findContainerClientCoords();
30372 var width = bb[2];
30373 var height = bb[3];
30374 var pixelRatio = r.getPixelRatio();
30375 var mbPxRatio = r.motionBlurPxRatio;
30376
30377 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30378 pixelRatio = mbPxRatio;
30379 }
30380
30381 var canvasWidth = width * pixelRatio;
30382 var canvasHeight = height * pixelRatio;
30383 var canvas;
30384
30385 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30386 return; // save cycles if same
30387 }
30388
30389 r.fontCaches = null; // resizing resets the style
30390
30391 var canvasContainer = data.canvasContainer;
30392 canvasContainer.style.width = width + 'px';
30393 canvasContainer.style.height = height + 'px';
30394
30395 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30396 canvas = data.canvases[i];
30397 canvas.width = canvasWidth;
30398 canvas.height = canvasHeight;
30399 canvas.style.width = width + 'px';
30400 canvas.style.height = height + 'px';
30401 }
30402
30403 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30404 canvas = data.bufferCanvases[i];
30405 canvas.width = canvasWidth;
30406 canvas.height = canvasHeight;
30407 canvas.style.width = width + 'px';
30408 canvas.style.height = height + 'px';
30409 }
30410
30411 r.textureMult = 1;
30412
30413 if (pixelRatio <= 1) {
30414 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30415 r.textureMult = 2;
30416 canvas.width = canvasWidth * r.textureMult;
30417 canvas.height = canvasHeight * r.textureMult;
30418 }
30419
30420 r.canvasWidth = canvasWidth;
30421 r.canvasHeight = canvasHeight;
30422};
30423
30424CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30425 this.render({
30426 forcedContext: cxt,
30427 forcedZoom: zoom,
30428 forcedPan: pan,
30429 drawAllLayers: true,
30430 forcedPxRatio: pxRatio
30431 });
30432};
30433
30434CRp$6.render = function (options) {
30435 options = options || staticEmptyObject();
30436 var forcedContext = options.forcedContext;
30437 var drawAllLayers = options.drawAllLayers;
30438 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30439 var forcedZoom = options.forcedZoom;
30440 var forcedPan = options.forcedPan;
30441 var r = this;
30442 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30443 var cy = r.cy;
30444 var data = r.data;
30445 var needDraw = data.canvasNeedsRedraw;
30446 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30447 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30448 var mbPxRatio = r.motionBlurPxRatio;
30449 var hasCompoundNodes = cy.hasCompoundNodes();
30450 var inNodeDragGesture = r.hoverData.draggingEles;
30451 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30452 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30453 var motionBlurFadeEffect = motionBlur;
30454
30455 if (!forcedContext) {
30456 if (r.prevPxRatio !== pixelRatio) {
30457 r.invalidateContainerClientCoordsCache();
30458 r.matchCanvasSize(r.container);
30459 r.redrawHint('eles', true);
30460 r.redrawHint('drag', true);
30461 }
30462
30463 r.prevPxRatio = pixelRatio;
30464 }
30465
30466 if (!forcedContext && r.motionBlurTimeout) {
30467 clearTimeout(r.motionBlurTimeout);
30468 }
30469
30470 if (motionBlur) {
30471 if (r.mbFrames == null) {
30472 r.mbFrames = 0;
30473 }
30474
30475 r.mbFrames++;
30476
30477 if (r.mbFrames < 3) {
30478 // need several frames before even high quality motionblur
30479 motionBlurFadeEffect = false;
30480 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30481
30482
30483 if (r.mbFrames > r.minMbLowQualFrames) {
30484 //r.fullQualityMb = false;
30485 r.motionBlurPxRatio = r.mbPxRBlurry;
30486 }
30487 }
30488
30489 if (r.clearingMotionBlur) {
30490 r.motionBlurPxRatio = 1;
30491 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30492 // because a rogue async texture frame would clear needDraw
30493
30494
30495 if (r.textureDrawLastFrame && !textureDraw) {
30496 needDraw[r.NODE] = true;
30497 needDraw[r.SELECT_BOX] = true;
30498 }
30499
30500 var style = cy.style();
30501 var zoom = cy.zoom();
30502 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30503 var pan = cy.pan();
30504 var effectivePan = {
30505 x: pan.x,
30506 y: pan.y
30507 };
30508 var vp = {
30509 zoom: zoom,
30510 pan: {
30511 x: pan.x,
30512 y: pan.y
30513 }
30514 };
30515 var prevVp = r.prevViewport;
30516 var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y; // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
30517
30518 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30519 r.motionBlurPxRatio = 1;
30520 }
30521
30522 if (forcedPan) {
30523 effectivePan = forcedPan;
30524 } // apply pixel ratio
30525
30526
30527 effectiveZoom *= pixelRatio;
30528 effectivePan.x *= pixelRatio;
30529 effectivePan.y *= pixelRatio;
30530 var eles = r.getCachedZSortedEles();
30531
30532 function mbclear(context, x, y, w, h) {
30533 var gco = context.globalCompositeOperation;
30534 context.globalCompositeOperation = 'destination-out';
30535 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30536 context.fillRect(x, y, w, h);
30537 context.globalCompositeOperation = gco;
30538 }
30539
30540 function setContextTransform(context, clear) {
30541 var ePan, eZoom, w, h;
30542
30543 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30544 ePan = {
30545 x: pan.x * mbPxRatio,
30546 y: pan.y * mbPxRatio
30547 };
30548 eZoom = zoom * mbPxRatio;
30549 w = r.canvasWidth * mbPxRatio;
30550 h = r.canvasHeight * mbPxRatio;
30551 } else {
30552 ePan = effectivePan;
30553 eZoom = effectiveZoom;
30554 w = r.canvasWidth;
30555 h = r.canvasHeight;
30556 }
30557
30558 context.setTransform(1, 0, 0, 1, 0, 0);
30559
30560 if (clear === 'motionBlur') {
30561 mbclear(context, 0, 0, w, h);
30562 } else if (!forcedContext && (clear === undefined || clear)) {
30563 context.clearRect(0, 0, w, h);
30564 }
30565
30566 if (!drawAllLayers) {
30567 context.translate(ePan.x, ePan.y);
30568 context.scale(eZoom, eZoom);
30569 }
30570
30571 if (forcedPan) {
30572 context.translate(forcedPan.x, forcedPan.y);
30573 }
30574
30575 if (forcedZoom) {
30576 context.scale(forcedZoom, forcedZoom);
30577 }
30578 }
30579
30580 if (!textureDraw) {
30581 r.textureDrawLastFrame = false;
30582 }
30583
30584 if (textureDraw) {
30585 r.textureDrawLastFrame = true;
30586
30587 if (!r.textureCache) {
30588 r.textureCache = {};
30589 r.textureCache.bb = cy.mutableElements().boundingBox();
30590 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30591 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30592 cxt.setTransform(1, 0, 0, 1, 0, 0);
30593 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30594 r.render({
30595 forcedContext: cxt,
30596 drawOnlyNodeLayer: true,
30597 forcedPxRatio: pixelRatio * r.textureMult
30598 });
30599 var vp = r.textureCache.viewport = {
30600 zoom: cy.zoom(),
30601 pan: cy.pan(),
30602 width: r.canvasWidth,
30603 height: r.canvasHeight
30604 };
30605 vp.mpan = {
30606 x: (0 - vp.pan.x) / vp.zoom,
30607 y: (0 - vp.pan.y) / vp.zoom
30608 };
30609 }
30610
30611 needDraw[r.DRAG] = false;
30612 needDraw[r.NODE] = false;
30613 var context = data.contexts[r.NODE];
30614 var texture = r.textureCache.texture;
30615 var vp = r.textureCache.viewport;
30616 context.setTransform(1, 0, 0, 1, 0, 0);
30617
30618 if (motionBlur) {
30619 mbclear(context, 0, 0, vp.width, vp.height);
30620 } else {
30621 context.clearRect(0, 0, vp.width, vp.height);
30622 }
30623
30624 var outsideBgColor = style.core('outside-texture-bg-color').value;
30625 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30626 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30627 context.fillRect(0, 0, vp.width, vp.height);
30628 var zoom = cy.zoom();
30629 setContextTransform(context, false);
30630 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30631 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30632 } else if (r.textureOnViewport && !forcedContext) {
30633 // clear the cache since we don't need it
30634 r.textureCache = null;
30635 }
30636
30637 var extent = cy.extent();
30638 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30639 var hideEdges = r.hideEdgesOnViewport && vpManip;
30640 var needMbClear = [];
30641 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30642
30643 if (needMbClear[r.NODE]) {
30644 r.clearedForMotionBlur[r.NODE] = true;
30645 }
30646
30647 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30648
30649 if (needMbClear[r.DRAG]) {
30650 r.clearedForMotionBlur[r.DRAG] = true;
30651 }
30652
30653 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30654 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30655 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30656 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30657 setContextTransform(context, clear);
30658
30659 if (hideEdges) {
30660 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30661 } else {
30662 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30663 }
30664
30665 if (r.debug) {
30666 r.drawDebugPoints(context, eles.nondrag);
30667 }
30668
30669 if (!drawAllLayers && !motionBlur) {
30670 needDraw[r.NODE] = false;
30671 }
30672 }
30673
30674 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30675 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30676 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30677 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30678
30679 if (hideEdges) {
30680 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30681 } else {
30682 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30683 }
30684
30685 if (r.debug) {
30686 r.drawDebugPoints(context, eles.drag);
30687 }
30688
30689 if (!drawAllLayers && !motionBlur) {
30690 needDraw[r.DRAG] = false;
30691 }
30692 }
30693
30694 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30695 var context = forcedContext || data.contexts[r.SELECT_BOX];
30696 setContextTransform(context);
30697
30698 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30699 var zoom = r.cy.zoom();
30700 var borderWidth = style.core('selection-box-border-width').value / zoom;
30701 context.lineWidth = borderWidth;
30702 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 + ')';
30703 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30704
30705 if (borderWidth > 0) {
30706 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 + ')';
30707 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30708 }
30709 }
30710
30711 if (data.bgActivePosistion && !r.hoverData.selecting) {
30712 var zoom = r.cy.zoom();
30713 var pos = data.bgActivePosistion;
30714 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 + ')';
30715 context.beginPath();
30716 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30717 context.fill();
30718 }
30719
30720 var timeToRender = r.lastRedrawTime;
30721
30722 if (r.showFps && timeToRender) {
30723 timeToRender = Math.round(timeToRender);
30724 var fps = Math.round(1000 / timeToRender);
30725 context.setTransform(1, 0, 0, 1, 0, 0);
30726 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30727 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30728 context.lineWidth = 1;
30729 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30730 var maxFps = 60;
30731 context.strokeRect(0, 30, 250, 20);
30732 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30733 }
30734
30735 if (!drawAllLayers) {
30736 needDraw[r.SELECT_BOX] = false;
30737 }
30738 } // motionblur: blit rendered blurry frames
30739
30740
30741 if (motionBlur && mbPxRatio !== 1) {
30742 var cxtNode = data.contexts[r.NODE];
30743 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30744 var cxtDrag = data.contexts[r.DRAG];
30745 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30746
30747 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30748 cxt.setTransform(1, 0, 0, 1, 0, 0);
30749
30750 if (needClear || !motionBlurFadeEffect) {
30751 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30752 } else {
30753 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30754 }
30755
30756 var pxr = mbPxRatio;
30757 cxt.drawImage(txt, // img
30758 0, 0, // sx, sy
30759 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30760 0, 0, // x, y
30761 r.canvasWidth, r.canvasHeight // w, h
30762 );
30763 };
30764
30765 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30766 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30767 needDraw[r.NODE] = false;
30768 }
30769
30770 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30771 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30772 needDraw[r.DRAG] = false;
30773 }
30774 }
30775
30776 r.prevViewport = vp;
30777
30778 if (r.clearingMotionBlur) {
30779 r.clearingMotionBlur = false;
30780 r.motionBlurCleared = true;
30781 r.motionBlur = true;
30782 }
30783
30784 if (motionBlur) {
30785 r.motionBlurTimeout = setTimeout(function () {
30786 r.motionBlurTimeout = null;
30787 r.clearedForMotionBlur[r.NODE] = false;
30788 r.clearedForMotionBlur[r.DRAG] = false;
30789 r.motionBlur = false;
30790 r.clearingMotionBlur = !textureDraw;
30791 r.mbFrames = 0;
30792 needDraw[r.NODE] = true;
30793 needDraw[r.DRAG] = true;
30794 r.redraw();
30795 }, motionBlurDelay);
30796 }
30797
30798 if (!forcedContext) {
30799 cy.emit('render');
30800 }
30801};
30802
30803var CRp$7 = {}; // @O Polygon drawing
30804
30805CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30806 var halfW = width / 2;
30807 var halfH = height / 2;
30808
30809 if (context.beginPath) {
30810 context.beginPath();
30811 }
30812
30813 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30814
30815 for (var i = 1; i < points.length / 2; i++) {
30816 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30817 }
30818
30819 context.closePath();
30820};
30821
30822CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30823 var halfW = width / 2;
30824 var halfH = height / 2;
30825 var cornerRadius = getRoundPolygonRadius(width, height);
30826
30827 if (context.beginPath) {
30828 context.beginPath();
30829 }
30830
30831 for (var _i = 0; _i < points.length / 4; _i++) {
30832 var sourceUv = void 0,
30833 destUv = void 0;
30834
30835 if (_i === 0) {
30836 sourceUv = points.length - 2;
30837 } else {
30838 sourceUv = _i * 4 - 2;
30839 }
30840
30841 destUv = _i * 4 + 2;
30842 var px = x + halfW * points[_i * 4];
30843 var py = y + halfH * points[_i * 4 + 1];
30844 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30845 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30846 var cp0x = px - offset * points[sourceUv];
30847 var cp0y = py - offset * points[sourceUv + 1];
30848 var cp1x = px + offset * points[destUv];
30849 var cp1y = py + offset * points[destUv + 1];
30850
30851 if (_i === 0) {
30852 context.moveTo(cp0x, cp0y);
30853 } else {
30854 context.lineTo(cp0x, cp0y);
30855 }
30856
30857 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30858 }
30859
30860 context.closePath();
30861}; // Round rectangle drawing
30862
30863
30864CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30865 var halfWidth = width / 2;
30866 var halfHeight = height / 2;
30867 var cornerRadius = getRoundRectangleRadius(width, height);
30868
30869 if (context.beginPath) {
30870 context.beginPath();
30871 } // Start at top middle
30872
30873
30874 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30875
30876 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30877
30878 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30879
30880 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30881
30882 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30883
30884 context.lineTo(x, y - halfHeight);
30885 context.closePath();
30886};
30887
30888CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30889 var halfWidth = width / 2;
30890 var halfHeight = height / 2;
30891 var cornerRadius = getRoundRectangleRadius(width, height);
30892
30893 if (context.beginPath) {
30894 context.beginPath();
30895 } // Start at top middle
30896
30897
30898 context.moveTo(x, y - halfHeight);
30899 context.lineTo(x + halfWidth, y - halfHeight);
30900 context.lineTo(x + halfWidth, y);
30901 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30902 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30903 context.lineTo(x - halfWidth, y - halfHeight);
30904 context.lineTo(x, y - halfHeight);
30905 context.closePath();
30906};
30907
30908CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30909 var halfWidth = width / 2;
30910 var halfHeight = height / 2;
30911 var cornerLength = getCutRectangleCornerLength();
30912
30913 if (context.beginPath) {
30914 context.beginPath();
30915 }
30916
30917 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30918 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30919 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30920 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30921 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30922 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30923 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30924 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30925 context.closePath();
30926};
30927
30928CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30929 var halfWidth = width / 2;
30930 var halfHeight = height / 2;
30931 var xBegin = x - halfWidth;
30932 var xEnd = x + halfWidth;
30933 var yBegin = y - halfHeight;
30934 var yEnd = y + halfHeight;
30935 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30936 var wOffset = barrelCurveConstants.widthOffset;
30937 var hOffset = barrelCurveConstants.heightOffset;
30938 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30939
30940 if (context.beginPath) {
30941 context.beginPath();
30942 }
30943
30944 context.moveTo(xBegin, yBegin + hOffset);
30945 context.lineTo(xBegin, yEnd - hOffset);
30946 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30947 context.lineTo(xEnd - wOffset, yEnd);
30948 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30949 context.lineTo(xEnd, yBegin + hOffset);
30950 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30951 context.lineTo(xBegin + wOffset, yBegin);
30952 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30953 context.closePath();
30954};
30955
30956var sin0 = Math.sin(0);
30957var cos0 = Math.cos(0);
30958var sin = {};
30959var cos = {};
30960var ellipseStepSize = Math.PI / 40;
30961
30962for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30963 sin[i] = Math.sin(i);
30964 cos[i] = Math.cos(i);
30965}
30966
30967CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30968 if (context.beginPath) {
30969 context.beginPath();
30970 }
30971
30972 if (context.ellipse) {
30973 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30974 } else {
30975 var xPos, yPos;
30976 var rw = width / 2;
30977 var rh = height / 2;
30978
30979 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30980 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30981 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30982
30983 if (i === 0) {
30984 context.moveTo(xPos, yPos);
30985 } else {
30986 context.lineTo(xPos, yPos);
30987 }
30988 }
30989 }
30990
30991 context.closePath();
30992};
30993
30994/* global atob, ArrayBuffer, Uint8Array, Blob */
30995var CRp$8 = {};
30996
30997CRp$8.createBuffer = function (w, h) {
30998 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30999
31000 buffer.width = w;
31001 buffer.height = h;
31002 return [buffer, buffer.getContext('2d')];
31003};
31004
31005CRp$8.bufferCanvasImage = function (options) {
31006 var cy = this.cy;
31007 var eles = cy.mutableElements();
31008 var bb = eles.boundingBox();
31009 var ctrRect = this.findContainerClientCoords();
31010 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31011 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31012 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
31013 var pxRatio = this.getPixelRatio();
31014 var scale = 1;
31015
31016 if (options.scale !== undefined) {
31017 width *= options.scale;
31018 height *= options.scale;
31019 scale = options.scale;
31020 } else if (specdMaxDims) {
31021 var maxScaleW = Infinity;
31022 var maxScaleH = Infinity;
31023
31024 if (number(options.maxWidth)) {
31025 maxScaleW = scale * options.maxWidth / width;
31026 }
31027
31028 if (number(options.maxHeight)) {
31029 maxScaleH = scale * options.maxHeight / height;
31030 }
31031
31032 scale = Math.min(maxScaleW, maxScaleH);
31033 width *= scale;
31034 height *= scale;
31035 }
31036
31037 if (!specdMaxDims) {
31038 width *= pxRatio;
31039 height *= pxRatio;
31040 scale *= pxRatio;
31041 }
31042
31043 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31044
31045 buffCanvas.width = width;
31046 buffCanvas.height = height;
31047 buffCanvas.style.width = width + 'px';
31048 buffCanvas.style.height = height + 'px';
31049 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31050
31051 if (width > 0 && height > 0) {
31052 buffCxt.clearRect(0, 0, width, height);
31053 buffCxt.globalCompositeOperation = 'source-over';
31054 var zsortedEles = this.getCachedZSortedEles();
31055
31056 if (options.full) {
31057 // draw the full bounds of the graph
31058 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31059 buffCxt.scale(scale, scale);
31060 this.drawElements(buffCxt, zsortedEles);
31061 buffCxt.scale(1 / scale, 1 / scale);
31062 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31063 } else {
31064 // draw the current view
31065 var pan = cy.pan();
31066 var translation = {
31067 x: pan.x * scale,
31068 y: pan.y * scale
31069 };
31070 scale *= cy.zoom();
31071 buffCxt.translate(translation.x, translation.y);
31072 buffCxt.scale(scale, scale);
31073 this.drawElements(buffCxt, zsortedEles);
31074 buffCxt.scale(1 / scale, 1 / scale);
31075 buffCxt.translate(-translation.x, -translation.y);
31076 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31077
31078
31079 if (options.bg) {
31080 buffCxt.globalCompositeOperation = 'destination-over';
31081 buffCxt.fillStyle = options.bg;
31082 buffCxt.rect(0, 0, width, height);
31083 buffCxt.fill();
31084 }
31085 }
31086
31087 return buffCanvas;
31088};
31089
31090function b64ToBlob(b64, mimeType) {
31091 var bytes = atob(b64);
31092 var buff = new ArrayBuffer(bytes.length);
31093 var buffUint8 = new Uint8Array(buff);
31094
31095 for (var i = 0; i < bytes.length; i++) {
31096 buffUint8[i] = bytes.charCodeAt(i);
31097 }
31098
31099 return new Blob([buff], {
31100 type: mimeType
31101 });
31102}
31103
31104function b64UriToB64(b64uri) {
31105 var i = b64uri.indexOf(',');
31106 return b64uri.substr(i + 1);
31107}
31108
31109function output(options, canvas, mimeType) {
31110 var getB64Uri = function getB64Uri() {
31111 return canvas.toDataURL(mimeType, options.quality);
31112 };
31113
31114 switch (options.output) {
31115 case 'blob-promise':
31116 return new Promise$1(function (resolve, reject) {
31117 try {
31118 canvas.toBlob(function (blob) {
31119 if (blob != null) {
31120 resolve(blob);
31121 } else {
31122 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31123 }
31124 }, mimeType, options.quality);
31125 } catch (err) {
31126 reject(err);
31127 }
31128 });
31129
31130 case 'blob':
31131 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31132
31133 case 'base64':
31134 return b64UriToB64(getB64Uri());
31135
31136 case 'base64uri':
31137 default:
31138 return getB64Uri();
31139 }
31140}
31141
31142CRp$8.png = function (options) {
31143 return output(options, this.bufferCanvasImage(options), 'image/png');
31144};
31145
31146CRp$8.jpg = function (options) {
31147 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31148};
31149
31150var CRp$9 = {};
31151
31152CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31153 switch (name) {
31154 case 'ellipse':
31155 return this.drawEllipsePath(context, centerX, centerY, width, height);
31156
31157 case 'polygon':
31158 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31159
31160 case 'round-polygon':
31161 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31162
31163 case 'roundrectangle':
31164 case 'round-rectangle':
31165 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31166
31167 case 'cutrectangle':
31168 case 'cut-rectangle':
31169 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31170
31171 case 'bottomroundrectangle':
31172 case 'bottom-round-rectangle':
31173 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31174
31175 case 'barrel':
31176 return this.drawBarrelPath(context, centerX, centerY, width, height);
31177 }
31178};
31179
31180var CR = CanvasRenderer;
31181var CRp$a = CanvasRenderer.prototype;
31182CRp$a.CANVAS_LAYERS = 3; //
31183
31184CRp$a.SELECT_BOX = 0;
31185CRp$a.DRAG = 1;
31186CRp$a.NODE = 2;
31187CRp$a.BUFFER_COUNT = 3; //
31188
31189CRp$a.TEXTURE_BUFFER = 0;
31190CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
31191CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
31192
31193function CanvasRenderer(options) {
31194 var r = this;
31195 r.data = {
31196 canvases: new Array(CRp$a.CANVAS_LAYERS),
31197 contexts: new Array(CRp$a.CANVAS_LAYERS),
31198 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
31199 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
31200 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
31201 };
31202 var tapHlOffAttr = '-webkit-tap-highlight-color';
31203 var tapHlOffStyle = 'rgba(0,0,0,0)';
31204 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31205
31206 var containerStyle = r.data.canvasContainer.style;
31207 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31208 containerStyle.position = 'relative';
31209 containerStyle.zIndex = '0';
31210 containerStyle.overflow = 'hidden';
31211 var container = options.cy.container();
31212 container.appendChild(r.data.canvasContainer);
31213 container.style[tapHlOffAttr] = tapHlOffStyle;
31214 var styleMap = {
31215 '-webkit-user-select': 'none',
31216 '-moz-user-select': '-moz-none',
31217 'user-select': 'none',
31218 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31219 'outline-style': 'none'
31220 };
31221
31222 if (ms()) {
31223 styleMap['-ms-touch-action'] = 'none';
31224 styleMap['touch-action'] = 'none';
31225 }
31226
31227 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
31228 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31229
31230 r.data.contexts[i] = canvas.getContext('2d');
31231 Object.keys(styleMap).forEach(function (k) {
31232 canvas.style[k] = styleMap[k];
31233 });
31234 canvas.style.position = 'absolute';
31235 canvas.setAttribute('data-id', 'layer' + i);
31236 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
31237 r.data.canvasContainer.appendChild(canvas);
31238 r.data.canvasNeedsRedraw[i] = false;
31239 }
31240
31241 r.data.topCanvas = r.data.canvases[0];
31242 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
31243 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
31244 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
31245
31246 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
31247 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31248
31249 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31250 r.data.bufferCanvases[i].style.position = 'absolute';
31251 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31252 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31253 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31254 }
31255
31256 r.pathsEnabled = true;
31257 var emptyBb = makeBoundingBox();
31258
31259 var getBoxCenter = function getBoxCenter(bb) {
31260 return {
31261 x: (bb.x1 + bb.x2) / 2,
31262 y: (bb.y1 + bb.y2) / 2
31263 };
31264 };
31265
31266 var getCenterOffset = function getCenterOffset(bb) {
31267 return {
31268 x: -bb.w / 2,
31269 y: -bb.h / 2
31270 };
31271 };
31272
31273 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31274 var _p = ele[0]._private;
31275 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31276 return !same;
31277 };
31278
31279 var getStyleKey = function getStyleKey(ele) {
31280 return ele[0]._private.nodeKey;
31281 };
31282
31283 var getLabelKey = function getLabelKey(ele) {
31284 return ele[0]._private.labelStyleKey;
31285 };
31286
31287 var getSourceLabelKey = function getSourceLabelKey(ele) {
31288 return ele[0]._private.sourceLabelStyleKey;
31289 };
31290
31291 var getTargetLabelKey = function getTargetLabelKey(ele) {
31292 return ele[0]._private.targetLabelStyleKey;
31293 };
31294
31295 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31296 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31297 };
31298
31299 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31300 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31301 };
31302
31303 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31304 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31305 };
31306
31307 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31308 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31309 };
31310
31311 var getElementBox = function getElementBox(ele) {
31312 ele.boundingBox();
31313 return ele[0]._private.bodyBounds;
31314 };
31315
31316 var getLabelBox = function getLabelBox(ele) {
31317 ele.boundingBox();
31318 return ele[0]._private.labelBounds.main || emptyBb;
31319 };
31320
31321 var getSourceLabelBox = function getSourceLabelBox(ele) {
31322 ele.boundingBox();
31323 return ele[0]._private.labelBounds.source || emptyBb;
31324 };
31325
31326 var getTargetLabelBox = function getTargetLabelBox(ele) {
31327 ele.boundingBox();
31328 return ele[0]._private.labelBounds.target || emptyBb;
31329 };
31330
31331 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31332 return scaledLabelShown;
31333 };
31334
31335 var getElementRotationPoint = function getElementRotationPoint(ele) {
31336 return getBoxCenter(getElementBox(ele));
31337 };
31338
31339 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31340 var pre = prefix ? prefix + '-' : '';
31341 return {
31342 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31343 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31344 };
31345 };
31346
31347 var getRsPt = function getRsPt(ele, x, y) {
31348 var rs = ele[0]._private.rscratch;
31349 return {
31350 x: rs[x],
31351 y: rs[y]
31352 };
31353 };
31354
31355 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31356 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31357 };
31358
31359 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31360 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31361 };
31362
31363 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31364 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31365 };
31366
31367 var getElementRotationOffset = function getElementRotationOffset(ele) {
31368 return getCenterOffset(getElementBox(ele));
31369 };
31370
31371 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31372 return getCenterOffset(getSourceLabelBox(ele));
31373 };
31374
31375 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31376 return getCenterOffset(getTargetLabelBox(ele));
31377 };
31378
31379 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31380 var bb = getLabelBox(ele);
31381 var p = getCenterOffset(getLabelBox(ele));
31382
31383 if (ele.isNode()) {
31384 switch (ele.pstyle('text-halign').value) {
31385 case 'left':
31386 p.x = -bb.w;
31387 break;
31388
31389 case 'right':
31390 p.x = 0;
31391 break;
31392 }
31393
31394 switch (ele.pstyle('text-valign').value) {
31395 case 'top':
31396 p.y = -bb.h;
31397 break;
31398
31399 case 'bottom':
31400 p.y = 0;
31401 break;
31402 }
31403 }
31404
31405 return p;
31406 };
31407
31408 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31409 getKey: getStyleKey,
31410 doesEleInvalidateKey: backgroundTimestampHasChanged,
31411 drawElement: drawElement,
31412 getBoundingBox: getElementBox,
31413 getRotationPoint: getElementRotationPoint,
31414 getRotationOffset: getElementRotationOffset,
31415 allowEdgeTxrCaching: false,
31416 allowParentTxrCaching: false
31417 });
31418 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31419 getKey: getLabelKey,
31420 drawElement: drawLabel,
31421 getBoundingBox: getLabelBox,
31422 getRotationPoint: getLabelRotationPoint,
31423 getRotationOffset: getLabelRotationOffset,
31424 isVisible: isLabelVisibleAtScale
31425 });
31426 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31427 getKey: getSourceLabelKey,
31428 drawElement: drawSourceLabel,
31429 getBoundingBox: getSourceLabelBox,
31430 getRotationPoint: getSourceLabelRotationPoint,
31431 getRotationOffset: getSourceLabelRotationOffset,
31432 isVisible: isLabelVisibleAtScale
31433 });
31434 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31435 getKey: getTargetLabelKey,
31436 drawElement: drawTargetLabel,
31437 getBoundingBox: getTargetLabelBox,
31438 getRotationPoint: getTargetLabelRotationPoint,
31439 getRotationOffset: getTargetLabelRotationOffset,
31440 isVisible: isLabelVisibleAtScale
31441 });
31442 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31443 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31444 // each cache should check for sub-key diff to see that the update affects that cache particularly
31445 eleTxrCache.invalidateElements(eles);
31446 lblTxrCache.invalidateElements(eles);
31447 slbTxrCache.invalidateElements(eles);
31448 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31449
31450 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31451
31452 for (var _i = 0; _i < eles.length; _i++) {
31453 var _p = eles[_i]._private;
31454 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31455 }
31456 });
31457
31458 var refineInLayers = function refineInLayers(reqs) {
31459 for (var i = 0; i < reqs.length; i++) {
31460 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31461 }
31462 };
31463
31464 eleTxrCache.onDequeue(refineInLayers);
31465 lblTxrCache.onDequeue(refineInLayers);
31466 slbTxrCache.onDequeue(refineInLayers);
31467 tlbTxrCache.onDequeue(refineInLayers);
31468}
31469
31470CRp$a.redrawHint = function (group, bool) {
31471 var r = this;
31472
31473 switch (group) {
31474 case 'eles':
31475 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31476 break;
31477
31478 case 'drag':
31479 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31480 break;
31481
31482 case 'select':
31483 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31484 break;
31485 }
31486}; // whether to use Path2D caching for drawing
31487
31488
31489var pathsImpld = typeof Path2D !== 'undefined';
31490
31491CRp$a.path2dEnabled = function (on) {
31492 if (on === undefined) {
31493 return this.pathsEnabled;
31494 }
31495
31496 this.pathsEnabled = on ? true : false;
31497};
31498
31499CRp$a.usePaths = function () {
31500 return pathsImpld && this.pathsEnabled;
31501};
31502
31503CRp$a.setImgSmoothing = function (context, bool) {
31504 if (context.imageSmoothingEnabled != null) {
31505 context.imageSmoothingEnabled = bool;
31506 } else {
31507 context.webkitImageSmoothingEnabled = bool;
31508 context.mozImageSmoothingEnabled = bool;
31509 context.msImageSmoothingEnabled = bool;
31510 }
31511};
31512
31513CRp$a.getImgSmoothing = function (context) {
31514 if (context.imageSmoothingEnabled != null) {
31515 return context.imageSmoothingEnabled;
31516 } else {
31517 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31518 }
31519};
31520
31521CRp$a.makeOffscreenCanvas = function (width, height) {
31522 var canvas;
31523
31524 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31525 canvas = new OffscreenCanvas(width, height);
31526 } else {
31527 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31528
31529 canvas.width = width;
31530 canvas.height = height;
31531 }
31532
31533 return canvas;
31534};
31535
31536[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31537 extend(CRp$a, props);
31538});
31539
31540var renderer = [{
31541 name: 'null',
31542 impl: NullRenderer
31543}, {
31544 name: 'base',
31545 impl: BR
31546}, {
31547 name: 'canvas',
31548 impl: CR
31549}];
31550
31551var incExts = [{
31552 type: 'layout',
31553 extensions: layout
31554}, {
31555 type: 'renderer',
31556 extensions: renderer
31557}];
31558
31559var extensions = {}; // registered modules for extensions, indexed by name
31560
31561var modules = {};
31562
31563function setExtension(type, name, registrant) {
31564 var ext = registrant;
31565
31566 var overrideErr = function overrideErr(field) {
31567 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31568 };
31569
31570 if (type === 'core') {
31571 if (Core.prototype[name]) {
31572 return overrideErr(name);
31573 } else {
31574 Core.prototype[name] = registrant;
31575 }
31576 } else if (type === 'collection') {
31577 if (Collection.prototype[name]) {
31578 return overrideErr(name);
31579 } else {
31580 Collection.prototype[name] = registrant;
31581 }
31582 } else if (type === 'layout') {
31583 // fill in missing layout functions in the prototype
31584 var Layout = function Layout(options) {
31585 this.options = options;
31586 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31587
31588 if (!plainObject(this._private)) {
31589 this._private = {};
31590 }
31591
31592 this._private.cy = options.cy;
31593 this._private.listeners = [];
31594 this.createEmitter();
31595 };
31596
31597 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31598 var optLayoutFns = [];
31599
31600 for (var i = 0; i < optLayoutFns.length; i++) {
31601 var fnName = optLayoutFns[i];
31602
31603 layoutProto[fnName] = layoutProto[fnName] || function () {
31604 return this;
31605 };
31606 } // either .start() or .run() is defined, so autogen the other
31607
31608
31609 if (layoutProto.start && !layoutProto.run) {
31610 layoutProto.run = function () {
31611 this.start();
31612 return this;
31613 };
31614 } else if (!layoutProto.start && layoutProto.run) {
31615 layoutProto.start = function () {
31616 this.run();
31617 return this;
31618 };
31619 }
31620
31621 var regStop = registrant.prototype.stop;
31622
31623 layoutProto.stop = function () {
31624 var opts = this.options;
31625
31626 if (opts && opts.animate) {
31627 var anis = this.animations;
31628
31629 if (anis) {
31630 for (var _i = 0; _i < anis.length; _i++) {
31631 anis[_i].stop();
31632 }
31633 }
31634 }
31635
31636 if (regStop) {
31637 regStop.call(this);
31638 } else {
31639 this.emit('layoutstop');
31640 }
31641
31642 return this;
31643 };
31644
31645 if (!layoutProto.destroy) {
31646 layoutProto.destroy = function () {
31647 return this;
31648 };
31649 }
31650
31651 layoutProto.cy = function () {
31652 return this._private.cy;
31653 };
31654
31655 var getCy = function getCy(layout) {
31656 return layout._private.cy;
31657 };
31658
31659 var emitterOpts = {
31660 addEventFields: function addEventFields(layout, evt) {
31661 evt.layout = layout;
31662 evt.cy = getCy(layout);
31663 evt.target = layout;
31664 },
31665 bubble: function bubble() {
31666 return true;
31667 },
31668 parent: function parent(layout) {
31669 return getCy(layout);
31670 }
31671 };
31672 extend(layoutProto, {
31673 createEmitter: function createEmitter() {
31674 this._private.emitter = new Emitter(emitterOpts, this);
31675 return this;
31676 },
31677 emitter: function emitter() {
31678 return this._private.emitter;
31679 },
31680 on: function on(evt, cb) {
31681 this.emitter().on(evt, cb);
31682 return this;
31683 },
31684 one: function one(evt, cb) {
31685 this.emitter().one(evt, cb);
31686 return this;
31687 },
31688 once: function once(evt, cb) {
31689 this.emitter().one(evt, cb);
31690 return this;
31691 },
31692 removeListener: function removeListener(evt, cb) {
31693 this.emitter().removeListener(evt, cb);
31694 return this;
31695 },
31696 removeAllListeners: function removeAllListeners() {
31697 this.emitter().removeAllListeners();
31698 return this;
31699 },
31700 emit: function emit(evt, params) {
31701 this.emitter().emit(evt, params);
31702 return this;
31703 }
31704 });
31705 define$3.eventAliasesOn(layoutProto);
31706 ext = Layout; // replace with our wrapped layout
31707 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31708 // user registered renderers inherit from base
31709 var BaseRenderer = getExtension('renderer', 'base');
31710 var bProto = BaseRenderer.prototype;
31711 var RegistrantRenderer = registrant;
31712 var rProto = registrant.prototype;
31713
31714 var Renderer = function Renderer() {
31715 BaseRenderer.apply(this, arguments);
31716 RegistrantRenderer.apply(this, arguments);
31717 };
31718
31719 var proto = Renderer.prototype;
31720
31721 for (var pName in bProto) {
31722 var pVal = bProto[pName];
31723 var existsInR = rProto[pName] != null;
31724
31725 if (existsInR) {
31726 return overrideErr(pName);
31727 }
31728
31729 proto[pName] = pVal; // take impl from base
31730 }
31731
31732 for (var _pName in rProto) {
31733 proto[_pName] = rProto[_pName]; // take impl from registrant
31734 }
31735
31736 bProto.clientFunctions.forEach(function (name) {
31737 proto[name] = proto[name] || function () {
31738 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31739 };
31740 });
31741 ext = Renderer;
31742 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31743 // to avoid potential prototype pollution
31744 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31745 }
31746
31747 return setMap({
31748 map: extensions,
31749 keys: [type, name],
31750 value: ext
31751 });
31752}
31753
31754function getExtension(type, name) {
31755 return getMap({
31756 map: extensions,
31757 keys: [type, name]
31758 });
31759}
31760
31761function setModule(type, name, moduleType, moduleName, registrant) {
31762 return setMap({
31763 map: modules,
31764 keys: [type, name, moduleType, moduleName],
31765 value: registrant
31766 });
31767}
31768
31769function getModule(type, name, moduleType, moduleName) {
31770 return getMap({
31771 map: modules,
31772 keys: [type, name, moduleType, moduleName]
31773 });
31774}
31775
31776var extension = function extension() {
31777 // e.g. extension('renderer', 'svg')
31778 if (arguments.length === 2) {
31779 return getExtension.apply(null, arguments);
31780 } // e.g. extension('renderer', 'svg', { ... })
31781 else if (arguments.length === 3) {
31782 return setExtension.apply(null, arguments);
31783 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31784 else if (arguments.length === 4) {
31785 return getModule.apply(null, arguments);
31786 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31787 else if (arguments.length === 5) {
31788 return setModule.apply(null, arguments);
31789 } else {
31790 error('Invalid extension access syntax');
31791 }
31792}; // allows a core instance to access extensions internally
31793
31794
31795Core.prototype.extension = extension; // included extensions
31796
31797incExts.forEach(function (group) {
31798 group.extensions.forEach(function (ext) {
31799 setExtension(group.type, ext.name, ext.impl);
31800 });
31801});
31802
31803// (useful for init)
31804
31805var Stylesheet = function Stylesheet() {
31806 if (!(this instanceof Stylesheet)) {
31807 return new Stylesheet();
31808 }
31809
31810 this.length = 0;
31811};
31812
31813var sheetfn = Stylesheet.prototype;
31814
31815sheetfn.instanceString = function () {
31816 return 'stylesheet';
31817}; // just store the selector to be parsed later
31818
31819
31820sheetfn.selector = function (selector) {
31821 var i = this.length++;
31822 this[i] = {
31823 selector: selector,
31824 properties: []
31825 };
31826 return this; // chaining
31827}; // just store the property to be parsed later
31828
31829
31830sheetfn.css = function (name, value) {
31831 var i = this.length - 1;
31832
31833 if (string(name)) {
31834 this[i].properties.push({
31835 name: name,
31836 value: value
31837 });
31838 } else if (plainObject(name)) {
31839 var map = name;
31840 var propNames = Object.keys(map);
31841
31842 for (var j = 0; j < propNames.length; j++) {
31843 var key = propNames[j];
31844 var mapVal = map[key];
31845
31846 if (mapVal == null) {
31847 continue;
31848 }
31849
31850 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31851
31852 if (prop == null) {
31853 continue;
31854 }
31855
31856 var _name = prop.name;
31857 var _value = mapVal;
31858 this[i].properties.push({
31859 name: _name,
31860 value: _value
31861 });
31862 }
31863 }
31864
31865 return this; // chaining
31866};
31867
31868sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31869
31870sheetfn.generateStyle = function (cy) {
31871 var style = new Style(cy);
31872 return this.appendToStyle(style);
31873}; // append a dummy stylesheet object on a real style object
31874
31875
31876sheetfn.appendToStyle = function (style) {
31877 for (var i = 0; i < this.length; i++) {
31878 var context = this[i];
31879 var selector = context.selector;
31880 var props = context.properties;
31881 style.selector(selector); // apply selector
31882
31883 for (var j = 0; j < props.length; j++) {
31884 var prop = props[j];
31885 style.css(prop.name, prop.value); // apply property
31886 }
31887 }
31888
31889 return style;
31890};
31891
31892var version = "3.21.2";
31893
31894var cytoscape = function cytoscape(options) {
31895 // if no options specified, use default
31896 if (options === undefined) {
31897 options = {};
31898 } // create instance
31899
31900
31901 if (plainObject(options)) {
31902 return new Core(options);
31903 } // allow for registration of extensions
31904 else if (string(options)) {
31905 return extension.apply(extension, arguments);
31906 }
31907}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31908
31909
31910cytoscape.use = function (ext) {
31911 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31912
31913 args.unshift(cytoscape); // cytoscape is first arg to ext
31914
31915 ext.apply(null, args);
31916 return this;
31917};
31918
31919cytoscape.warnings = function (bool) {
31920 return warnings(bool);
31921}; // replaced by build system
31922
31923
31924cytoscape.version = version; // expose public apis (mostly for extensions)
31925
31926cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31927
31928module.exports = cytoscape;