UNPKG

930 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2020, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23(function (global, factory) {
24 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
25 typeof define === 'function' && define.amd ? define(factory) :
26 (global = global || self, global.cytoscape = factory());
27}(this, (function () { 'use strict';
28
29 function _typeof(obj) {
30 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
31 _typeof = function (obj) {
32 return typeof obj;
33 };
34 } else {
35 _typeof = function (obj) {
36 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
37 };
38 }
39
40 return _typeof(obj);
41 }
42
43 function _classCallCheck(instance, Constructor) {
44 if (!(instance instanceof Constructor)) {
45 throw new TypeError("Cannot call a class as a function");
46 }
47 }
48
49 function _defineProperties(target, props) {
50 for (var i = 0; i < props.length; i++) {
51 var descriptor = props[i];
52 descriptor.enumerable = descriptor.enumerable || false;
53 descriptor.configurable = true;
54 if ("value" in descriptor) descriptor.writable = true;
55 Object.defineProperty(target, descriptor.key, descriptor);
56 }
57 }
58
59 function _createClass(Constructor, protoProps, staticProps) {
60 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
61 if (staticProps) _defineProperties(Constructor, staticProps);
62 return Constructor;
63 }
64
65 function _defineProperty(obj, key, value) {
66 if (key in obj) {
67 Object.defineProperty(obj, key, {
68 value: value,
69 enumerable: true,
70 configurable: true,
71 writable: true
72 });
73 } else {
74 obj[key] = value;
75 }
76
77 return obj;
78 }
79
80 function _slicedToArray(arr, i) {
81 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
82 }
83
84 function _arrayWithHoles(arr) {
85 if (Array.isArray(arr)) return arr;
86 }
87
88 function _iterableToArrayLimit(arr, i) {
89 var _arr = [];
90 var _n = true;
91 var _d = false;
92 var _e = undefined;
93
94 try {
95 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
96 _arr.push(_s.value);
97
98 if (i && _arr.length === i) break;
99 }
100 } catch (err) {
101 _d = true;
102 _e = err;
103 } finally {
104 try {
105 if (!_n && _i["return"] != null) _i["return"]();
106 } finally {
107 if (_d) throw _e;
108 }
109 }
110
111 return _arr;
112 }
113
114 function _nonIterableRest() {
115 throw new TypeError("Invalid attempt to destructure non-iterable instance");
116 }
117
118 var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
119
120 var navigator = window$1 ? window$1.navigator : null;
121 var document$1 = window$1 ? window$1.document : null;
122
123 var typeofstr = _typeof('');
124
125 var typeofobj = _typeof({});
126
127 var typeoffn = _typeof(function () {});
128
129 var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
130
131 var instanceStr = function instanceStr(obj) {
132 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
133 };
134
135 var string = function string(obj) {
136 return obj != null && _typeof(obj) == typeofstr;
137 };
138 var fn = function fn(obj) {
139 return obj != null && _typeof(obj) === typeoffn;
140 };
141 var array = function array(obj) {
142 return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
143 };
144 var plainObject = function plainObject(obj) {
145 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
146 };
147 var object = function object(obj) {
148 return obj != null && _typeof(obj) === typeofobj;
149 };
150 var number = function number(obj) {
151 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
152 };
153 var integer = function integer(obj) {
154 return number(obj) && Math.floor(obj) === obj;
155 };
156 var htmlElement = function htmlElement(obj) {
157 if ('undefined' === typeofhtmlele) {
158 return undefined;
159 } else {
160 return null != obj && obj instanceof HTMLElement;
161 }
162 };
163 var elementOrCollection = function elementOrCollection(obj) {
164 return element(obj) || collection(obj);
165 };
166 var element = function element(obj) {
167 return instanceStr(obj) === 'collection' && obj._private.single;
168 };
169 var collection = function collection(obj) {
170 return instanceStr(obj) === 'collection' && !obj._private.single;
171 };
172 var core = function core(obj) {
173 return instanceStr(obj) === 'core';
174 };
175 var stylesheet = function stylesheet(obj) {
176 return instanceStr(obj) === 'stylesheet';
177 };
178 var event = function event(obj) {
179 return instanceStr(obj) === 'event';
180 };
181 var emptyString = function emptyString(obj) {
182 if (obj === undefined || obj === null) {
183 // null is empty
184 return true;
185 } else if (obj === '' || obj.match(/^\s+$/)) {
186 return true; // empty string is empty
187 }
188
189 return false; // otherwise, we don't know what we've got
190 };
191 var domElement = function domElement(obj) {
192 if (typeof HTMLElement === 'undefined') {
193 return false; // we're not in a browser so it doesn't matter
194 } else {
195 return obj instanceof HTMLElement;
196 }
197 };
198 var boundingBox = function boundingBox(obj) {
199 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
200 };
201 var promise = function promise(obj) {
202 return object(obj) && fn(obj.then);
203 };
204 var ms = function ms() {
205 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
206 }; // probably a better way to detect this...
207
208 var memoize = function memoize(fn, keyFn) {
209 if (!keyFn) {
210 keyFn = function keyFn() {
211 if (arguments.length === 1) {
212 return arguments[0];
213 } else if (arguments.length === 0) {
214 return 'undefined';
215 }
216
217 var args = [];
218
219 for (var i = 0; i < arguments.length; i++) {
220 args.push(arguments[i]);
221 }
222
223 return args.join('$');
224 };
225 }
226
227 var memoizedFn = function memoizedFn() {
228 var self = this;
229 var args = arguments;
230 var ret;
231 var k = keyFn.apply(self, args);
232 var cache = memoizedFn.cache;
233
234 if (!(ret = cache[k])) {
235 ret = cache[k] = fn.apply(self, args);
236 }
237
238 return ret;
239 };
240
241 memoizedFn.cache = {};
242 return memoizedFn;
243 };
244
245 var camel2dash = memoize(function (str) {
246 return str.replace(/([A-Z])/g, function (v) {
247 return '-' + v.toLowerCase();
248 });
249 });
250 var dash2camel = memoize(function (str) {
251 return str.replace(/(-\w)/g, function (v) {
252 return v[1].toUpperCase();
253 });
254 });
255 var prependCamel = memoize(function (prefix, str) {
256 return prefix + str[0].toUpperCase() + str.substring(1);
257 }, function (prefix, str) {
258 return prefix + '$' + str;
259 });
260 var capitalize = function capitalize(str) {
261 if (emptyString(str)) {
262 return str;
263 }
264
265 return str.charAt(0).toUpperCase() + str.substring(1);
266 };
267
268 var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
269 var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
270 var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
271 var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
272 var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
273 var hex3 = '\\#[0-9a-fA-F]{3}';
274 var hex6 = '\\#[0-9a-fA-F]{6}';
275
276 var ascending = function ascending(a, b) {
277 if (a < b) {
278 return -1;
279 } else if (a > b) {
280 return 1;
281 } else {
282 return 0;
283 }
284 };
285 var descending = function descending(a, b) {
286 return -1 * ascending(a, b);
287 };
288
289 var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
290 var args = arguments;
291
292 for (var i = 1; i < args.length; i++) {
293 var obj = args[i];
294
295 if (obj == null) {
296 continue;
297 }
298
299 var keys = Object.keys(obj);
300
301 for (var j = 0; j < keys.length; j++) {
302 var k = keys[j];
303 tgt[k] = obj[k];
304 }
305 }
306
307 return tgt;
308 };
309
310 var hex2tuple = function hex2tuple(hex) {
311 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
312 return;
313 }
314
315 var shortHex = hex.length === 4;
316 var r, g, b;
317 var base = 16;
318
319 if (shortHex) {
320 r = parseInt(hex[1] + hex[1], base);
321 g = parseInt(hex[2] + hex[2], base);
322 b = parseInt(hex[3] + hex[3], base);
323 } else {
324 r = parseInt(hex[1] + hex[2], base);
325 g = parseInt(hex[3] + hex[4], base);
326 b = parseInt(hex[5] + hex[6], base);
327 }
328
329 return [r, g, b];
330 }; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
331
332 var hsl2tuple = function hsl2tuple(hsl) {
333 var ret;
334 var h, s, l, a, r, g, b;
335
336 function hue2rgb(p, q, t) {
337 if (t < 0) t += 1;
338 if (t > 1) t -= 1;
339 if (t < 1 / 6) return p + (q - p) * 6 * t;
340 if (t < 1 / 2) return q;
341 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
342 return p;
343 }
344
345 var m = new RegExp('^' + hsla + '$').exec(hsl);
346
347 if (m) {
348 // get hue
349 h = parseInt(m[1]);
350
351 if (h < 0) {
352 h = (360 - -1 * h % 360) % 360;
353 } else if (h > 360) {
354 h = h % 360;
355 }
356
357 h /= 360; // normalise on [0, 1]
358
359 s = parseFloat(m[2]);
360
361 if (s < 0 || s > 100) {
362 return;
363 } // saturation is [0, 100]
364
365
366 s = s / 100; // normalise on [0, 1]
367
368 l = parseFloat(m[3]);
369
370 if (l < 0 || l > 100) {
371 return;
372 } // lightness is [0, 100]
373
374
375 l = l / 100; // normalise on [0, 1]
376
377 a = m[4];
378
379 if (a !== undefined) {
380 a = parseFloat(a);
381
382 if (a < 0 || a > 1) {
383 return;
384 } // alpha is [0, 1]
385
386 } // now, convert to rgb
387 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
388
389
390 if (s === 0) {
391 r = g = b = Math.round(l * 255); // achromatic
392 } else {
393 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
394 var p = 2 * l - q;
395 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
396 g = Math.round(255 * hue2rgb(p, q, h));
397 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
398 }
399
400 ret = [r, g, b, a];
401 }
402
403 return ret;
404 }; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
405
406 var rgb2tuple = function rgb2tuple(rgb) {
407 var ret;
408 var m = new RegExp('^' + rgba + '$').exec(rgb);
409
410 if (m) {
411 ret = [];
412 var isPct = [];
413
414 for (var i = 1; i <= 3; i++) {
415 var channel = m[i];
416
417 if (channel[channel.length - 1] === '%') {
418 isPct[i] = true;
419 }
420
421 channel = parseFloat(channel);
422
423 if (isPct[i]) {
424 channel = channel / 100 * 255; // normalise to [0, 255]
425 }
426
427 if (channel < 0 || channel > 255) {
428 return;
429 } // invalid channel value
430
431
432 ret.push(Math.floor(channel));
433 }
434
435 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
436 var allArePct = isPct[1] && isPct[2] && isPct[3];
437
438 if (atLeastOneIsPct && !allArePct) {
439 return;
440 } // must all be percent values if one is
441
442
443 var alpha = m[4];
444
445 if (alpha !== undefined) {
446 alpha = parseFloat(alpha);
447
448 if (alpha < 0 || alpha > 1) {
449 return;
450 } // invalid alpha value
451
452
453 ret.push(alpha);
454 }
455 }
456
457 return ret;
458 };
459 var colorname2tuple = function colorname2tuple(color) {
460 return colors[color.toLowerCase()];
461 };
462 var color2tuple = function color2tuple(color) {
463 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
464 };
465 var colors = {
466 // special colour names
467 transparent: [0, 0, 0, 0],
468 // NB alpha === 0
469 // regular colours
470 aliceblue: [240, 248, 255],
471 antiquewhite: [250, 235, 215],
472 aqua: [0, 255, 255],
473 aquamarine: [127, 255, 212],
474 azure: [240, 255, 255],
475 beige: [245, 245, 220],
476 bisque: [255, 228, 196],
477 black: [0, 0, 0],
478 blanchedalmond: [255, 235, 205],
479 blue: [0, 0, 255],
480 blueviolet: [138, 43, 226],
481 brown: [165, 42, 42],
482 burlywood: [222, 184, 135],
483 cadetblue: [95, 158, 160],
484 chartreuse: [127, 255, 0],
485 chocolate: [210, 105, 30],
486 coral: [255, 127, 80],
487 cornflowerblue: [100, 149, 237],
488 cornsilk: [255, 248, 220],
489 crimson: [220, 20, 60],
490 cyan: [0, 255, 255],
491 darkblue: [0, 0, 139],
492 darkcyan: [0, 139, 139],
493 darkgoldenrod: [184, 134, 11],
494 darkgray: [169, 169, 169],
495 darkgreen: [0, 100, 0],
496 darkgrey: [169, 169, 169],
497 darkkhaki: [189, 183, 107],
498 darkmagenta: [139, 0, 139],
499 darkolivegreen: [85, 107, 47],
500 darkorange: [255, 140, 0],
501 darkorchid: [153, 50, 204],
502 darkred: [139, 0, 0],
503 darksalmon: [233, 150, 122],
504 darkseagreen: [143, 188, 143],
505 darkslateblue: [72, 61, 139],
506 darkslategray: [47, 79, 79],
507 darkslategrey: [47, 79, 79],
508 darkturquoise: [0, 206, 209],
509 darkviolet: [148, 0, 211],
510 deeppink: [255, 20, 147],
511 deepskyblue: [0, 191, 255],
512 dimgray: [105, 105, 105],
513 dimgrey: [105, 105, 105],
514 dodgerblue: [30, 144, 255],
515 firebrick: [178, 34, 34],
516 floralwhite: [255, 250, 240],
517 forestgreen: [34, 139, 34],
518 fuchsia: [255, 0, 255],
519 gainsboro: [220, 220, 220],
520 ghostwhite: [248, 248, 255],
521 gold: [255, 215, 0],
522 goldenrod: [218, 165, 32],
523 gray: [128, 128, 128],
524 grey: [128, 128, 128],
525 green: [0, 128, 0],
526 greenyellow: [173, 255, 47],
527 honeydew: [240, 255, 240],
528 hotpink: [255, 105, 180],
529 indianred: [205, 92, 92],
530 indigo: [75, 0, 130],
531 ivory: [255, 255, 240],
532 khaki: [240, 230, 140],
533 lavender: [230, 230, 250],
534 lavenderblush: [255, 240, 245],
535 lawngreen: [124, 252, 0],
536 lemonchiffon: [255, 250, 205],
537 lightblue: [173, 216, 230],
538 lightcoral: [240, 128, 128],
539 lightcyan: [224, 255, 255],
540 lightgoldenrodyellow: [250, 250, 210],
541 lightgray: [211, 211, 211],
542 lightgreen: [144, 238, 144],
543 lightgrey: [211, 211, 211],
544 lightpink: [255, 182, 193],
545 lightsalmon: [255, 160, 122],
546 lightseagreen: [32, 178, 170],
547 lightskyblue: [135, 206, 250],
548 lightslategray: [119, 136, 153],
549 lightslategrey: [119, 136, 153],
550 lightsteelblue: [176, 196, 222],
551 lightyellow: [255, 255, 224],
552 lime: [0, 255, 0],
553 limegreen: [50, 205, 50],
554 linen: [250, 240, 230],
555 magenta: [255, 0, 255],
556 maroon: [128, 0, 0],
557 mediumaquamarine: [102, 205, 170],
558 mediumblue: [0, 0, 205],
559 mediumorchid: [186, 85, 211],
560 mediumpurple: [147, 112, 219],
561 mediumseagreen: [60, 179, 113],
562 mediumslateblue: [123, 104, 238],
563 mediumspringgreen: [0, 250, 154],
564 mediumturquoise: [72, 209, 204],
565 mediumvioletred: [199, 21, 133],
566 midnightblue: [25, 25, 112],
567 mintcream: [245, 255, 250],
568 mistyrose: [255, 228, 225],
569 moccasin: [255, 228, 181],
570 navajowhite: [255, 222, 173],
571 navy: [0, 0, 128],
572 oldlace: [253, 245, 230],
573 olive: [128, 128, 0],
574 olivedrab: [107, 142, 35],
575 orange: [255, 165, 0],
576 orangered: [255, 69, 0],
577 orchid: [218, 112, 214],
578 palegoldenrod: [238, 232, 170],
579 palegreen: [152, 251, 152],
580 paleturquoise: [175, 238, 238],
581 palevioletred: [219, 112, 147],
582 papayawhip: [255, 239, 213],
583 peachpuff: [255, 218, 185],
584 peru: [205, 133, 63],
585 pink: [255, 192, 203],
586 plum: [221, 160, 221],
587 powderblue: [176, 224, 230],
588 purple: [128, 0, 128],
589 red: [255, 0, 0],
590 rosybrown: [188, 143, 143],
591 royalblue: [65, 105, 225],
592 saddlebrown: [139, 69, 19],
593 salmon: [250, 128, 114],
594 sandybrown: [244, 164, 96],
595 seagreen: [46, 139, 87],
596 seashell: [255, 245, 238],
597 sienna: [160, 82, 45],
598 silver: [192, 192, 192],
599 skyblue: [135, 206, 235],
600 slateblue: [106, 90, 205],
601 slategray: [112, 128, 144],
602 slategrey: [112, 128, 144],
603 snow: [255, 250, 250],
604 springgreen: [0, 255, 127],
605 steelblue: [70, 130, 180],
606 tan: [210, 180, 140],
607 teal: [0, 128, 128],
608 thistle: [216, 191, 216],
609 tomato: [255, 99, 71],
610 turquoise: [64, 224, 208],
611 violet: [238, 130, 238],
612 wheat: [245, 222, 179],
613 white: [255, 255, 255],
614 whitesmoke: [245, 245, 245],
615 yellow: [255, 255, 0],
616 yellowgreen: [154, 205, 50]
617 };
618
619 var setMap = function setMap(options) {
620 var obj = options.map;
621 var keys = options.keys;
622 var l = keys.length;
623
624 for (var i = 0; i < l; i++) {
625 var key = keys[i];
626
627 if (plainObject(key)) {
628 throw Error('Tried to set map with object key');
629 }
630
631 if (i < keys.length - 1) {
632 // extend the map if necessary
633 if (obj[key] == null) {
634 obj[key] = {};
635 }
636
637 obj = obj[key];
638 } else {
639 // set the value
640 obj[key] = options.value;
641 }
642 }
643 }; // gets the value in a map even if it's not built in places
644
645 var getMap = function getMap(options) {
646 var obj = options.map;
647 var keys = options.keys;
648 var l = keys.length;
649
650 for (var i = 0; i < l; i++) {
651 var key = keys[i];
652
653 if (plainObject(key)) {
654 throw Error('Tried to get map with object key');
655 }
656
657 obj = obj[key];
658
659 if (obj == null) {
660 return obj;
661 }
662 }
663
664 return obj;
665 }; // deletes the entry in the map
666
667 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
668
669 function createCommonjsModule(fn, module) {
670 return module = { exports: {} }, fn(module, module.exports), module.exports;
671 }
672
673 /**
674 * lodash (Custom Build) <https://lodash.com/>
675 * Build: `lodash modularize exports="npm" -o ./`
676 * Copyright jQuery Foundation and other contributors <https://jquery.org/>
677 * Released under MIT license <https://lodash.com/license>
678 * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
679 * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
680 */
681
682 /** Used as the `TypeError` message for "Functions" methods. */
683 var FUNC_ERROR_TEXT = 'Expected a function';
684
685 /** Used as references for various `Number` constants. */
686 var NAN = 0 / 0;
687
688 /** `Object#toString` result references. */
689 var symbolTag = '[object Symbol]';
690
691 /** Used to match leading and trailing whitespace. */
692 var reTrim = /^\s+|\s+$/g;
693
694 /** Used to detect bad signed hexadecimal string values. */
695 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
696
697 /** Used to detect binary string values. */
698 var reIsBinary = /^0b[01]+$/i;
699
700 /** Used to detect octal string values. */
701 var reIsOctal = /^0o[0-7]+$/i;
702
703 /** Built-in method references without a dependency on `root`. */
704 var freeParseInt = parseInt;
705
706 /** Detect free variable `global` from Node.js. */
707 var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
708
709 /** Detect free variable `self`. */
710 var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
711
712 /** Used as a reference to the global object. */
713 var root = freeGlobal || freeSelf || Function('return this')();
714
715 /** Used for built-in method references. */
716 var objectProto = Object.prototype;
717
718 /**
719 * Used to resolve the
720 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
721 * of values.
722 */
723 var objectToString = objectProto.toString;
724
725 /* Built-in method references for those with the same name as other `lodash` methods. */
726 var nativeMax = Math.max,
727 nativeMin = Math.min;
728
729 /**
730 * Gets the timestamp of the number of milliseconds that have elapsed since
731 * the Unix epoch (1 January 1970 00:00:00 UTC).
732 *
733 * @static
734 * @memberOf _
735 * @since 2.4.0
736 * @category Date
737 * @returns {number} Returns the timestamp.
738 * @example
739 *
740 * _.defer(function(stamp) {
741 * console.log(_.now() - stamp);
742 * }, _.now());
743 * // => Logs the number of milliseconds it took for the deferred invocation.
744 */
745 var now = function() {
746 return root.Date.now();
747 };
748
749 /**
750 * Creates a debounced function that delays invoking `func` until after `wait`
751 * milliseconds have elapsed since the last time the debounced function was
752 * invoked. The debounced function comes with a `cancel` method to cancel
753 * delayed `func` invocations and a `flush` method to immediately invoke them.
754 * Provide `options` to indicate whether `func` should be invoked on the
755 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
756 * with the last arguments provided to the debounced function. Subsequent
757 * calls to the debounced function return the result of the last `func`
758 * invocation.
759 *
760 * **Note:** If `leading` and `trailing` options are `true`, `func` is
761 * invoked on the trailing edge of the timeout only if the debounced function
762 * is invoked more than once during the `wait` timeout.
763 *
764 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
765 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
766 *
767 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
768 * for details over the differences between `_.debounce` and `_.throttle`.
769 *
770 * @static
771 * @memberOf _
772 * @since 0.1.0
773 * @category Function
774 * @param {Function} func The function to debounce.
775 * @param {number} [wait=0] The number of milliseconds to delay.
776 * @param {Object} [options={}] The options object.
777 * @param {boolean} [options.leading=false]
778 * Specify invoking on the leading edge of the timeout.
779 * @param {number} [options.maxWait]
780 * The maximum time `func` is allowed to be delayed before it's invoked.
781 * @param {boolean} [options.trailing=true]
782 * Specify invoking on the trailing edge of the timeout.
783 * @returns {Function} Returns the new debounced function.
784 * @example
785 *
786 * // Avoid costly calculations while the window size is in flux.
787 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
788 *
789 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
790 * jQuery(element).on('click', _.debounce(sendMail, 300, {
791 * 'leading': true,
792 * 'trailing': false
793 * }));
794 *
795 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
796 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
797 * var source = new EventSource('/stream');
798 * jQuery(source).on('message', debounced);
799 *
800 * // Cancel the trailing debounced invocation.
801 * jQuery(window).on('popstate', debounced.cancel);
802 */
803 function debounce(func, wait, options) {
804 var lastArgs,
805 lastThis,
806 maxWait,
807 result,
808 timerId,
809 lastCallTime,
810 lastInvokeTime = 0,
811 leading = false,
812 maxing = false,
813 trailing = true;
814
815 if (typeof func != 'function') {
816 throw new TypeError(FUNC_ERROR_TEXT);
817 }
818 wait = toNumber(wait) || 0;
819 if (isObject(options)) {
820 leading = !!options.leading;
821 maxing = 'maxWait' in options;
822 maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
823 trailing = 'trailing' in options ? !!options.trailing : trailing;
824 }
825
826 function invokeFunc(time) {
827 var args = lastArgs,
828 thisArg = lastThis;
829
830 lastArgs = lastThis = undefined;
831 lastInvokeTime = time;
832 result = func.apply(thisArg, args);
833 return result;
834 }
835
836 function leadingEdge(time) {
837 // Reset any `maxWait` timer.
838 lastInvokeTime = time;
839 // Start the timer for the trailing edge.
840 timerId = setTimeout(timerExpired, wait);
841 // Invoke the leading edge.
842 return leading ? invokeFunc(time) : result;
843 }
844
845 function remainingWait(time) {
846 var timeSinceLastCall = time - lastCallTime,
847 timeSinceLastInvoke = time - lastInvokeTime,
848 result = wait - timeSinceLastCall;
849
850 return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
851 }
852
853 function shouldInvoke(time) {
854 var timeSinceLastCall = time - lastCallTime,
855 timeSinceLastInvoke = time - lastInvokeTime;
856
857 // Either this is the first call, activity has stopped and we're at the
858 // trailing edge, the system time has gone backwards and we're treating
859 // it as the trailing edge, or we've hit the `maxWait` limit.
860 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
861 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
862 }
863
864 function timerExpired() {
865 var time = now();
866 if (shouldInvoke(time)) {
867 return trailingEdge(time);
868 }
869 // Restart the timer.
870 timerId = setTimeout(timerExpired, remainingWait(time));
871 }
872
873 function trailingEdge(time) {
874 timerId = undefined;
875
876 // Only invoke if we have `lastArgs` which means `func` has been
877 // debounced at least once.
878 if (trailing && lastArgs) {
879 return invokeFunc(time);
880 }
881 lastArgs = lastThis = undefined;
882 return result;
883 }
884
885 function cancel() {
886 if (timerId !== undefined) {
887 clearTimeout(timerId);
888 }
889 lastInvokeTime = 0;
890 lastArgs = lastCallTime = lastThis = timerId = undefined;
891 }
892
893 function flush() {
894 return timerId === undefined ? result : trailingEdge(now());
895 }
896
897 function debounced() {
898 var time = now(),
899 isInvoking = shouldInvoke(time);
900
901 lastArgs = arguments;
902 lastThis = this;
903 lastCallTime = time;
904
905 if (isInvoking) {
906 if (timerId === undefined) {
907 return leadingEdge(lastCallTime);
908 }
909 if (maxing) {
910 // Handle invocations in a tight loop.
911 timerId = setTimeout(timerExpired, wait);
912 return invokeFunc(lastCallTime);
913 }
914 }
915 if (timerId === undefined) {
916 timerId = setTimeout(timerExpired, wait);
917 }
918 return result;
919 }
920 debounced.cancel = cancel;
921 debounced.flush = flush;
922 return debounced;
923 }
924
925 /**
926 * Checks if `value` is the
927 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
928 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
929 *
930 * @static
931 * @memberOf _
932 * @since 0.1.0
933 * @category Lang
934 * @param {*} value The value to check.
935 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
936 * @example
937 *
938 * _.isObject({});
939 * // => true
940 *
941 * _.isObject([1, 2, 3]);
942 * // => true
943 *
944 * _.isObject(_.noop);
945 * // => true
946 *
947 * _.isObject(null);
948 * // => false
949 */
950 function isObject(value) {
951 var type = typeof value;
952 return !!value && (type == 'object' || type == 'function');
953 }
954
955 /**
956 * Checks if `value` is object-like. A value is object-like if it's not `null`
957 * and has a `typeof` result of "object".
958 *
959 * @static
960 * @memberOf _
961 * @since 4.0.0
962 * @category Lang
963 * @param {*} value The value to check.
964 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
965 * @example
966 *
967 * _.isObjectLike({});
968 * // => true
969 *
970 * _.isObjectLike([1, 2, 3]);
971 * // => true
972 *
973 * _.isObjectLike(_.noop);
974 * // => false
975 *
976 * _.isObjectLike(null);
977 * // => false
978 */
979 function isObjectLike(value) {
980 return !!value && typeof value == 'object';
981 }
982
983 /**
984 * Checks if `value` is classified as a `Symbol` primitive or object.
985 *
986 * @static
987 * @memberOf _
988 * @since 4.0.0
989 * @category Lang
990 * @param {*} value The value to check.
991 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
992 * @example
993 *
994 * _.isSymbol(Symbol.iterator);
995 * // => true
996 *
997 * _.isSymbol('abc');
998 * // => false
999 */
1000 function isSymbol(value) {
1001 return typeof value == 'symbol' ||
1002 (isObjectLike(value) && objectToString.call(value) == symbolTag);
1003 }
1004
1005 /**
1006 * Converts `value` to a number.
1007 *
1008 * @static
1009 * @memberOf _
1010 * @since 4.0.0
1011 * @category Lang
1012 * @param {*} value The value to process.
1013 * @returns {number} Returns the number.
1014 * @example
1015 *
1016 * _.toNumber(3.2);
1017 * // => 3.2
1018 *
1019 * _.toNumber(Number.MIN_VALUE);
1020 * // => 5e-324
1021 *
1022 * _.toNumber(Infinity);
1023 * // => Infinity
1024 *
1025 * _.toNumber('3.2');
1026 * // => 3.2
1027 */
1028 function toNumber(value) {
1029 if (typeof value == 'number') {
1030 return value;
1031 }
1032 if (isSymbol(value)) {
1033 return NAN;
1034 }
1035 if (isObject(value)) {
1036 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
1037 value = isObject(other) ? (other + '') : other;
1038 }
1039 if (typeof value != 'string') {
1040 return value === 0 ? value : +value;
1041 }
1042 value = value.replace(reTrim, '');
1043 var isBinary = reIsBinary.test(value);
1044 return (isBinary || reIsOctal.test(value))
1045 ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
1046 : (reIsBadHex.test(value) ? NAN : +value);
1047 }
1048
1049 var lodash_debounce = debounce;
1050
1051 var performance = window$1 ? window$1.performance : null;
1052 var pnow = performance && performance.now ? function () {
1053 return performance.now();
1054 } : function () {
1055 return Date.now();
1056 };
1057
1058 var raf = function () {
1059 if (window$1) {
1060 if (window$1.requestAnimationFrame) {
1061 return function (fn) {
1062 window$1.requestAnimationFrame(fn);
1063 };
1064 } else if (window$1.mozRequestAnimationFrame) {
1065 return function (fn) {
1066 window$1.mozRequestAnimationFrame(fn);
1067 };
1068 } else if (window$1.webkitRequestAnimationFrame) {
1069 return function (fn) {
1070 window$1.webkitRequestAnimationFrame(fn);
1071 };
1072 } else if (window$1.msRequestAnimationFrame) {
1073 return function (fn) {
1074 window$1.msRequestAnimationFrame(fn);
1075 };
1076 }
1077 }
1078
1079 return function (fn) {
1080 if (fn) {
1081 setTimeout(function () {
1082 fn(pnow());
1083 }, 1000 / 60);
1084 }
1085 };
1086 }();
1087
1088 var requestAnimationFrame = function requestAnimationFrame(fn) {
1089 return raf(fn);
1090 };
1091 var performanceNow = pnow;
1092
1093 var DEFAULT_SEED = 5381;
1094 var hashIterableInts = function hashIterableInts(iterator) {
1095 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
1096 // djb2/string-hash
1097 var hash = seed;
1098 var entry;
1099
1100 for (;;) {
1101 entry = iterator.next();
1102
1103 if (entry.done) {
1104 break;
1105 }
1106
1107 hash = (hash << 5) + hash + entry.value | 0;
1108 }
1109
1110 return hash;
1111 };
1112 var hashInt = function hashInt(num) {
1113 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
1114 // djb2/string-hash
1115 return (seed << 5) + seed + num | 0;
1116 };
1117 var hashIntsArray = function hashIntsArray(ints, seed) {
1118 var entry = {
1119 value: 0,
1120 done: false
1121 };
1122 var i = 0;
1123 var length = ints.length;
1124 var iterator = {
1125 next: function next() {
1126 if (i < length) {
1127 entry.value = ints[i++];
1128 } else {
1129 entry.done = true;
1130 }
1131
1132 return entry;
1133 }
1134 };
1135 return hashIterableInts(iterator, seed);
1136 };
1137 var hashString = function hashString(str, seed) {
1138 var entry = {
1139 value: 0,
1140 done: false
1141 };
1142 var i = 0;
1143 var length = str.length;
1144 var iterator = {
1145 next: function next() {
1146 if (i < length) {
1147 entry.value = str.charCodeAt(i++);
1148 } else {
1149 entry.done = true;
1150 }
1151
1152 return entry;
1153 }
1154 };
1155 return hashIterableInts(iterator, seed);
1156 };
1157 var hashStrings = function hashStrings() {
1158 return hashStringsArray(arguments);
1159 };
1160 var hashStringsArray = function hashStringsArray(strs) {
1161 var hash;
1162
1163 for (var i = 0; i < strs.length; i++) {
1164 var str = strs[i];
1165
1166 if (i === 0) {
1167 hash = hashString(str);
1168 } else {
1169 hash = hashString(str, hash);
1170 }
1171 }
1172
1173 return hash;
1174 };
1175
1176 /*global console */
1177 var warningsEnabled = true;
1178 var warnSupported = console.warn != null; // eslint-disable-line no-console
1179
1180 var traceSupported = console.trace != null; // eslint-disable-line no-console
1181
1182 var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
1183 var trueify = function trueify() {
1184 return true;
1185 };
1186 var falsify = function falsify() {
1187 return false;
1188 };
1189 var zeroify = function zeroify() {
1190 return 0;
1191 };
1192 var noop = function noop() {};
1193 var error = function error(msg) {
1194 throw new Error(msg);
1195 };
1196 var warnings = function warnings(enabled) {
1197 if (enabled !== undefined) {
1198 warningsEnabled = !!enabled;
1199 } else {
1200 return warningsEnabled;
1201 }
1202 };
1203 var warn = function warn(msg) {
1204 /* eslint-disable no-console */
1205 if (!warnings()) {
1206 return;
1207 }
1208
1209 if (warnSupported) {
1210 console.warn(msg);
1211 } else {
1212 console.log(msg);
1213
1214 if (traceSupported) {
1215 console.trace();
1216 }
1217 }
1218 };
1219 /* eslint-enable */
1220
1221 var clone = function clone(obj) {
1222 return extend({}, obj);
1223 }; // gets a shallow copy of the argument
1224
1225 var copy = function copy(obj) {
1226 if (obj == null) {
1227 return obj;
1228 }
1229
1230 if (array(obj)) {
1231 return obj.slice();
1232 } else if (plainObject(obj)) {
1233 return clone(obj);
1234 } else {
1235 return obj;
1236 }
1237 };
1238 var copyArray = function copyArray(arr) {
1239 return arr.slice();
1240 };
1241 var uuid = function uuid(a, b
1242 /* placeholders */
1243 ) {
1244 for ( // loop :)
1245 b = a = ''; // b - result , a - numeric letiable
1246 a++ < 36; //
1247 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
1248 ? // return a random number or 4
1249 (a ^ 15 // if "a" is not 15
1250 ? // genetate a random number from 0 to 15
1251 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
1252 : 4 // otherwise 4
1253 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
1254 ) {
1255 }
1256
1257 return b;
1258 };
1259 var _staticEmptyObject = {};
1260 var staticEmptyObject = function staticEmptyObject() {
1261 return _staticEmptyObject;
1262 };
1263 var defaults = function defaults(_defaults) {
1264 var keys = Object.keys(_defaults);
1265 return function (opts) {
1266 var filledOpts = {};
1267
1268 for (var i = 0; i < keys.length; i++) {
1269 var key = keys[i];
1270 var optVal = opts == null ? undefined : opts[key];
1271 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
1272 }
1273
1274 return filledOpts;
1275 };
1276 };
1277 var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
1278 for (var i = arr.length; i >= 0; i--) {
1279 if (arr[i] === ele) {
1280 arr.splice(i, 1);
1281
1282 if (!manyCopies) {
1283 break;
1284 }
1285 }
1286 }
1287 };
1288 var clearArray = function clearArray(arr) {
1289 arr.splice(0, arr.length);
1290 };
1291 var push = function push(arr, otherArr) {
1292 for (var i = 0; i < otherArr.length; i++) {
1293 var el = otherArr[i];
1294 arr.push(el);
1295 }
1296 };
1297 var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
1298 if (prefix) {
1299 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1300 }
1301
1302 return obj[propName];
1303 };
1304 var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
1305 if (prefix) {
1306 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1307 }
1308
1309 obj[propName] = value;
1310 };
1311
1312 /* global Map */
1313 var ObjectMap =
1314 /*#__PURE__*/
1315 function () {
1316 function ObjectMap() {
1317 _classCallCheck(this, ObjectMap);
1318
1319 this._obj = {};
1320 }
1321
1322 _createClass(ObjectMap, [{
1323 key: "set",
1324 value: function set(key, val) {
1325 this._obj[key] = val;
1326 return this;
1327 }
1328 }, {
1329 key: "delete",
1330 value: function _delete(key) {
1331 this._obj[key] = undefined;
1332 return this;
1333 }
1334 }, {
1335 key: "clear",
1336 value: function clear() {
1337 this._obj = {};
1338 }
1339 }, {
1340 key: "has",
1341 value: function has(key) {
1342 return this._obj[key] !== undefined;
1343 }
1344 }, {
1345 key: "get",
1346 value: function get(key) {
1347 return this._obj[key];
1348 }
1349 }]);
1350
1351 return ObjectMap;
1352 }();
1353
1354 var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
1355
1356 /* global Set */
1357 var undef = "undefined" ;
1358
1359 var ObjectSet =
1360 /*#__PURE__*/
1361 function () {
1362 function ObjectSet(arrayOrObjectSet) {
1363 _classCallCheck(this, ObjectSet);
1364
1365 this._obj = Object.create(null);
1366 this.size = 0;
1367
1368 if (arrayOrObjectSet != null) {
1369 var arr;
1370
1371 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1372 arr = arrayOrObjectSet.toArray();
1373 } else {
1374 arr = arrayOrObjectSet;
1375 }
1376
1377 for (var i = 0; i < arr.length; i++) {
1378 this.add(arr[i]);
1379 }
1380 }
1381 }
1382
1383 _createClass(ObjectSet, [{
1384 key: "instanceString",
1385 value: function instanceString() {
1386 return 'set';
1387 }
1388 }, {
1389 key: "add",
1390 value: function add(val) {
1391 var o = this._obj;
1392
1393 if (o[val] !== 1) {
1394 o[val] = 1;
1395 this.size++;
1396 }
1397 }
1398 }, {
1399 key: "delete",
1400 value: function _delete(val) {
1401 var o = this._obj;
1402
1403 if (o[val] === 1) {
1404 o[val] = 0;
1405 this.size--;
1406 }
1407 }
1408 }, {
1409 key: "clear",
1410 value: function clear() {
1411 this._obj = Object.create(null);
1412 }
1413 }, {
1414 key: "has",
1415 value: function has(val) {
1416 return this._obj[val] === 1;
1417 }
1418 }, {
1419 key: "toArray",
1420 value: function toArray() {
1421 var _this = this;
1422
1423 return Object.keys(this._obj).filter(function (key) {
1424 return _this.has(key);
1425 });
1426 }
1427 }, {
1428 key: "forEach",
1429 value: function forEach(callback, thisArg) {
1430 return this.toArray().forEach(callback, thisArg);
1431 }
1432 }]);
1433
1434 return ObjectSet;
1435 }();
1436
1437 var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1438
1439 var Element = function Element(cy, params, restore) {
1440 restore = restore === undefined || restore ? true : false;
1441
1442 if (cy === undefined || params === undefined || !core(cy)) {
1443 error('An element must have a core reference and parameters set');
1444 return;
1445 }
1446
1447 var group = params.group; // try to automatically infer the group if unspecified
1448
1449 if (group == null) {
1450 if (params.data && params.data.source != null && params.data.target != null) {
1451 group = 'edges';
1452 } else {
1453 group = 'nodes';
1454 }
1455 } // validate group
1456
1457
1458 if (group !== 'nodes' && group !== 'edges') {
1459 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1460 return;
1461 } // make the element array-like, just like a collection
1462
1463
1464 this.length = 1;
1465 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1466
1467 var _p = this._private = {
1468 cy: cy,
1469 single: true,
1470 // indicates this is an element
1471 data: params.data || {},
1472 // data object
1473 position: params.position || {
1474 x: 0,
1475 y: 0
1476 },
1477 // (x, y) position pair
1478 autoWidth: undefined,
1479 // width and height of nodes calculated by the renderer when set to special 'auto' value
1480 autoHeight: undefined,
1481 autoPadding: undefined,
1482 compoundBoundsClean: false,
1483 // whether the compound dimensions need to be recalculated the next time dimensions are read
1484 listeners: [],
1485 // array of bound listeners
1486 group: group,
1487 // string; 'nodes' or 'edges'
1488 style: {},
1489 // properties as set by the style
1490 rstyle: {},
1491 // properties for style sent from the renderer to the core
1492 styleCxts: [],
1493 // applied style contexts from the styler
1494 styleKeys: {},
1495 // per-group keys of style property values
1496 removed: true,
1497 // whether it's inside the vis; true if removed (set true here since we call restore)
1498 selected: params.selected ? true : false,
1499 // whether it's selected
1500 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1501 // whether it's selectable
1502 locked: params.locked ? true : false,
1503 // whether the element is locked (cannot be moved)
1504 grabbed: false,
1505 // whether the element is grabbed by the mouse; renderer sets this privately
1506 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1507 // whether the element can be grabbed
1508 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1509 // whether the element has passthrough panning enabled
1510 active: false,
1511 // whether the element is active from user interaction
1512 classes: new Set$1(),
1513 // map ( className => true )
1514 animation: {
1515 // object for currently-running animations
1516 current: [],
1517 queue: []
1518 },
1519 rscratch: {},
1520 // object in which the renderer can store information
1521 scratch: params.scratch || {},
1522 // scratch objects
1523 edges: [],
1524 // array of connected edges
1525 children: [],
1526 // array of children
1527 parent: null,
1528 // parent ref
1529 traversalCache: {},
1530 // cache of output of traversal functions
1531 backgrounding: false,
1532 // whether background images are loading
1533 bbCache: null,
1534 // cache of the current bounding box
1535 bbCacheShift: {
1536 x: 0,
1537 y: 0
1538 },
1539 // shift applied to cached bb to be applied on next get
1540 bodyBounds: null,
1541 // bounds cache of element body, w/o overlay
1542 overlayBounds: null,
1543 // bounds cache of element body, including overlay
1544 labelBounds: {
1545 // bounds cache of labels
1546 all: null,
1547 source: null,
1548 target: null,
1549 main: null
1550 },
1551 arrowBounds: {
1552 // bounds cache of edge arrows
1553 source: null,
1554 target: null,
1555 'mid-source': null,
1556 'mid-target': null
1557 }
1558 };
1559
1560 if (_p.position.x == null) {
1561 _p.position.x = 0;
1562 }
1563
1564 if (_p.position.y == null) {
1565 _p.position.y = 0;
1566 } // renderedPosition overrides if specified
1567
1568
1569 if (params.renderedPosition) {
1570 var rpos = params.renderedPosition;
1571 var pan = cy.pan();
1572 var zoom = cy.zoom();
1573 _p.position = {
1574 x: (rpos.x - pan.x) / zoom,
1575 y: (rpos.y - pan.y) / zoom
1576 };
1577 }
1578
1579 var classes = [];
1580
1581 if (array(params.classes)) {
1582 classes = params.classes;
1583 } else if (string(params.classes)) {
1584 classes = params.classes.split(/\s+/);
1585 }
1586
1587 for (var i = 0, l = classes.length; i < l; i++) {
1588 var cls = classes[i];
1589
1590 if (!cls || cls === '') {
1591 continue;
1592 }
1593
1594 _p.classes.add(cls);
1595 }
1596
1597 this.createEmitter();
1598 var bypass = params.style || params.css;
1599
1600 if (bypass) {
1601 warn('Setting a `style` bypass at element creation is deprecated');
1602 this.style(bypass);
1603 }
1604
1605 if (restore === undefined || restore) {
1606 this.restore();
1607 }
1608 };
1609
1610 var defineSearch = function defineSearch(params) {
1611 params = {
1612 bfs: params.bfs || !params.dfs,
1613 dfs: params.dfs || !params.bfs
1614 }; // from pseudocode on wikipedia
1615
1616 return function searchFn(roots, fn$1, directed) {
1617 var options;
1618
1619 if (plainObject(roots) && !elementOrCollection(roots)) {
1620 options = roots;
1621 roots = options.roots || options.root;
1622 fn$1 = options.visit;
1623 directed = options.directed;
1624 }
1625
1626 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1627 fn$1 = fn(fn$1) ? fn$1 : function () {};
1628 var cy = this._private.cy;
1629 var v = roots = string(roots) ? this.filter(roots) : roots;
1630 var Q = [];
1631 var connectedNodes = [];
1632 var connectedBy = {};
1633 var id2depth = {};
1634 var V = {};
1635 var j = 0;
1636 var found;
1637
1638 var _this$byGroup = this.byGroup(),
1639 nodes = _this$byGroup.nodes,
1640 edges = _this$byGroup.edges; // enqueue v
1641
1642
1643 for (var i = 0; i < v.length; i++) {
1644 var vi = v[i];
1645 var viId = vi.id();
1646
1647 if (vi.isNode()) {
1648 Q.unshift(vi);
1649
1650 if (params.bfs) {
1651 V[viId] = true;
1652 connectedNodes.push(vi);
1653 }
1654
1655 id2depth[viId] = 0;
1656 }
1657 }
1658
1659 var _loop2 = function _loop2() {
1660 var v = params.bfs ? Q.shift() : Q.pop();
1661 var vId = v.id();
1662
1663 if (params.dfs) {
1664 if (V[vId]) {
1665 return "continue";
1666 }
1667
1668 V[vId] = true;
1669 connectedNodes.push(v);
1670 }
1671
1672 var depth = id2depth[vId];
1673 var prevEdge = connectedBy[vId];
1674 var src = prevEdge != null ? prevEdge.source() : null;
1675 var tgt = prevEdge != null ? prevEdge.target() : null;
1676 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1677 var ret = void 0;
1678 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1679
1680 if (ret === true) {
1681 found = v;
1682 return "break";
1683 }
1684
1685 if (ret === false) {
1686 return "break";
1687 }
1688
1689 var vwEdges = v.connectedEdges().filter(function (e) {
1690 return (!directed || e.source().same(v)) && edges.has(e);
1691 });
1692
1693 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1694 var e = vwEdges[_i2];
1695 var w = e.connectedNodes().filter(function (n) {
1696 return !n.same(v) && nodes.has(n);
1697 });
1698 var wId = w.id();
1699
1700 if (w.length !== 0 && !V[wId]) {
1701 w = w[0];
1702 Q.push(w);
1703
1704 if (params.bfs) {
1705 V[wId] = true;
1706 connectedNodes.push(w);
1707 }
1708
1709 connectedBy[wId] = e;
1710 id2depth[wId] = id2depth[vId] + 1;
1711 }
1712 }
1713 };
1714
1715 _loop: while (Q.length !== 0) {
1716 var _ret = _loop2();
1717
1718 switch (_ret) {
1719 case "continue":
1720 continue;
1721
1722 case "break":
1723 break _loop;
1724 }
1725 }
1726
1727 var connectedEles = cy.collection();
1728
1729 for (var _i = 0; _i < connectedNodes.length; _i++) {
1730 var node = connectedNodes[_i];
1731 var edge = connectedBy[node.id()];
1732
1733 if (edge != null) {
1734 connectedEles.merge(edge);
1735 }
1736
1737 connectedEles.merge(node);
1738 }
1739
1740 return {
1741 path: cy.collection(connectedEles),
1742 found: cy.collection(found)
1743 };
1744 };
1745 }; // search, spanning trees, etc
1746
1747
1748 var elesfn = {
1749 breadthFirstSearch: defineSearch({
1750 bfs: true
1751 }),
1752 depthFirstSearch: defineSearch({
1753 dfs: true
1754 })
1755 }; // nice, short mathemathical alias
1756
1757 elesfn.bfs = elesfn.breadthFirstSearch;
1758 elesfn.dfs = elesfn.depthFirstSearch;
1759
1760 var heap = createCommonjsModule(function (module, exports) {
1761 // Generated by CoffeeScript 1.8.0
1762 (function() {
1763 var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
1764
1765 floor = Math.floor, min = Math.min;
1766
1767
1768 /*
1769 Default comparison function to be used
1770 */
1771
1772 defaultCmp = function(x, y) {
1773 if (x < y) {
1774 return -1;
1775 }
1776 if (x > y) {
1777 return 1;
1778 }
1779 return 0;
1780 };
1781
1782
1783 /*
1784 Insert item x in list a, and keep it sorted assuming a is sorted.
1785
1786 If x is already in a, insert it to the right of the rightmost x.
1787
1788 Optional args lo (default 0) and hi (default a.length) bound the slice
1789 of a to be searched.
1790 */
1791
1792 insort = function(a, x, lo, hi, cmp) {
1793 var mid;
1794 if (lo == null) {
1795 lo = 0;
1796 }
1797 if (cmp == null) {
1798 cmp = defaultCmp;
1799 }
1800 if (lo < 0) {
1801 throw new Error('lo must be non-negative');
1802 }
1803 if (hi == null) {
1804 hi = a.length;
1805 }
1806 while (lo < hi) {
1807 mid = floor((lo + hi) / 2);
1808 if (cmp(x, a[mid]) < 0) {
1809 hi = mid;
1810 } else {
1811 lo = mid + 1;
1812 }
1813 }
1814 return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
1815 };
1816
1817
1818 /*
1819 Push item onto heap, maintaining the heap invariant.
1820 */
1821
1822 heappush = function(array, item, cmp) {
1823 if (cmp == null) {
1824 cmp = defaultCmp;
1825 }
1826 array.push(item);
1827 return _siftdown(array, 0, array.length - 1, cmp);
1828 };
1829
1830
1831 /*
1832 Pop the smallest item off the heap, maintaining the heap invariant.
1833 */
1834
1835 heappop = function(array, cmp) {
1836 var lastelt, returnitem;
1837 if (cmp == null) {
1838 cmp = defaultCmp;
1839 }
1840 lastelt = array.pop();
1841 if (array.length) {
1842 returnitem = array[0];
1843 array[0] = lastelt;
1844 _siftup(array, 0, cmp);
1845 } else {
1846 returnitem = lastelt;
1847 }
1848 return returnitem;
1849 };
1850
1851
1852 /*
1853 Pop and return the current smallest value, and add the new item.
1854
1855 This is more efficient than heappop() followed by heappush(), and can be
1856 more appropriate when using a fixed size heap. Note that the value
1857 returned may be larger than item! That constrains reasonable use of
1858 this routine unless written as part of a conditional replacement:
1859 if item > array[0]
1860 item = heapreplace(array, item)
1861 */
1862
1863 heapreplace = function(array, item, cmp) {
1864 var returnitem;
1865 if (cmp == null) {
1866 cmp = defaultCmp;
1867 }
1868 returnitem = array[0];
1869 array[0] = item;
1870 _siftup(array, 0, cmp);
1871 return returnitem;
1872 };
1873
1874
1875 /*
1876 Fast version of a heappush followed by a heappop.
1877 */
1878
1879 heappushpop = function(array, item, cmp) {
1880 var _ref;
1881 if (cmp == null) {
1882 cmp = defaultCmp;
1883 }
1884 if (array.length && cmp(array[0], item) < 0) {
1885 _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
1886 _siftup(array, 0, cmp);
1887 }
1888 return item;
1889 };
1890
1891
1892 /*
1893 Transform list into a heap, in-place, in O(array.length) time.
1894 */
1895
1896 heapify = function(array, cmp) {
1897 var i, _i, _len, _ref1, _results, _results1;
1898 if (cmp == null) {
1899 cmp = defaultCmp;
1900 }
1901 _ref1 = (function() {
1902 _results1 = [];
1903 for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
1904 return _results1;
1905 }).apply(this).reverse();
1906 _results = [];
1907 for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
1908 i = _ref1[_i];
1909 _results.push(_siftup(array, i, cmp));
1910 }
1911 return _results;
1912 };
1913
1914
1915 /*
1916 Update the position of the given item in the heap.
1917 This function should be called every time the item is being modified.
1918 */
1919
1920 updateItem = function(array, item, cmp) {
1921 var pos;
1922 if (cmp == null) {
1923 cmp = defaultCmp;
1924 }
1925 pos = array.indexOf(item);
1926 if (pos === -1) {
1927 return;
1928 }
1929 _siftdown(array, 0, pos, cmp);
1930 return _siftup(array, pos, cmp);
1931 };
1932
1933
1934 /*
1935 Find the n largest elements in a dataset.
1936 */
1937
1938 nlargest = function(array, n, cmp) {
1939 var elem, result, _i, _len, _ref;
1940 if (cmp == null) {
1941 cmp = defaultCmp;
1942 }
1943 result = array.slice(0, n);
1944 if (!result.length) {
1945 return result;
1946 }
1947 heapify(result, cmp);
1948 _ref = array.slice(n);
1949 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1950 elem = _ref[_i];
1951 heappushpop(result, elem, cmp);
1952 }
1953 return result.sort(cmp).reverse();
1954 };
1955
1956
1957 /*
1958 Find the n smallest elements in a dataset.
1959 */
1960
1961 nsmallest = function(array, n, cmp) {
1962 var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
1963 if (cmp == null) {
1964 cmp = defaultCmp;
1965 }
1966 if (n * 10 <= array.length) {
1967 result = array.slice(0, n).sort(cmp);
1968 if (!result.length) {
1969 return result;
1970 }
1971 los = result[result.length - 1];
1972 _ref = array.slice(n);
1973 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1974 elem = _ref[_i];
1975 if (cmp(elem, los) < 0) {
1976 insort(result, elem, 0, null, cmp);
1977 result.pop();
1978 los = result[result.length - 1];
1979 }
1980 }
1981 return result;
1982 }
1983 heapify(array, cmp);
1984 _results = [];
1985 for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
1986 _results.push(heappop(array, cmp));
1987 }
1988 return _results;
1989 };
1990
1991 _siftdown = function(array, startpos, pos, cmp) {
1992 var newitem, parent, parentpos;
1993 if (cmp == null) {
1994 cmp = defaultCmp;
1995 }
1996 newitem = array[pos];
1997 while (pos > startpos) {
1998 parentpos = (pos - 1) >> 1;
1999 parent = array[parentpos];
2000 if (cmp(newitem, parent) < 0) {
2001 array[pos] = parent;
2002 pos = parentpos;
2003 continue;
2004 }
2005 break;
2006 }
2007 return array[pos] = newitem;
2008 };
2009
2010 _siftup = function(array, pos, cmp) {
2011 var childpos, endpos, newitem, rightpos, startpos;
2012 if (cmp == null) {
2013 cmp = defaultCmp;
2014 }
2015 endpos = array.length;
2016 startpos = pos;
2017 newitem = array[pos];
2018 childpos = 2 * pos + 1;
2019 while (childpos < endpos) {
2020 rightpos = childpos + 1;
2021 if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
2022 childpos = rightpos;
2023 }
2024 array[pos] = array[childpos];
2025 pos = childpos;
2026 childpos = 2 * pos + 1;
2027 }
2028 array[pos] = newitem;
2029 return _siftdown(array, startpos, pos, cmp);
2030 };
2031
2032 Heap = (function() {
2033 Heap.push = heappush;
2034
2035 Heap.pop = heappop;
2036
2037 Heap.replace = heapreplace;
2038
2039 Heap.pushpop = heappushpop;
2040
2041 Heap.heapify = heapify;
2042
2043 Heap.updateItem = updateItem;
2044
2045 Heap.nlargest = nlargest;
2046
2047 Heap.nsmallest = nsmallest;
2048
2049 function Heap(cmp) {
2050 this.cmp = cmp != null ? cmp : defaultCmp;
2051 this.nodes = [];
2052 }
2053
2054 Heap.prototype.push = function(x) {
2055 return heappush(this.nodes, x, this.cmp);
2056 };
2057
2058 Heap.prototype.pop = function() {
2059 return heappop(this.nodes, this.cmp);
2060 };
2061
2062 Heap.prototype.peek = function() {
2063 return this.nodes[0];
2064 };
2065
2066 Heap.prototype.contains = function(x) {
2067 return this.nodes.indexOf(x) !== -1;
2068 };
2069
2070 Heap.prototype.replace = function(x) {
2071 return heapreplace(this.nodes, x, this.cmp);
2072 };
2073
2074 Heap.prototype.pushpop = function(x) {
2075 return heappushpop(this.nodes, x, this.cmp);
2076 };
2077
2078 Heap.prototype.heapify = function() {
2079 return heapify(this.nodes, this.cmp);
2080 };
2081
2082 Heap.prototype.updateItem = function(x) {
2083 return updateItem(this.nodes, x, this.cmp);
2084 };
2085
2086 Heap.prototype.clear = function() {
2087 return this.nodes = [];
2088 };
2089
2090 Heap.prototype.empty = function() {
2091 return this.nodes.length === 0;
2092 };
2093
2094 Heap.prototype.size = function() {
2095 return this.nodes.length;
2096 };
2097
2098 Heap.prototype.clone = function() {
2099 var heap;
2100 heap = new Heap();
2101 heap.nodes = this.nodes.slice(0);
2102 return heap;
2103 };
2104
2105 Heap.prototype.toArray = function() {
2106 return this.nodes.slice(0);
2107 };
2108
2109 Heap.prototype.insert = Heap.prototype.push;
2110
2111 Heap.prototype.top = Heap.prototype.peek;
2112
2113 Heap.prototype.front = Heap.prototype.peek;
2114
2115 Heap.prototype.has = Heap.prototype.contains;
2116
2117 Heap.prototype.copy = Heap.prototype.clone;
2118
2119 return Heap;
2120
2121 })();
2122
2123 (function(root, factory) {
2124 {
2125 return module.exports = factory();
2126 }
2127 })(this, function() {
2128 return Heap;
2129 });
2130
2131 }).call(commonjsGlobal);
2132 });
2133
2134 var heap$1 = heap;
2135
2136 var dijkstraDefaults = defaults({
2137 root: null,
2138 weight: function weight(edge) {
2139 return 1;
2140 },
2141 directed: false
2142 });
2143 var elesfn$1 = {
2144 dijkstra: function dijkstra(options) {
2145 if (!plainObject(options)) {
2146 var args = arguments;
2147 options = {
2148 root: args[0],
2149 weight: args[1],
2150 directed: args[2]
2151 };
2152 }
2153
2154 var _dijkstraDefaults = dijkstraDefaults(options),
2155 root = _dijkstraDefaults.root,
2156 weight = _dijkstraDefaults.weight,
2157 directed = _dijkstraDefaults.directed;
2158
2159 var eles = this;
2160 var weightFn = weight;
2161 var source = string(root) ? this.filter(root)[0] : root[0];
2162 var dist = {};
2163 var prev = {};
2164 var knownDist = {};
2165
2166 var _this$byGroup = this.byGroup(),
2167 nodes = _this$byGroup.nodes,
2168 edges = _this$byGroup.edges;
2169
2170 edges.unmergeBy(function (ele) {
2171 return ele.isLoop();
2172 });
2173
2174 var getDist = function getDist(node) {
2175 return dist[node.id()];
2176 };
2177
2178 var setDist = function setDist(node, d) {
2179 dist[node.id()] = d;
2180 Q.updateItem(node);
2181 };
2182
2183 var Q = new heap$1(function (a, b) {
2184 return getDist(a) - getDist(b);
2185 });
2186
2187 for (var i = 0; i < nodes.length; i++) {
2188 var node = nodes[i];
2189 dist[node.id()] = node.same(source) ? 0 : Infinity;
2190 Q.push(node);
2191 }
2192
2193 var distBetween = function distBetween(u, v) {
2194 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
2195 var smallestDistance = Infinity;
2196 var smallestEdge;
2197
2198 for (var _i = 0; _i < uvs.length; _i++) {
2199 var edge = uvs[_i];
2200
2201 var _weight = weightFn(edge);
2202
2203 if (_weight < smallestDistance || !smallestEdge) {
2204 smallestDistance = _weight;
2205 smallestEdge = edge;
2206 }
2207 }
2208
2209 return {
2210 edge: smallestEdge,
2211 dist: smallestDistance
2212 };
2213 };
2214
2215 while (Q.size() > 0) {
2216 var u = Q.pop();
2217 var smalletsDist = getDist(u);
2218 var uid = u.id();
2219 knownDist[uid] = smalletsDist;
2220
2221 if (smalletsDist === Infinity) {
2222 continue;
2223 }
2224
2225 var neighbors = u.neighborhood().intersect(nodes);
2226
2227 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
2228 var v = neighbors[_i2];
2229 var vid = v.id();
2230 var vDist = distBetween(u, v);
2231 var alt = smalletsDist + vDist.dist;
2232
2233 if (alt < getDist(v)) {
2234 setDist(v, alt);
2235 prev[vid] = {
2236 node: u,
2237 edge: vDist.edge
2238 };
2239 }
2240 } // for
2241
2242 } // while
2243
2244
2245 return {
2246 distanceTo: function distanceTo(node) {
2247 var target = string(node) ? nodes.filter(node)[0] : node[0];
2248 return knownDist[target.id()];
2249 },
2250 pathTo: function pathTo(node) {
2251 var target = string(node) ? nodes.filter(node)[0] : node[0];
2252 var S = [];
2253 var u = target;
2254 var uid = u.id();
2255
2256 if (target.length > 0) {
2257 S.unshift(target);
2258
2259 while (prev[uid]) {
2260 var p = prev[uid];
2261 S.unshift(p.edge);
2262 S.unshift(p.node);
2263 u = p.node;
2264 uid = u.id();
2265 }
2266 }
2267
2268 return eles.spawn(S);
2269 }
2270 };
2271 }
2272 };
2273
2274 var elesfn$2 = {
2275 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
2276 // implemented from pseudocode from wikipedia
2277 kruskal: function kruskal(weightFn) {
2278 weightFn = weightFn || function (edge) {
2279 return 1;
2280 };
2281
2282 var _this$byGroup = this.byGroup(),
2283 nodes = _this$byGroup.nodes,
2284 edges = _this$byGroup.edges;
2285
2286 var numNodes = nodes.length;
2287 var forest = new Array(numNodes);
2288 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
2289
2290 var findSetIndex = function findSetIndex(ele) {
2291 for (var i = 0; i < forest.length; i++) {
2292 var eles = forest[i];
2293
2294 if (eles.has(ele)) {
2295 return i;
2296 }
2297 }
2298 }; // start with one forest per node
2299
2300
2301 for (var i = 0; i < numNodes; i++) {
2302 forest[i] = this.spawn(nodes[i]);
2303 }
2304
2305 var S = edges.sort(function (a, b) {
2306 return weightFn(a) - weightFn(b);
2307 });
2308
2309 for (var _i = 0; _i < S.length; _i++) {
2310 var edge = S[_i];
2311 var u = edge.source()[0];
2312 var v = edge.target()[0];
2313 var setUIndex = findSetIndex(u);
2314 var setVIndex = findSetIndex(v);
2315 var setU = forest[setUIndex];
2316 var setV = forest[setVIndex];
2317
2318 if (setUIndex !== setVIndex) {
2319 A.merge(edge); // combine forests for u and v
2320
2321 setU.merge(setV);
2322 forest.splice(setVIndex, 1);
2323 }
2324 }
2325
2326 return A;
2327 }
2328 };
2329
2330 var aStarDefaults = defaults({
2331 root: null,
2332 goal: null,
2333 weight: function weight(edge) {
2334 return 1;
2335 },
2336 heuristic: function heuristic(edge) {
2337 return 0;
2338 },
2339 directed: false
2340 });
2341 var elesfn$3 = {
2342 // Implemented from pseudocode from wikipedia
2343 aStar: function aStar(options) {
2344 var cy = this.cy();
2345
2346 var _aStarDefaults = aStarDefaults(options),
2347 root = _aStarDefaults.root,
2348 goal = _aStarDefaults.goal,
2349 heuristic = _aStarDefaults.heuristic,
2350 directed = _aStarDefaults.directed,
2351 weight = _aStarDefaults.weight;
2352
2353 root = cy.collection(root)[0];
2354 goal = cy.collection(goal)[0];
2355 var sid = root.id();
2356 var tid = goal.id();
2357 var gScore = {};
2358 var fScore = {};
2359 var closedSetIds = {};
2360 var openSet = new heap$1(function (a, b) {
2361 return fScore[a.id()] - fScore[b.id()];
2362 });
2363 var openSetIds = new Set$1();
2364 var cameFrom = {};
2365 var cameFromEdge = {};
2366
2367 var addToOpenSet = function addToOpenSet(ele, id) {
2368 openSet.push(ele);
2369 openSetIds.add(id);
2370 };
2371
2372 var cMin, cMinId;
2373
2374 var popFromOpenSet = function popFromOpenSet() {
2375 cMin = openSet.pop();
2376 cMinId = cMin.id();
2377 openSetIds["delete"](cMinId);
2378 };
2379
2380 var isInOpenSet = function isInOpenSet(id) {
2381 return openSetIds.has(id);
2382 };
2383
2384 addToOpenSet(root, sid);
2385 gScore[sid] = 0;
2386 fScore[sid] = heuristic(root); // Counter
2387
2388 var steps = 0; // Main loop
2389
2390 while (openSet.size() > 0) {
2391 popFromOpenSet();
2392 steps++; // If we've found our goal, then we are done
2393
2394 if (cMinId === tid) {
2395 var path = [];
2396 var pathNode = goal;
2397 var pathNodeId = tid;
2398 var pathEdge = cameFromEdge[pathNodeId];
2399
2400 for (;;) {
2401 path.unshift(pathNode);
2402
2403 if (pathEdge != null) {
2404 path.unshift(pathEdge);
2405 }
2406
2407 pathNode = cameFrom[pathNodeId];
2408
2409 if (pathNode == null) {
2410 break;
2411 }
2412
2413 pathNodeId = pathNode.id();
2414 pathEdge = cameFromEdge[pathNodeId];
2415 }
2416
2417 return {
2418 found: true,
2419 distance: gScore[cMinId],
2420 path: this.spawn(path),
2421 steps: steps
2422 };
2423 } // Add cMin to processed nodes
2424
2425
2426 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
2427 // Take into account if graph is directed or not
2428
2429 var vwEdges = cMin._private.edges;
2430
2431 for (var i = 0; i < vwEdges.length; i++) {
2432 var e = vwEdges[i]; // edge must be in set of calling eles
2433
2434 if (!this.hasElementWithId(e.id())) {
2435 continue;
2436 } // cMin must be the source of edge if directed
2437
2438
2439 if (directed && e.data('source') !== cMinId) {
2440 continue;
2441 }
2442
2443 var wSrc = e.source();
2444 var wTgt = e.target();
2445 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
2446 var wid = w.id(); // node must be in set of calling eles
2447
2448 if (!this.hasElementWithId(wid)) {
2449 continue;
2450 } // if node is in closedSet, ignore it
2451
2452
2453 if (closedSetIds[wid]) {
2454 continue;
2455 } // New tentative score for node w
2456
2457
2458 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
2459 // w not present in openSet
2460 // OR
2461 // tentative gScore is less than previous value
2462 // w not in openSet
2463
2464 if (!isInOpenSet(wid)) {
2465 gScore[wid] = tempScore;
2466 fScore[wid] = tempScore + heuristic(w);
2467 addToOpenSet(w, wid);
2468 cameFrom[wid] = cMin;
2469 cameFromEdge[wid] = e;
2470 continue;
2471 } // w already in openSet, but with greater gScore
2472
2473
2474 if (tempScore < gScore[wid]) {
2475 gScore[wid] = tempScore;
2476 fScore[wid] = tempScore + heuristic(w);
2477 cameFrom[wid] = cMin;
2478 }
2479 } // End of neighbors update
2480
2481 } // End of main loop
2482 // If we've reached here, then we've not reached our goal
2483
2484
2485 return {
2486 found: false,
2487 distance: undefined,
2488 path: undefined,
2489 steps: steps
2490 };
2491 }
2492 }; // elesfn
2493
2494 var floydWarshallDefaults = defaults({
2495 weight: function weight(edge) {
2496 return 1;
2497 },
2498 directed: false
2499 });
2500 var elesfn$4 = {
2501 // Implemented from pseudocode from wikipedia
2502 floydWarshall: function floydWarshall(options) {
2503 var cy = this.cy();
2504
2505 var _floydWarshallDefault = floydWarshallDefaults(options),
2506 weight = _floydWarshallDefault.weight,
2507 directed = _floydWarshallDefault.directed;
2508
2509 var weightFn = weight;
2510
2511 var _this$byGroup = this.byGroup(),
2512 nodes = _this$byGroup.nodes,
2513 edges = _this$byGroup.edges;
2514
2515 var N = nodes.length;
2516 var Nsq = N * N;
2517
2518 var indexOf = function indexOf(node) {
2519 return nodes.indexOf(node);
2520 };
2521
2522 var atIndex = function atIndex(i) {
2523 return nodes[i];
2524 }; // Initialize distance matrix
2525
2526
2527 var dist = new Array(Nsq);
2528
2529 for (var n = 0; n < Nsq; n++) {
2530 var j = n % N;
2531 var i = (n - j) / N;
2532
2533 if (i === j) {
2534 dist[n] = 0;
2535 } else {
2536 dist[n] = Infinity;
2537 }
2538 } // Initialize matrix used for path reconstruction
2539 // Initialize distance matrix
2540
2541
2542 var next = new Array(Nsq);
2543 var edgeNext = new Array(Nsq); // Process edges
2544
2545 for (var _i = 0; _i < edges.length; _i++) {
2546 var edge = edges[_i];
2547 var src = edge.source()[0];
2548 var tgt = edge.target()[0];
2549
2550 if (src === tgt) {
2551 continue;
2552 } // exclude loops
2553
2554
2555 var s = indexOf(src);
2556 var t = indexOf(tgt);
2557 var st = s * N + t; // source to target index
2558
2559 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
2560
2561
2562 if (dist[st] > _weight) {
2563 dist[st] = _weight;
2564 next[st] = t;
2565 edgeNext[st] = edge;
2566 } // If undirected graph, process 'reversed' edge
2567
2568
2569 if (!directed) {
2570 var ts = t * N + s; // target to source index
2571
2572 if (!directed && dist[ts] > _weight) {
2573 dist[ts] = _weight;
2574 next[ts] = s;
2575 edgeNext[ts] = edge;
2576 }
2577 }
2578 } // Main loop
2579
2580
2581 for (var k = 0; k < N; k++) {
2582 for (var _i2 = 0; _i2 < N; _i2++) {
2583 var ik = _i2 * N + k;
2584
2585 for (var _j = 0; _j < N; _j++) {
2586 var ij = _i2 * N + _j;
2587 var kj = k * N + _j;
2588
2589 if (dist[ik] + dist[kj] < dist[ij]) {
2590 dist[ij] = dist[ik] + dist[kj];
2591 next[ij] = next[ik];
2592 }
2593 }
2594 }
2595 }
2596
2597 var getArgEle = function getArgEle(ele) {
2598 return (string(ele) ? cy.filter(ele) : ele)[0];
2599 };
2600
2601 var indexOfArgEle = function indexOfArgEle(ele) {
2602 return indexOf(getArgEle(ele));
2603 };
2604
2605 var res = {
2606 distance: function distance(from, to) {
2607 var i = indexOfArgEle(from);
2608 var j = indexOfArgEle(to);
2609 return dist[i * N + j];
2610 },
2611 path: function path(from, to) {
2612 var i = indexOfArgEle(from);
2613 var j = indexOfArgEle(to);
2614 var fromNode = atIndex(i);
2615
2616 if (i === j) {
2617 return fromNode.collection();
2618 }
2619
2620 if (next[i * N + j] == null) {
2621 return cy.collection();
2622 }
2623
2624 var path = cy.collection();
2625 var prev = i;
2626 var edge;
2627 path.merge(fromNode);
2628
2629 while (i !== j) {
2630 prev = i;
2631 i = next[i * N + j];
2632 edge = edgeNext[prev * N + i];
2633 path.merge(edge);
2634 path.merge(atIndex(i));
2635 }
2636
2637 return path;
2638 }
2639 };
2640 return res;
2641 } // floydWarshall
2642
2643 }; // elesfn
2644
2645 var bellmanFordDefaults = defaults({
2646 weight: function weight(edge) {
2647 return 1;
2648 },
2649 directed: false,
2650 root: null
2651 });
2652 var elesfn$5 = {
2653 // Implemented from pseudocode from wikipedia
2654 bellmanFord: function bellmanFord(options) {
2655 var _this = this;
2656
2657 var _bellmanFordDefaults = bellmanFordDefaults(options),
2658 weight = _bellmanFordDefaults.weight,
2659 directed = _bellmanFordDefaults.directed,
2660 root = _bellmanFordDefaults.root;
2661
2662 var weightFn = weight;
2663 var eles = this;
2664 var cy = this.cy();
2665
2666 var _this$byGroup = this.byGroup(),
2667 edges = _this$byGroup.edges,
2668 nodes = _this$byGroup.nodes;
2669
2670 var numNodes = nodes.length;
2671 var infoMap = new Map$1();
2672 var hasNegativeWeightCycle = false;
2673 var negativeWeightCycles = [];
2674 root = cy.collection(root)[0]; // in case selector passed
2675
2676 edges.unmergeBy(function (edge) {
2677 return edge.isLoop();
2678 });
2679 var numEdges = edges.length;
2680
2681 var getInfo = function getInfo(node) {
2682 var obj = infoMap.get(node.id());
2683
2684 if (!obj) {
2685 obj = {};
2686 infoMap.set(node.id(), obj);
2687 }
2688
2689 return obj;
2690 };
2691
2692 var getNodeFromTo = function getNodeFromTo(to) {
2693 return (string(to) ? cy.$(to) : to)[0];
2694 };
2695
2696 var distanceTo = function distanceTo(to) {
2697 return getInfo(getNodeFromTo(to)).dist;
2698 };
2699
2700 var pathTo = function pathTo(to) {
2701 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2702 var end = getNodeFromTo(to);
2703 var path = [];
2704 var node = end;
2705
2706 for (;;) {
2707 if (node == null) {
2708 return _this.spawn();
2709 }
2710
2711 var _getInfo = getInfo(node),
2712 edge = _getInfo.edge,
2713 pred = _getInfo.pred;
2714
2715 path.unshift(node[0]);
2716
2717 if (node.same(thisStart) && path.length > 0) {
2718 break;
2719 }
2720
2721 if (edge != null) {
2722 path.unshift(edge);
2723 }
2724
2725 node = pred;
2726 }
2727
2728 return eles.spawn(path);
2729 }; // Initializations { dist, pred, edge }
2730
2731
2732 for (var i = 0; i < numNodes; i++) {
2733 var node = nodes[i];
2734 var info = getInfo(node);
2735
2736 if (node.same(root)) {
2737 info.dist = 0;
2738 } else {
2739 info.dist = Infinity;
2740 }
2741
2742 info.pred = null;
2743 info.edge = null;
2744 } // Edges relaxation
2745
2746
2747 var replacedEdge = false;
2748
2749 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2750 var dist = info1.dist + weight;
2751
2752 if (dist < info2.dist && !edge.same(info1.edge)) {
2753 info2.dist = dist;
2754 info2.pred = node1;
2755 info2.edge = edge;
2756 replacedEdge = true;
2757 }
2758 };
2759
2760 for (var _i = 1; _i < numNodes; _i++) {
2761 replacedEdge = false;
2762
2763 for (var e = 0; e < numEdges; e++) {
2764 var edge = edges[e];
2765 var src = edge.source();
2766 var tgt = edge.target();
2767
2768 var _weight = weightFn(edge);
2769
2770 var srcInfo = getInfo(src);
2771 var tgtInfo = getInfo(tgt);
2772 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2773
2774 if (!directed) {
2775 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2776 }
2777 }
2778
2779 if (!replacedEdge) {
2780 break;
2781 }
2782 }
2783
2784 if (replacedEdge) {
2785 // Check for negative weight cycles
2786 for (var _e = 0; _e < numEdges; _e++) {
2787 var _edge = edges[_e];
2788
2789 var _src = _edge.source();
2790
2791 var _tgt = _edge.target();
2792
2793 var _weight2 = weightFn(_edge);
2794
2795 var srcDist = getInfo(_src).dist;
2796 var tgtDist = getInfo(_tgt).dist;
2797
2798 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2799 warn('Graph contains a negative weight cycle for Bellman-Ford');
2800 hasNegativeWeightCycle = true;
2801 break;
2802 }
2803 }
2804 }
2805
2806 return {
2807 distanceTo: distanceTo,
2808 pathTo: pathTo,
2809 hasNegativeWeightCycle: hasNegativeWeightCycle,
2810 negativeWeightCycles: negativeWeightCycles
2811 };
2812 } // bellmanFord
2813
2814 }; // elesfn
2815
2816 var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2817 // Updates the remaining edge lists
2818 // Receives as a paramater the edge which causes the collapse
2819
2820 var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2821 if (remainingEdges.length === 0) {
2822 error("Karger-Stein must be run on a connected (sub)graph");
2823 }
2824
2825 var edgeInfo = remainingEdges[edgeIndex];
2826 var sourceIn = edgeInfo[1];
2827 var targetIn = edgeInfo[2];
2828 var partition1 = nodeMap[sourceIn];
2829 var partition2 = nodeMap[targetIn];
2830 var newEdges = remainingEdges; // re-use array
2831 // Delete all edges between partition1 and partition2
2832
2833 for (var i = newEdges.length - 1; i >= 0; i--) {
2834 var edge = newEdges[i];
2835 var src = edge[1];
2836 var tgt = edge[2];
2837
2838 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2839 newEdges.splice(i, 1);
2840 }
2841 } // All edges pointing to partition2 should now point to partition1
2842
2843
2844 for (var _i = 0; _i < newEdges.length; _i++) {
2845 var _edge = newEdges[_i];
2846
2847 if (_edge[1] === partition2) {
2848 // Check source
2849 newEdges[_i] = _edge.slice(); // copy
2850
2851 newEdges[_i][1] = partition1;
2852 } else if (_edge[2] === partition2) {
2853 // Check target
2854 newEdges[_i] = _edge.slice(); // copy
2855
2856 newEdges[_i][2] = partition1;
2857 }
2858 } // Move all nodes from partition2 to partition1
2859
2860
2861 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2862 if (nodeMap[_i2] === partition2) {
2863 nodeMap[_i2] = partition1;
2864 }
2865 }
2866
2867 return newEdges;
2868 }; // Contracts a graph until we reach a certain number of meta nodes
2869
2870
2871 var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2872 while (size > sizeLimit) {
2873 // Choose an edge randomly
2874 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2875
2876 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2877 size--;
2878 }
2879
2880 return remainingEdges;
2881 };
2882
2883 var elesfn$6 = {
2884 // Computes the minimum cut of an undirected graph
2885 // Returns the correct answer with high probability
2886 kargerStein: function kargerStein() {
2887 var _this = this;
2888
2889 var _this$byGroup = this.byGroup(),
2890 nodes = _this$byGroup.nodes,
2891 edges = _this$byGroup.edges;
2892
2893 edges.unmergeBy(function (edge) {
2894 return edge.isLoop();
2895 });
2896 var numNodes = nodes.length;
2897 var numEdges = edges.length;
2898 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2899 var stopSize = Math.floor(numNodes / sqrt2);
2900
2901 if (numNodes < 2) {
2902 error('At least 2 nodes are required for Karger-Stein algorithm');
2903 return undefined;
2904 } // Now store edge destination as indexes
2905 // Format for each edge (edge index, source node index, target node index)
2906
2907
2908 var edgeIndexes = [];
2909
2910 for (var i = 0; i < numEdges; i++) {
2911 var e = edges[i];
2912 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2913 } // We will store the best cut found here
2914
2915
2916 var minCutSize = Infinity;
2917 var minCutEdgeIndexes = [];
2918 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2919
2920 var metaNodeMap = new Array(numNodes);
2921 var metaNodeMap2 = new Array(numNodes);
2922
2923 var copyNodesMap = function copyNodesMap(from, to) {
2924 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2925 to[_i3] = from[_i3];
2926 }
2927 }; // Main loop
2928
2929
2930 for (var iter = 0; iter <= numIter; iter++) {
2931 // Reset meta node partition
2932 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2933 metaNodeMap[_i4] = _i4;
2934 } // Contract until stop point (stopSize nodes)
2935
2936
2937 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2938 var edgesState2 = edgesState.slice(); // copy
2939 // Create a copy of the colapsed nodes state
2940
2941 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2942
2943 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2944 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2945
2946 if (res1.length <= res2.length && res1.length < minCutSize) {
2947 minCutSize = res1.length;
2948 minCutEdgeIndexes = res1;
2949 copyNodesMap(metaNodeMap, minCutNodeMap);
2950 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2951 minCutSize = res2.length;
2952 minCutEdgeIndexes = res2;
2953 copyNodesMap(metaNodeMap2, minCutNodeMap);
2954 }
2955 } // end of main loop
2956 // Construct result
2957
2958
2959 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2960 return edges[e[0]];
2961 }));
2962 var partition1 = this.spawn();
2963 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2964
2965 var witnessNodePartition = minCutNodeMap[0];
2966
2967 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2968 var partitionId = minCutNodeMap[_i5];
2969 var node = nodes[_i5];
2970
2971 if (partitionId === witnessNodePartition) {
2972 partition1.merge(node);
2973 } else {
2974 partition2.merge(node);
2975 }
2976 } // construct components corresponding to each disjoint subset of nodes
2977
2978
2979 var constructComponent = function constructComponent(subset) {
2980 var component = _this.spawn();
2981
2982 subset.forEach(function (node) {
2983 component.merge(node);
2984 node.connectedEdges().forEach(function (edge) {
2985 // ensure edge is within calling collection and edge is not in cut
2986 if (_this.contains(edge) && !cut.contains(edge)) {
2987 component.merge(edge);
2988 }
2989 });
2990 });
2991 return component;
2992 };
2993
2994 var components = [constructComponent(partition1), constructComponent(partition2)];
2995 var ret = {
2996 cut: cut,
2997 components: components,
2998 // n.b. partitions are included to be compatible with the old api spec
2999 // (could be removed in a future major version)
3000 partition1: partition1,
3001 partition2: partition2
3002 };
3003 return ret;
3004 }
3005 }; // elesfn
3006
3007 var copyPosition = function copyPosition(p) {
3008 return {
3009 x: p.x,
3010 y: p.y
3011 };
3012 };
3013 var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3014 return {
3015 x: p.x * zoom + pan.x,
3016 y: p.y * zoom + pan.y
3017 };
3018 };
3019 var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3020 return {
3021 x: (p.x - pan.x) / zoom,
3022 y: (p.y - pan.y) / zoom
3023 };
3024 };
3025 var array2point = function array2point(arr) {
3026 return {
3027 x: arr[0],
3028 y: arr[1]
3029 };
3030 };
3031 var min = function min(arr) {
3032 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3033 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3034 var min = Infinity;
3035
3036 for (var i = begin; i < end; i++) {
3037 var val = arr[i];
3038
3039 if (isFinite(val)) {
3040 min = Math.min(val, min);
3041 }
3042 }
3043
3044 return min;
3045 };
3046 var max = function max(arr) {
3047 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3048 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3049 var max = -Infinity;
3050
3051 for (var i = begin; i < end; i++) {
3052 var val = arr[i];
3053
3054 if (isFinite(val)) {
3055 max = Math.max(val, max);
3056 }
3057 }
3058
3059 return max;
3060 };
3061 var mean = function mean(arr) {
3062 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3063 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3064 var total = 0;
3065 var n = 0;
3066
3067 for (var i = begin; i < end; i++) {
3068 var val = arr[i];
3069
3070 if (isFinite(val)) {
3071 total += val;
3072 n++;
3073 }
3074 }
3075
3076 return total / n;
3077 };
3078 var median = function median(arr) {
3079 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3080 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3081 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3082 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3083 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3084
3085 if (copy) {
3086 arr = arr.slice(begin, end);
3087 } else {
3088 if (end < arr.length) {
3089 arr.splice(end, arr.length - end);
3090 }
3091
3092 if (begin > 0) {
3093 arr.splice(0, begin);
3094 }
3095 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3096
3097
3098 var off = 0; // offset from non-finite values
3099
3100 for (var i = arr.length - 1; i >= 0; i--) {
3101 var v = arr[i];
3102
3103 if (includeHoles) {
3104 if (!isFinite(v)) {
3105 arr[i] = -Infinity;
3106 off++;
3107 }
3108 } else {
3109 // just remove it if we don't want to consider holes
3110 arr.splice(i, 1);
3111 }
3112 }
3113
3114 if (sort) {
3115 arr.sort(function (a, b) {
3116 return a - b;
3117 }); // requires copy = true if you don't want to change the orig
3118 }
3119
3120 var len = arr.length;
3121 var mid = Math.floor(len / 2);
3122
3123 if (len % 2 !== 0) {
3124 return arr[mid + 1 + off];
3125 } else {
3126 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3127 }
3128 };
3129 var deg2rad = function deg2rad(deg) {
3130 return Math.PI * deg / 180;
3131 };
3132 var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3133 return Math.atan2(dispY, dispX) - Math.PI / 2;
3134 };
3135 var log2 = Math.log2 || function (n) {
3136 return Math.log(n) / Math.log(2);
3137 };
3138 var signum = function signum(x) {
3139 if (x > 0) {
3140 return 1;
3141 } else if (x < 0) {
3142 return -1;
3143 } else {
3144 return 0;
3145 }
3146 };
3147 var dist = function dist(p1, p2) {
3148 return Math.sqrt(sqdist(p1, p2));
3149 };
3150 var sqdist = function sqdist(p1, p2) {
3151 var dx = p2.x - p1.x;
3152 var dy = p2.y - p1.y;
3153 return dx * dx + dy * dy;
3154 };
3155 var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3156 var length = v.length; // First, get sum of all elements
3157
3158 var total = 0;
3159
3160 for (var i = 0; i < length; i++) {
3161 total += v[i];
3162 } // Now, divide each by the sum of all elements
3163
3164
3165 for (var _i = 0; _i < length; _i++) {
3166 v[_i] = v[_i] / total;
3167 }
3168
3169 return v;
3170 };
3171
3172 var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3173 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3174 };
3175 var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3176 return {
3177 x: qbezierAt(p0.x, p1.x, p2.x, t),
3178 y: qbezierAt(p0.y, p1.y, p2.y, t)
3179 };
3180 };
3181 var lineAt = function lineAt(p0, p1, t, d) {
3182 var vec = {
3183 x: p1.x - p0.x,
3184 y: p1.y - p0.y
3185 };
3186 var vecDist = dist(p0, p1);
3187 var normVec = {
3188 x: vec.x / vecDist,
3189 y: vec.y / vecDist
3190 };
3191 t = t == null ? 0 : t;
3192 d = d != null ? d : t * vecDist;
3193 return {
3194 x: p0.x + normVec.x * d,
3195 y: p0.y + normVec.y * d
3196 };
3197 };
3198 var bound = function bound(min, val, max) {
3199 return Math.max(min, Math.min(max, val));
3200 }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3201
3202 var makeBoundingBox = function makeBoundingBox(bb) {
3203 if (bb == null) {
3204 return {
3205 x1: Infinity,
3206 y1: Infinity,
3207 x2: -Infinity,
3208 y2: -Infinity,
3209 w: 0,
3210 h: 0
3211 };
3212 } else if (bb.x1 != null && bb.y1 != null) {
3213 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3214 return {
3215 x1: bb.x1,
3216 y1: bb.y1,
3217 x2: bb.x2,
3218 y2: bb.y2,
3219 w: bb.x2 - bb.x1,
3220 h: bb.y2 - bb.y1
3221 };
3222 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3223 return {
3224 x1: bb.x1,
3225 y1: bb.y1,
3226 x2: bb.x1 + bb.w,
3227 y2: bb.y1 + bb.h,
3228 w: bb.w,
3229 h: bb.h
3230 };
3231 }
3232 }
3233 };
3234 var copyBoundingBox = function copyBoundingBox(bb) {
3235 return {
3236 x1: bb.x1,
3237 x2: bb.x2,
3238 w: bb.w,
3239 y1: bb.y1,
3240 y2: bb.y2,
3241 h: bb.h
3242 };
3243 };
3244 var clearBoundingBox = function clearBoundingBox(bb) {
3245 bb.x1 = Infinity;
3246 bb.y1 = Infinity;
3247 bb.x2 = -Infinity;
3248 bb.y2 = -Infinity;
3249 bb.w = 0;
3250 bb.h = 0;
3251 };
3252 var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3253 // update bb1 with bb2 bounds
3254 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3255 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3256 bb1.w = bb1.x2 - bb1.x1;
3257 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3258 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3259 bb1.h = bb1.y2 - bb1.y1;
3260 };
3261 var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3262 bb.x1 = Math.min(bb.x1, x);
3263 bb.x2 = Math.max(bb.x2, x);
3264 bb.w = bb.x2 - bb.x1;
3265 bb.y1 = Math.min(bb.y1, y);
3266 bb.y2 = Math.max(bb.y2, y);
3267 bb.h = bb.y2 - bb.y1;
3268 };
3269 var expandBoundingBox = function expandBoundingBox(bb) {
3270 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3271 bb.x1 -= padding;
3272 bb.x2 += padding;
3273 bb.y1 -= padding;
3274 bb.y2 += padding;
3275 bb.w = bb.x2 - bb.x1;
3276 bb.h = bb.y2 - bb.y1;
3277 return bb;
3278 };
3279 var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3280 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3281 var top, right, bottom, left;
3282
3283 if (padding.length === 1) {
3284 top = right = bottom = left = padding[0];
3285 } else if (padding.length === 2) {
3286 top = bottom = padding[0];
3287 left = right = padding[1];
3288 } else if (padding.length === 4) {
3289 var _padding = _slicedToArray(padding, 4);
3290
3291 top = _padding[0];
3292 right = _padding[1];
3293 bottom = _padding[2];
3294 left = _padding[3];
3295 }
3296
3297 bb.x1 -= left;
3298 bb.x2 += right;
3299 bb.y1 -= top;
3300 bb.y2 += bottom;
3301 bb.w = bb.x2 - bb.x1;
3302 bb.h = bb.y2 - bb.y1;
3303 return bb;
3304 };
3305
3306 var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3307 bb1.x1 = bb2.x1;
3308 bb1.y1 = bb2.y1;
3309 bb1.x2 = bb2.x2;
3310 bb1.y2 = bb2.y2;
3311 bb1.w = bb1.x2 - bb1.x1;
3312 bb1.h = bb1.y2 - bb1.y1;
3313 };
3314 var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
3315 bb.x1 += delta.x;
3316 bb.x2 += delta.x;
3317 bb.y1 += delta.y;
3318 bb.y2 += delta.y;
3319 };
3320 var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3321 // case: one bb to right of other
3322 if (bb1.x1 > bb2.x2) {
3323 return false;
3324 }
3325
3326 if (bb2.x1 > bb1.x2) {
3327 return false;
3328 } // case: one bb to left of other
3329
3330
3331 if (bb1.x2 < bb2.x1) {
3332 return false;
3333 }
3334
3335 if (bb2.x2 < bb1.x1) {
3336 return false;
3337 } // case: one bb above other
3338
3339
3340 if (bb1.y2 < bb2.y1) {
3341 return false;
3342 }
3343
3344 if (bb2.y2 < bb1.y1) {
3345 return false;
3346 } // case: one bb below other
3347
3348
3349 if (bb1.y1 > bb2.y2) {
3350 return false;
3351 }
3352
3353 if (bb2.y1 > bb1.y2) {
3354 return false;
3355 } // otherwise, must have some overlap
3356
3357
3358 return true;
3359 };
3360 var inBoundingBox = function inBoundingBox(bb, x, y) {
3361 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3362 };
3363 var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3364 return inBoundingBox(bb, pt.x, pt.y);
3365 };
3366 var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3367 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3368 };
3369 var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3370 var cornerRadius = getRoundRectangleRadius(width, height);
3371 var halfWidth = width / 2;
3372 var halfHeight = height / 2; // Check intersections with straight line segments
3373
3374 var straightLineIntersections; // Top segment, left to right
3375
3376 {
3377 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3378 var topStartY = nodeY - halfHeight - padding;
3379 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3380 var topEndY = topStartY;
3381 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3382
3383 if (straightLineIntersections.length > 0) {
3384 return straightLineIntersections;
3385 }
3386 } // Right segment, top to bottom
3387
3388 {
3389 var rightStartX = nodeX + halfWidth + padding;
3390 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3391 var rightEndX = rightStartX;
3392 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3393 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3394
3395 if (straightLineIntersections.length > 0) {
3396 return straightLineIntersections;
3397 }
3398 } // Bottom segment, left to right
3399
3400 {
3401 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3402 var bottomStartY = nodeY + halfHeight + padding;
3403 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3404 var bottomEndY = bottomStartY;
3405 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3406
3407 if (straightLineIntersections.length > 0) {
3408 return straightLineIntersections;
3409 }
3410 } // Left segment, top to bottom
3411
3412 {
3413 var leftStartX = nodeX - halfWidth - padding;
3414 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3415 var leftEndX = leftStartX;
3416 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3417 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3418
3419 if (straightLineIntersections.length > 0) {
3420 return straightLineIntersections;
3421 }
3422 } // Check intersections with arc segments
3423
3424 var arcIntersections; // Top Left
3425
3426 {
3427 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3428 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3429 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3430
3431 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3432 return [arcIntersections[0], arcIntersections[1]];
3433 }
3434 } // Top Right
3435
3436 {
3437 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3438 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3439 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3440
3441 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3442 return [arcIntersections[0], arcIntersections[1]];
3443 }
3444 } // Bottom Right
3445
3446 {
3447 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3448 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3449 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3450
3451 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3452 return [arcIntersections[0], arcIntersections[1]];
3453 }
3454 } // Bottom Left
3455
3456 {
3457 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3458 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3459 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3460
3461 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3462 return [arcIntersections[0], arcIntersections[1]];
3463 }
3464 }
3465 return []; // if nothing
3466 };
3467 var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3468 var t = tolerance;
3469 var x1 = Math.min(lx1, lx2);
3470 var x2 = Math.max(lx1, lx2);
3471 var y1 = Math.min(ly1, ly2);
3472 var y2 = Math.max(ly1, ly2);
3473 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3474 };
3475 var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3476 var bb = {
3477 x1: Math.min(x1, x3, x2) - tolerance,
3478 x2: Math.max(x1, x3, x2) + tolerance,
3479 y1: Math.min(y1, y3, y2) - tolerance,
3480 y2: Math.max(y1, y3, y2) + tolerance
3481 }; // if outside the rough bounding box for the bezier, then it can't be a hit
3482
3483 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3484 // console.log('bezier out of rough bb')
3485 return false;
3486 } else {
3487 // console.log('do more expensive check');
3488 return true;
3489 }
3490 };
3491 var solveQuadratic = function solveQuadratic(a, b, c, val) {
3492 c -= val;
3493 var r = b * b - 4 * a * c;
3494
3495 if (r < 0) {
3496 return [];
3497 }
3498
3499 var sqrtR = Math.sqrt(r);
3500 var denom = 2 * a;
3501 var root1 = (-b + sqrtR) / denom;
3502 var root2 = (-b - sqrtR) / denom;
3503 return [root1, root2];
3504 };
3505 var solveCubic = function solveCubic(a, b, c, d, result) {
3506 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3507 // r is the real component, i is the imaginary component
3508 // An implementation of the Cardano method from the year 1545
3509 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3510 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
3511
3512 if (a === 0) {
3513 a = epsilon;
3514 }
3515
3516 b /= a;
3517 c /= a;
3518 d /= a;
3519 var discriminant, q, r, dum1, s, t, term1, r13;
3520 q = (3.0 * c - b * b) / 9.0;
3521 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3522 r /= 54.0;
3523 discriminant = q * q * q + r * r;
3524 result[1] = 0;
3525 term1 = b / 3.0;
3526
3527 if (discriminant > 0) {
3528 s = r + Math.sqrt(discriminant);
3529 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3530 t = r - Math.sqrt(discriminant);
3531 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3532 result[0] = -term1 + s + t;
3533 term1 += (s + t) / 2.0;
3534 result[4] = result[2] = -term1;
3535 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3536 result[3] = term1;
3537 result[5] = -term1;
3538 return;
3539 }
3540
3541 result[5] = result[3] = 0;
3542
3543 if (discriminant === 0) {
3544 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3545 result[0] = -term1 + 2.0 * r13;
3546 result[4] = result[2] = -(r13 + term1);
3547 return;
3548 }
3549
3550 q = -q;
3551 dum1 = q * q * q;
3552 dum1 = Math.acos(r / Math.sqrt(dum1));
3553 r13 = 2.0 * Math.sqrt(q);
3554 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3555 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3556 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3557 return;
3558 };
3559 var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3560 // Find minimum distance by using the minimum of the distance
3561 // function between the given point and the curve
3562 // This gives the coefficients of the resulting cubic equation
3563 // whose roots tell us where a possible minimum is
3564 // (Coefficients are divided by 4)
3565 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;
3566 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;
3567 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;
3568 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);
3569
3570 var roots = []; // Use the cubic solving algorithm
3571
3572 solveCubic(a, b, c, d, roots);
3573 var zeroThreshold = 0.0000001;
3574 var params = [];
3575
3576 for (var index = 0; index < 6; index += 2) {
3577 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3578 params.push(roots[index]);
3579 }
3580 }
3581
3582 params.push(1.0);
3583 params.push(0.0);
3584 var minDistanceSquared = -1;
3585 var curX, curY, distSquared;
3586
3587 for (var i = 0; i < params.length; i++) {
3588 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3589 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3590 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3591
3592 if (minDistanceSquared >= 0) {
3593 if (distSquared < minDistanceSquared) {
3594 minDistanceSquared = distSquared;
3595 }
3596 } else {
3597 minDistanceSquared = distSquared;
3598 }
3599 }
3600
3601 return minDistanceSquared;
3602 };
3603 var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3604 var offset = [x - x1, y - y1];
3605 var line = [x2 - x1, y2 - y1];
3606 var lineSq = line[0] * line[0] + line[1] * line[1];
3607 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3608 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3609 var adjSq = dotProduct * dotProduct / lineSq;
3610
3611 if (dotProduct < 0) {
3612 return hypSq;
3613 }
3614
3615 if (adjSq > lineSq) {
3616 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3617 }
3618
3619 return hypSq - adjSq;
3620 };
3621 var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3622 var x1, y1, x2, y2;
3623 var y3; // Intersect with vertical line through (x, y)
3624
3625 var up = 0; // let down = 0;
3626
3627 for (var i = 0; i < points.length / 2; i++) {
3628 x1 = points[i * 2];
3629 y1 = points[i * 2 + 1];
3630
3631 if (i + 1 < points.length / 2) {
3632 x2 = points[(i + 1) * 2];
3633 y2 = points[(i + 1) * 2 + 1];
3634 } else {
3635 x2 = points[(i + 1 - points.length / 2) * 2];
3636 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3637 }
3638
3639 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3640 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3641
3642 if (y3 > y) {
3643 up++;
3644 } // if( y3 < y ){
3645 // down++;
3646 // }
3647
3648 } else {
3649 continue;
3650 }
3651 }
3652
3653 if (up % 2 === 0) {
3654 return false;
3655 } else {
3656 return true;
3657 }
3658 };
3659 var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3660 var transformedPoints = new Array(basePoints.length); // Gives negative angle
3661
3662 var angle;
3663
3664 if (direction[0] != null) {
3665 angle = Math.atan(direction[1] / direction[0]);
3666
3667 if (direction[0] < 0) {
3668 angle = angle + Math.PI / 2;
3669 } else {
3670 angle = -angle - Math.PI / 2;
3671 }
3672 } else {
3673 angle = direction;
3674 }
3675
3676 var cos = Math.cos(-angle);
3677 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
3678
3679 for (var i = 0; i < transformedPoints.length / 2; i++) {
3680 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3681 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3682 transformedPoints[i * 2] += centerX;
3683 transformedPoints[i * 2 + 1] += centerY;
3684 }
3685
3686 var points;
3687
3688 if (padding > 0) {
3689 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3690 points = joinLines(expandedLineSet);
3691 } else {
3692 points = transformedPoints;
3693 }
3694
3695 return pointInsidePolygonPoints(x, y, points);
3696 };
3697 var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3698 var cutPolygonPoints = new Array(basePoints.length);
3699 var halfW = width / 2;
3700 var halfH = height / 2;
3701 var cornerRadius = getRoundPolygonRadius(width, height);
3702 var squaredCornerRadius = cornerRadius * cornerRadius;
3703
3704 for (var i = 0; i < basePoints.length / 4; i++) {
3705 var sourceUv = void 0,
3706 destUv = void 0;
3707
3708 if (i === 0) {
3709 sourceUv = basePoints.length - 2;
3710 } else {
3711 sourceUv = i * 4 - 2;
3712 }
3713
3714 destUv = i * 4 + 2;
3715 var px = centerX + halfW * basePoints[i * 4];
3716 var py = centerY + halfH * basePoints[i * 4 + 1];
3717 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3718 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3719 var cp0x = px - offset * basePoints[sourceUv];
3720 var cp0y = py - offset * basePoints[sourceUv + 1];
3721 var cp1x = px + offset * basePoints[destUv];
3722 var cp1y = py + offset * basePoints[destUv + 1];
3723 cutPolygonPoints[i * 4] = cp0x;
3724 cutPolygonPoints[i * 4 + 1] = cp0y;
3725 cutPolygonPoints[i * 4 + 2] = cp1x;
3726 cutPolygonPoints[i * 4 + 3] = cp1y;
3727 var orthx = basePoints[sourceUv + 1];
3728 var orthy = -basePoints[sourceUv];
3729 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3730
3731 if (cosAlpha < 0) {
3732 orthx *= -1;
3733 orthy *= -1;
3734 }
3735
3736 var cx = cp0x + orthx * cornerRadius;
3737 var cy = cp0y + orthy * cornerRadius;
3738 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3739
3740 if (squaredDistance <= squaredCornerRadius) {
3741 return true;
3742 }
3743 }
3744
3745 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3746 };
3747 var joinLines = function joinLines(lineSet) {
3748 var vertices = new Array(lineSet.length / 2);
3749 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3750 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3751
3752 for (var i = 0; i < lineSet.length / 4; i++) {
3753 currentLineStartX = lineSet[i * 4];
3754 currentLineStartY = lineSet[i * 4 + 1];
3755 currentLineEndX = lineSet[i * 4 + 2];
3756 currentLineEndY = lineSet[i * 4 + 3];
3757
3758 if (i < lineSet.length / 4 - 1) {
3759 nextLineStartX = lineSet[(i + 1) * 4];
3760 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3761 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3762 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3763 } else {
3764 nextLineStartX = lineSet[0];
3765 nextLineStartY = lineSet[1];
3766 nextLineEndX = lineSet[2];
3767 nextLineEndY = lineSet[3];
3768 }
3769
3770 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3771 vertices[i * 2] = intersection[0];
3772 vertices[i * 2 + 1] = intersection[1];
3773 }
3774
3775 return vertices;
3776 };
3777 var expandPolygon = function expandPolygon(points, pad) {
3778 var expandedLineSet = new Array(points.length * 2);
3779 var currentPointX, currentPointY, nextPointX, nextPointY;
3780
3781 for (var i = 0; i < points.length / 2; i++) {
3782 currentPointX = points[i * 2];
3783 currentPointY = points[i * 2 + 1];
3784
3785 if (i < points.length / 2 - 1) {
3786 nextPointX = points[(i + 1) * 2];
3787 nextPointY = points[(i + 1) * 2 + 1];
3788 } else {
3789 nextPointX = points[0];
3790 nextPointY = points[1];
3791 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3792 // Assume CCW polygon winding
3793
3794
3795 var offsetX = nextPointY - currentPointY;
3796 var offsetY = -(nextPointX - currentPointX); // Normalize
3797
3798 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3799 var normalizedOffsetX = offsetX / offsetLength;
3800 var normalizedOffsetY = offsetY / offsetLength;
3801 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3802 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3803 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3804 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3805 }
3806
3807 return expandedLineSet;
3808 };
3809 var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3810 var dispX = centerX - x;
3811 var dispY = centerY - y;
3812 dispX /= ellipseWradius;
3813 dispY /= ellipseHradius;
3814 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3815 var newLength = len - 1;
3816
3817 if (newLength < 0) {
3818 return [];
3819 }
3820
3821 var lenProportion = newLength / len;
3822 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3823 };
3824 var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3825 x -= centerX;
3826 y -= centerY;
3827 x /= width / 2 + padding;
3828 y /= height / 2 + padding;
3829 return x * x + y * y <= 1;
3830 }; // Returns intersections of increasing distance from line's start point
3831
3832 var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3833 // Calculate d, direction vector of line
3834 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3835
3836 var f = [x1 - centerX, y1 - centerY];
3837 var a = d[0] * d[0] + d[1] * d[1];
3838 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3839 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3840 var discriminant = b * b - 4 * a * c;
3841
3842 if (discriminant < 0) {
3843 return [];
3844 }
3845
3846 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3847 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3848 var tMin = Math.min(t1, t2);
3849 var tMax = Math.max(t1, t2);
3850 var inRangeParams = [];
3851
3852 if (tMin >= 0 && tMin <= 1) {
3853 inRangeParams.push(tMin);
3854 }
3855
3856 if (tMax >= 0 && tMax <= 1) {
3857 inRangeParams.push(tMax);
3858 }
3859
3860 if (inRangeParams.length === 0) {
3861 return [];
3862 }
3863
3864 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3865 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3866
3867 if (inRangeParams.length > 1) {
3868 if (inRangeParams[0] == inRangeParams[1]) {
3869 return [nearIntersectionX, nearIntersectionY];
3870 } else {
3871 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3872 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3873 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3874 }
3875 } else {
3876 return [nearIntersectionX, nearIntersectionY];
3877 }
3878 };
3879 var midOfThree = function midOfThree(a, b, c) {
3880 if (b <= a && a <= c || c <= a && a <= b) {
3881 return a;
3882 } else if (a <= b && b <= c || c <= b && b <= a) {
3883 return b;
3884 } else {
3885 return c;
3886 }
3887 }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3888
3889 var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3890 var dx13 = x1 - x3;
3891 var dx21 = x2 - x1;
3892 var dx43 = x4 - x3;
3893 var dy13 = y1 - y3;
3894 var dy21 = y2 - y1;
3895 var dy43 = y4 - y3;
3896 var ua_t = dx43 * dy13 - dy43 * dx13;
3897 var ub_t = dx21 * dy13 - dy21 * dx13;
3898 var u_b = dy43 * dx21 - dx43 * dy21;
3899
3900 if (u_b !== 0) {
3901 var ua = ua_t / u_b;
3902 var ub = ub_t / u_b;
3903 var flptThreshold = 0.001;
3904
3905 var _min = 0 - flptThreshold;
3906
3907 var _max = 1 + flptThreshold;
3908
3909 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3910 return [x1 + ua * dx21, y1 + ua * dy21];
3911 } else {
3912 if (!infiniteLines) {
3913 return [];
3914 } else {
3915 return [x1 + ua * dx21, y1 + ua * dy21];
3916 }
3917 }
3918 } else {
3919 if (ua_t === 0 || ub_t === 0) {
3920 // Parallel, coincident lines. Check if overlap
3921 // Check endpoint of second line
3922 if (midOfThree(x1, x2, x4) === x4) {
3923 return [x4, y4];
3924 } // Check start point of second line
3925
3926
3927 if (midOfThree(x1, x2, x3) === x3) {
3928 return [x3, y3];
3929 } // Endpoint of first line
3930
3931
3932 if (midOfThree(x3, x4, x2) === x2) {
3933 return [x2, y2];
3934 }
3935
3936 return [];
3937 } else {
3938 // Parallel, non-coincident
3939 return [];
3940 }
3941 }
3942 }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3943 // intersect a node polygon (pts transformed)
3944 //
3945 // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3946 // intersect the points (no transform)
3947
3948 var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3949 var intersections = [];
3950 var intersection;
3951 var transformedPoints = new Array(basePoints.length);
3952 var doTransform = true;
3953
3954 if (width == null) {
3955 doTransform = false;
3956 }
3957
3958 var points;
3959
3960 if (doTransform) {
3961 for (var i = 0; i < transformedPoints.length / 2; i++) {
3962 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3963 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3964 }
3965
3966 if (padding > 0) {
3967 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3968 points = joinLines(expandedLineSet);
3969 } else {
3970 points = transformedPoints;
3971 }
3972 } else {
3973 points = basePoints;
3974 }
3975
3976 var currentX, currentY, nextX, nextY;
3977
3978 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3979 currentX = points[_i2 * 2];
3980 currentY = points[_i2 * 2 + 1];
3981
3982 if (_i2 < points.length / 2 - 1) {
3983 nextX = points[(_i2 + 1) * 2];
3984 nextY = points[(_i2 + 1) * 2 + 1];
3985 } else {
3986 nextX = points[0];
3987 nextY = points[1];
3988 }
3989
3990 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3991
3992 if (intersection.length !== 0) {
3993 intersections.push(intersection[0], intersection[1]);
3994 }
3995 }
3996
3997 return intersections;
3998 };
3999 var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
4000 var intersections = [];
4001 var intersection;
4002 var lines = new Array(basePoints.length);
4003 var halfW = width / 2;
4004 var halfH = height / 2;
4005 var cornerRadius = getRoundPolygonRadius(width, height);
4006
4007 for (var i = 0; i < basePoints.length / 4; i++) {
4008 var sourceUv = void 0,
4009 destUv = void 0;
4010
4011 if (i === 0) {
4012 sourceUv = basePoints.length - 2;
4013 } else {
4014 sourceUv = i * 4 - 2;
4015 }
4016
4017 destUv = i * 4 + 2;
4018 var px = centerX + halfW * basePoints[i * 4];
4019 var py = centerY + halfH * basePoints[i * 4 + 1];
4020 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
4021 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
4022 var cp0x = px - offset * basePoints[sourceUv];
4023 var cp0y = py - offset * basePoints[sourceUv + 1];
4024 var cp1x = px + offset * basePoints[destUv];
4025 var cp1y = py + offset * basePoints[destUv + 1];
4026
4027 if (i === 0) {
4028 lines[basePoints.length - 2] = cp0x;
4029 lines[basePoints.length - 1] = cp0y;
4030 } else {
4031 lines[i * 4 - 2] = cp0x;
4032 lines[i * 4 - 1] = cp0y;
4033 }
4034
4035 lines[i * 4] = cp1x;
4036 lines[i * 4 + 1] = cp1y;
4037 var orthx = basePoints[sourceUv + 1];
4038 var orthy = -basePoints[sourceUv];
4039 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
4040
4041 if (cosAlpha < 0) {
4042 orthx *= -1;
4043 orthy *= -1;
4044 }
4045
4046 var cx = cp0x + orthx * cornerRadius;
4047 var cy = cp0y + orthy * cornerRadius;
4048 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
4049
4050 if (intersection.length !== 0) {
4051 intersections.push(intersection[0], intersection[1]);
4052 }
4053 }
4054
4055 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
4056 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
4057
4058 if (intersection.length !== 0) {
4059 intersections.push(intersection[0], intersection[1]);
4060 }
4061 }
4062
4063 if (intersections.length > 2) {
4064 var lowestIntersection = [intersections[0], intersections[1]];
4065 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
4066
4067 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
4068 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
4069
4070 if (squaredDistance <= lowestSquaredDistance) {
4071 lowestIntersection[0] = intersections[_i4 * 2];
4072 lowestIntersection[1] = intersections[_i4 * 2 + 1];
4073 lowestSquaredDistance = squaredDistance;
4074 }
4075 }
4076
4077 return lowestIntersection;
4078 }
4079
4080 return intersections;
4081 };
4082 var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
4083 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
4084 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
4085 var lenRatio = (length - amount) / length;
4086
4087 if (lenRatio < 0) {
4088 lenRatio = 0.00001;
4089 }
4090
4091 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
4092 };
4093 var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
4094 var points = generateUnitNgonPoints(sides, rotationRadians);
4095 points = fitPolygonToSquare(points);
4096 return points;
4097 };
4098 var fitPolygonToSquare = function fitPolygonToSquare(points) {
4099 var x, y;
4100 var sides = points.length / 2;
4101 var minX = Infinity,
4102 minY = Infinity,
4103 maxX = -Infinity,
4104 maxY = -Infinity;
4105
4106 for (var i = 0; i < sides; i++) {
4107 x = points[2 * i];
4108 y = points[2 * i + 1];
4109 minX = Math.min(minX, x);
4110 maxX = Math.max(maxX, x);
4111 minY = Math.min(minY, y);
4112 maxY = Math.max(maxY, y);
4113 } // stretch factors
4114
4115
4116 var sx = 2 / (maxX - minX);
4117 var sy = 2 / (maxY - minY);
4118
4119 for (var _i5 = 0; _i5 < sides; _i5++) {
4120 x = points[2 * _i5] = points[2 * _i5] * sx;
4121 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
4122 minX = Math.min(minX, x);
4123 maxX = Math.max(maxX, x);
4124 minY = Math.min(minY, y);
4125 maxY = Math.max(maxY, y);
4126 }
4127
4128 if (minY < -1) {
4129 for (var _i6 = 0; _i6 < sides; _i6++) {
4130 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
4131 }
4132 }
4133
4134 return points;
4135 };
4136 var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4137 var increment = 1.0 / sides * 2 * Math.PI;
4138 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4139 startAngle += rotationRadians;
4140 var points = new Array(sides * 2);
4141 var currentAngle;
4142
4143 for (var i = 0; i < sides; i++) {
4144 currentAngle = i * increment + startAngle;
4145 points[2 * i] = Math.cos(currentAngle); // x
4146
4147 points[2 * i + 1] = Math.sin(-currentAngle); // y
4148 }
4149
4150 return points;
4151 }; // Set the default radius, unless half of width or height is smaller than default
4152
4153 var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4154 return Math.min(width / 4, height / 4, 8);
4155 }; // Set the default radius
4156
4157 var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4158 return Math.min(width / 10, height / 10, 8);
4159 };
4160 var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4161 return 8;
4162 };
4163 var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4164 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4165 }; // get curve width, height, and control point position offsets as a percentage of node height / width
4166
4167 var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4168 return {
4169 heightOffset: Math.min(15, 0.05 * height),
4170 widthOffset: Math.min(100, 0.25 * width),
4171 ctrlPtOffsetPct: 0.05
4172 };
4173 };
4174
4175 var pageRankDefaults = defaults({
4176 dampingFactor: 0.8,
4177 precision: 0.000001,
4178 iterations: 200,
4179 weight: function weight(edge) {
4180 return 1;
4181 }
4182 });
4183 var elesfn$7 = {
4184 pageRank: function pageRank(options) {
4185 var _pageRankDefaults = pageRankDefaults(options),
4186 dampingFactor = _pageRankDefaults.dampingFactor,
4187 precision = _pageRankDefaults.precision,
4188 iterations = _pageRankDefaults.iterations,
4189 weight = _pageRankDefaults.weight;
4190
4191 var cy = this._private.cy;
4192
4193 var _this$byGroup = this.byGroup(),
4194 nodes = _this$byGroup.nodes,
4195 edges = _this$byGroup.edges;
4196
4197 var numNodes = nodes.length;
4198 var numNodesSqd = numNodes * numNodes;
4199 var numEdges = edges.length; // Construct transposed adjacency matrix
4200 // First lets have a zeroed matrix of the right size
4201 // We'll also keep track of the sum of each column
4202
4203 var matrix = new Array(numNodesSqd);
4204 var columnSum = new Array(numNodes);
4205 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
4206
4207 for (var i = 0; i < numNodes; i++) {
4208 for (var j = 0; j < numNodes; j++) {
4209 var n = i * numNodes + j;
4210 matrix[n] = 0;
4211 }
4212
4213 columnSum[i] = 0;
4214 } // Now, process edges
4215
4216
4217 for (var _i = 0; _i < numEdges; _i++) {
4218 var edge = edges[_i];
4219 var srcId = edge.data('source');
4220 var tgtId = edge.data('target'); // Don't include loops in the matrix
4221
4222 if (srcId === tgtId) {
4223 continue;
4224 }
4225
4226 var s = nodes.indexOfId(srcId);
4227 var t = nodes.indexOfId(tgtId);
4228 var w = weight(edge);
4229
4230 var _n = t * numNodes + s; // Update matrix
4231
4232
4233 matrix[_n] += w; // Update column sum
4234
4235 columnSum[s] += w;
4236 } // Add additional probability based on damping factor
4237 // Also, take into account columns that have sum = 0
4238
4239
4240 var p = 1.0 / numNodes + additionalProb; // Shorthand
4241 // Traverse matrix, column by column
4242
4243 for (var _j = 0; _j < numNodes; _j++) {
4244 if (columnSum[_j] === 0) {
4245 // No 'links' out from node jth, assume equal probability for each possible node
4246 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4247 var _n2 = _i2 * numNodes + _j;
4248
4249 matrix[_n2] = p;
4250 }
4251 } else {
4252 // Node jth has outgoing link, compute normalized probabilities
4253 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4254 var _n3 = _i3 * numNodes + _j;
4255
4256 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4257 }
4258 }
4259 } // Compute dominant eigenvector using power method
4260
4261
4262 var eigenvector = new Array(numNodes);
4263 var temp = new Array(numNodes);
4264 var previous; // Start with a vector of all 1's
4265 // Also, initialize a null vector which will be used as shorthand
4266
4267 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4268 eigenvector[_i4] = 1;
4269 }
4270
4271 for (var iter = 0; iter < iterations; iter++) {
4272 // Temp array with all 0's
4273 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4274 temp[_i5] = 0;
4275 } // Multiply matrix with previous result
4276
4277
4278 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4279 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4280 var _n4 = _i6 * numNodes + _j2;
4281
4282 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4283 }
4284 }
4285
4286 inPlaceSumNormalize(temp);
4287 previous = eigenvector;
4288 eigenvector = temp;
4289 temp = previous;
4290 var diff = 0; // Compute difference (squared module) of both vectors
4291
4292 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4293 var delta = previous[_i7] - eigenvector[_i7];
4294 diff += delta * delta;
4295 } // If difference is less than the desired threshold, stop iterating
4296
4297
4298 if (diff < precision) {
4299 break;
4300 }
4301 } // Construct result
4302
4303
4304 var res = {
4305 rank: function rank(node) {
4306 node = cy.collection(node)[0];
4307 return eigenvector[nodes.indexOf(node)];
4308 }
4309 };
4310 return res;
4311 } // pageRank
4312
4313 }; // elesfn
4314
4315 var defaults$1 = defaults({
4316 root: null,
4317 weight: function weight(edge) {
4318 return 1;
4319 },
4320 directed: false,
4321 alpha: 0
4322 });
4323 var elesfn$8 = {
4324 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4325 options = defaults$1(options);
4326 var cy = this.cy();
4327 var nodes = this.nodes();
4328 var numNodes = nodes.length;
4329
4330 if (!options.directed) {
4331 var degrees = {};
4332 var maxDegree = 0;
4333
4334 for (var i = 0; i < numNodes; i++) {
4335 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
4336
4337 options.root = node;
4338 var currDegree = this.degreeCentrality(options);
4339
4340 if (maxDegree < currDegree.degree) {
4341 maxDegree = currDegree.degree;
4342 }
4343
4344 degrees[node.id()] = currDegree.degree;
4345 }
4346
4347 return {
4348 degree: function degree(node) {
4349 if (maxDegree === 0) {
4350 return 0;
4351 }
4352
4353 if (string(node)) {
4354 // from is a selector string
4355 node = cy.filter(node);
4356 }
4357
4358 return degrees[node.id()] / maxDegree;
4359 }
4360 };
4361 } else {
4362 var indegrees = {};
4363 var outdegrees = {};
4364 var maxIndegree = 0;
4365 var maxOutdegree = 0;
4366
4367 for (var _i = 0; _i < numNodes; _i++) {
4368 var _node = nodes[_i];
4369
4370 var id = _node.id(); // add current node to the current options object and call degreeCentrality
4371
4372
4373 options.root = _node;
4374
4375 var _currDegree = this.degreeCentrality(options);
4376
4377 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4378 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4379 indegrees[id] = _currDegree.indegree;
4380 outdegrees[id] = _currDegree.outdegree;
4381 }
4382
4383 return {
4384 indegree: function indegree(node) {
4385 if (maxIndegree == 0) {
4386 return 0;
4387 }
4388
4389 if (string(node)) {
4390 // from is a selector string
4391 node = cy.filter(node);
4392 }
4393
4394 return indegrees[node.id()] / maxIndegree;
4395 },
4396 outdegree: function outdegree(node) {
4397 if (maxOutdegree === 0) {
4398 return 0;
4399 }
4400
4401 if (string(node)) {
4402 // from is a selector string
4403 node = cy.filter(node);
4404 }
4405
4406 return outdegrees[node.id()] / maxOutdegree;
4407 }
4408 };
4409 }
4410 },
4411 // degreeCentralityNormalized
4412 // Implemented from the algorithm in Opsahl's paper
4413 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4414 // check the heading 2 "Degree"
4415 degreeCentrality: function degreeCentrality(options) {
4416 options = defaults$1(options);
4417 var cy = this.cy();
4418 var callingEles = this;
4419 var _options = options,
4420 root = _options.root,
4421 weight = _options.weight,
4422 directed = _options.directed,
4423 alpha = _options.alpha;
4424 root = cy.collection(root)[0];
4425
4426 if (!directed) {
4427 var connEdges = root.connectedEdges().intersection(callingEles);
4428 var k = connEdges.length;
4429 var s = 0; // Now, sum edge weights
4430
4431 for (var i = 0; i < connEdges.length; i++) {
4432 s += weight(connEdges[i]);
4433 }
4434
4435 return {
4436 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4437 };
4438 } else {
4439 var edges = root.connectedEdges();
4440 var incoming = edges.filter(function (edge) {
4441 return edge.target().same(root) && callingEles.has(edge);
4442 });
4443 var outgoing = edges.filter(function (edge) {
4444 return edge.source().same(root) && callingEles.has(edge);
4445 });
4446 var k_in = incoming.length;
4447 var k_out = outgoing.length;
4448 var s_in = 0;
4449 var s_out = 0; // Now, sum incoming edge weights
4450
4451 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4452 s_in += weight(incoming[_i2]);
4453 } // Now, sum outgoing edge weights
4454
4455
4456 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4457 s_out += weight(outgoing[_i3]);
4458 }
4459
4460 return {
4461 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4462 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4463 };
4464 }
4465 } // degreeCentrality
4466
4467 }; // elesfn
4468 // nice, short mathemathical alias
4469
4470 elesfn$8.dc = elesfn$8.degreeCentrality;
4471 elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
4472
4473 var defaults$2 = defaults({
4474 harmonic: true,
4475 weight: function weight() {
4476 return 1;
4477 },
4478 directed: false,
4479 root: null
4480 });
4481 var elesfn$9 = {
4482 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4483 var _defaults = defaults$2(options),
4484 harmonic = _defaults.harmonic,
4485 weight = _defaults.weight,
4486 directed = _defaults.directed;
4487
4488 var cy = this.cy();
4489 var closenesses = {};
4490 var maxCloseness = 0;
4491 var nodes = this.nodes();
4492 var fw = this.floydWarshall({
4493 weight: weight,
4494 directed: directed
4495 }); // Compute closeness for every node and find the maximum closeness
4496
4497 for (var i = 0; i < nodes.length; i++) {
4498 var currCloseness = 0;
4499 var node_i = nodes[i];
4500
4501 for (var j = 0; j < nodes.length; j++) {
4502 if (i !== j) {
4503 var d = fw.distance(node_i, nodes[j]);
4504
4505 if (harmonic) {
4506 currCloseness += 1 / d;
4507 } else {
4508 currCloseness += d;
4509 }
4510 }
4511 }
4512
4513 if (!harmonic) {
4514 currCloseness = 1 / currCloseness;
4515 }
4516
4517 if (maxCloseness < currCloseness) {
4518 maxCloseness = currCloseness;
4519 }
4520
4521 closenesses[node_i.id()] = currCloseness;
4522 }
4523
4524 return {
4525 closeness: function closeness(node) {
4526 if (maxCloseness == 0) {
4527 return 0;
4528 }
4529
4530 if (string(node)) {
4531 // from is a selector string
4532 node = cy.filter(node)[0].id();
4533 } else {
4534 // from is a node
4535 node = node.id();
4536 }
4537
4538 return closenesses[node] / maxCloseness;
4539 }
4540 };
4541 },
4542 // Implemented from pseudocode from wikipedia
4543 closenessCentrality: function closenessCentrality(options) {
4544 var _defaults2 = defaults$2(options),
4545 root = _defaults2.root,
4546 weight = _defaults2.weight,
4547 directed = _defaults2.directed,
4548 harmonic = _defaults2.harmonic;
4549
4550 root = this.filter(root)[0]; // we need distance from this node to every other node
4551
4552 var dijkstra = this.dijkstra({
4553 root: root,
4554 weight: weight,
4555 directed: directed
4556 });
4557 var totalDistance = 0;
4558 var nodes = this.nodes();
4559
4560 for (var i = 0; i < nodes.length; i++) {
4561 var n = nodes[i];
4562
4563 if (!n.same(root)) {
4564 var d = dijkstra.distanceTo(n);
4565
4566 if (harmonic) {
4567 totalDistance += 1 / d;
4568 } else {
4569 totalDistance += d;
4570 }
4571 }
4572 }
4573
4574 return harmonic ? totalDistance : 1 / totalDistance;
4575 } // closenessCentrality
4576
4577 }; // elesfn
4578 // nice, short mathemathical alias
4579
4580 elesfn$9.cc = elesfn$9.closenessCentrality;
4581 elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
4582
4583 var defaults$3 = defaults({
4584 weight: null,
4585 directed: false
4586 });
4587 var elesfn$a = {
4588 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4589 betweennessCentrality: function betweennessCentrality(options) {
4590 var _defaults = defaults$3(options),
4591 directed = _defaults.directed,
4592 weight = _defaults.weight;
4593
4594 var weighted = weight != null;
4595 var cy = this.cy(); // starting
4596
4597 var V = this.nodes();
4598 var A = {};
4599 var _C = {};
4600 var max = 0;
4601 var C = {
4602 set: function set(key, val) {
4603 _C[key] = val;
4604
4605 if (val > max) {
4606 max = val;
4607 }
4608 },
4609 get: function get(key) {
4610 return _C[key];
4611 }
4612 }; // A contains the neighborhoods of every node
4613
4614 for (var i = 0; i < V.length; i++) {
4615 var v = V[i];
4616 var vid = v.id();
4617
4618 if (directed) {
4619 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4620 } else {
4621 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4622 }
4623
4624 C.set(vid, 0);
4625 }
4626
4627 var _loop = function _loop(s) {
4628 var sid = V[s].id();
4629 var S = []; // stack
4630
4631 var P = {};
4632 var g = {};
4633 var d = {};
4634 var Q = new heap$1(function (a, b) {
4635 return d[a] - d[b];
4636 }); // queue
4637 // init dictionaries
4638
4639 for (var _i = 0; _i < V.length; _i++) {
4640 var _vid = V[_i].id();
4641
4642 P[_vid] = [];
4643 g[_vid] = 0;
4644 d[_vid] = Infinity;
4645 }
4646
4647 g[sid] = 1; // sigma
4648
4649 d[sid] = 0; // distance to s
4650
4651 Q.push(sid);
4652
4653 while (!Q.empty()) {
4654 var _v = Q.pop();
4655
4656 S.push(_v);
4657
4658 if (weighted) {
4659 for (var j = 0; j < A[_v].length; j++) {
4660 var w = A[_v][j];
4661 var vEle = cy.getElementById(_v);
4662 var edge = void 0;
4663
4664 if (vEle.edgesTo(w).length > 0) {
4665 edge = vEle.edgesTo(w)[0];
4666 } else {
4667 edge = w.edgesTo(vEle)[0];
4668 }
4669
4670 var edgeWeight = weight(edge);
4671 w = w.id();
4672
4673 if (d[w] > d[_v] + edgeWeight) {
4674 d[w] = d[_v] + edgeWeight;
4675
4676 if (Q.nodes.indexOf(w) < 0) {
4677 //if w is not in Q
4678 Q.push(w);
4679 } else {
4680 // update position if w is in Q
4681 Q.updateItem(w);
4682 }
4683
4684 g[w] = 0;
4685 P[w] = [];
4686 }
4687
4688 if (d[w] == d[_v] + edgeWeight) {
4689 g[w] = g[w] + g[_v];
4690 P[w].push(_v);
4691 }
4692 }
4693 } else {
4694 for (var _j = 0; _j < A[_v].length; _j++) {
4695 var _w = A[_v][_j].id();
4696
4697 if (d[_w] == Infinity) {
4698 Q.push(_w);
4699 d[_w] = d[_v] + 1;
4700 }
4701
4702 if (d[_w] == d[_v] + 1) {
4703 g[_w] = g[_w] + g[_v];
4704
4705 P[_w].push(_v);
4706 }
4707 }
4708 }
4709 }
4710
4711 var e = {};
4712
4713 for (var _i2 = 0; _i2 < V.length; _i2++) {
4714 e[V[_i2].id()] = 0;
4715 }
4716
4717 while (S.length > 0) {
4718 var _w2 = S.pop();
4719
4720 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4721 var _v2 = P[_w2][_j2];
4722 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4723
4724 if (_w2 != V[s].id()) {
4725 C.set(_w2, C.get(_w2) + e[_w2]);
4726 }
4727 }
4728 }
4729 };
4730
4731 for (var s = 0; s < V.length; s++) {
4732 _loop(s);
4733 }
4734
4735 var ret = {
4736 betweenness: function betweenness(node) {
4737 var id = cy.collection(node).id();
4738 return C.get(id);
4739 },
4740 betweennessNormalized: function betweennessNormalized(node) {
4741 if (max == 0) {
4742 return 0;
4743 }
4744
4745 var id = cy.collection(node).id();
4746 return C.get(id) / max;
4747 }
4748 }; // alias
4749
4750 ret.betweennessNormalised = ret.betweennessNormalized;
4751 return ret;
4752 } // betweennessCentrality
4753
4754 }; // elesfn
4755 // nice, short mathemathical alias
4756
4757 elesfn$a.bc = elesfn$a.betweennessCentrality;
4758
4759 // Implemented by Zoe Xi @zoexi for GSOC 2016
4760 /* eslint-disable no-unused-vars */
4761
4762 var defaults$4 = defaults({
4763 expandFactor: 2,
4764 // affects time of computation and cluster granularity to some extent: M * M
4765 inflateFactor: 2,
4766 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4767 multFactor: 1,
4768 // optional self loops for each node. Use a neutral value to improve cluster computations.
4769 maxIterations: 20,
4770 // maximum number of iterations of the MCL algorithm in a single run
4771 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4772 function (edge) {
4773 return 1;
4774 }]
4775 });
4776 /* eslint-enable */
4777
4778 var setOptions = function setOptions(options) {
4779 return defaults$4(options);
4780 };
4781 /* eslint-enable */
4782
4783
4784 var getSimilarity = function getSimilarity(edge, attributes) {
4785 var total = 0;
4786
4787 for (var i = 0; i < attributes.length; i++) {
4788 total += attributes[i](edge);
4789 }
4790
4791 return total;
4792 };
4793
4794 var addLoops = function addLoops(M, n, val) {
4795 for (var i = 0; i < n; i++) {
4796 M[i * n + i] = val;
4797 }
4798 };
4799
4800 var normalize = function normalize(M, n) {
4801 var sum;
4802
4803 for (var col = 0; col < n; col++) {
4804 sum = 0;
4805
4806 for (var row = 0; row < n; row++) {
4807 sum += M[row * n + col];
4808 }
4809
4810 for (var _row = 0; _row < n; _row++) {
4811 M[_row * n + col] = M[_row * n + col] / sum;
4812 }
4813 }
4814 }; // TODO: blocked matrix multiplication?
4815
4816
4817 var mmult = function mmult(A, B, n) {
4818 var C = new Array(n * n);
4819
4820 for (var i = 0; i < n; i++) {
4821 for (var j = 0; j < n; j++) {
4822 C[i * n + j] = 0;
4823 }
4824
4825 for (var k = 0; k < n; k++) {
4826 for (var _j = 0; _j < n; _j++) {
4827 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4828 }
4829 }
4830 }
4831
4832 return C;
4833 };
4834
4835 var expand = function expand(M, n, expandFactor
4836 /** power **/
4837 ) {
4838 var _M = M.slice(0);
4839
4840 for (var p = 1; p < expandFactor; p++) {
4841 M = mmult(M, _M, n);
4842 }
4843
4844 return M;
4845 };
4846
4847 var inflate = function inflate(M, n, inflateFactor
4848 /** r **/
4849 ) {
4850 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4851
4852
4853 for (var i = 0; i < n * n; i++) {
4854 _M[i] = Math.pow(M[i], inflateFactor);
4855 }
4856
4857 normalize(_M, n);
4858 return _M;
4859 };
4860
4861 var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4862 // Check that both matrices have the same elements (i,j)
4863 for (var i = 0; i < n2; i++) {
4864 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4865
4866 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4867
4868 if (v1 !== v2) {
4869 return false;
4870 }
4871 }
4872
4873 return true;
4874 };
4875
4876 var assign = function assign(M, n, nodes, cy) {
4877 var clusters = [];
4878
4879 for (var i = 0; i < n; i++) {
4880 var cluster = [];
4881
4882 for (var j = 0; j < n; j++) {
4883 // Row-wise attractors and elements that they attract belong in same cluster
4884 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4885 cluster.push(nodes[j]);
4886 }
4887 }
4888
4889 if (cluster.length !== 0) {
4890 clusters.push(cy.collection(cluster));
4891 }
4892 }
4893
4894 return clusters;
4895 };
4896
4897 var isDuplicate = function isDuplicate(c1, c2) {
4898 for (var i = 0; i < c1.length; i++) {
4899 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4900 return false;
4901 }
4902 }
4903
4904 return true;
4905 };
4906
4907 var removeDuplicates = function removeDuplicates(clusters) {
4908 for (var i = 0; i < clusters.length; i++) {
4909 for (var j = 0; j < clusters.length; j++) {
4910 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4911 clusters.splice(j, 1);
4912 }
4913 }
4914 }
4915
4916 return clusters;
4917 };
4918
4919 var markovClustering = function markovClustering(options) {
4920 var nodes = this.nodes();
4921 var edges = this.edges();
4922 var cy = this.cy(); // Set parameters of algorithm:
4923
4924 var opts = setOptions(options); // Map each node to its position in node array
4925
4926 var id2position = {};
4927
4928 for (var i = 0; i < nodes.length; i++) {
4929 id2position[nodes[i].id()] = i;
4930 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4931
4932
4933 var n = nodes.length,
4934 n2 = n * n;
4935
4936 var M = new Array(n2),
4937 _M;
4938
4939 for (var _i = 0; _i < n2; _i++) {
4940 M[_i] = 0;
4941 }
4942
4943 for (var e = 0; e < edges.length; e++) {
4944 var edge = edges[e];
4945 var _i2 = id2position[edge.source().id()];
4946 var j = id2position[edge.target().id()];
4947 var sim = getSimilarity(edge, opts.attributes);
4948 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4949
4950 M[j * n + _i2] += sim;
4951 } // Begin Markov cluster algorithm
4952 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4953
4954
4955 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4956
4957 normalize(M, n);
4958 var isStillMoving = true;
4959 var iterations = 0;
4960
4961 while (isStillMoving && iterations < opts.maxIterations) {
4962 isStillMoving = false; // Step 3:
4963
4964 _M = expand(M, n, opts.expandFactor); // Step 4:
4965
4966 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4967
4968 if (!hasConverged(M, _M, n2, 4)) {
4969 isStillMoving = true;
4970 }
4971
4972 iterations++;
4973 } // Build clusters from matrix
4974
4975
4976 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4977
4978 clusters = removeDuplicates(clusters);
4979 return clusters;
4980 };
4981
4982 var markovClustering$1 = {
4983 markovClustering: markovClustering,
4984 mcl: markovClustering
4985 };
4986
4987 // Common distance metrics for clustering algorithms
4988
4989 var identity = function identity(x) {
4990 return x;
4991 };
4992
4993 var absDiff = function absDiff(p, q) {
4994 return Math.abs(q - p);
4995 };
4996
4997 var addAbsDiff = function addAbsDiff(total, p, q) {
4998 return total + absDiff(p, q);
4999 };
5000
5001 var addSquaredDiff = function addSquaredDiff(total, p, q) {
5002 return total + Math.pow(q - p, 2);
5003 };
5004
5005 var sqrt = function sqrt(x) {
5006 return Math.sqrt(x);
5007 };
5008
5009 var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
5010 return Math.max(currentMax, absDiff(p, q));
5011 };
5012
5013 var getDistance = function getDistance(length, getP, getQ, init, visit) {
5014 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
5015 var ret = init;
5016 var p, q;
5017
5018 for (var dim = 0; dim < length; dim++) {
5019 p = getP(dim);
5020 q = getQ(dim);
5021 ret = visit(ret, p, q);
5022 }
5023
5024 return post(ret);
5025 };
5026
5027 var distances = {
5028 euclidean: function euclidean(length, getP, getQ) {
5029 if (length >= 2) {
5030 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
5031 } else {
5032 // for single attr case, more efficient to avoid sqrt
5033 return getDistance(length, getP, getQ, 0, addAbsDiff);
5034 }
5035 },
5036 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
5037 return getDistance(length, getP, getQ, 0, addSquaredDiff);
5038 },
5039 manhattan: function manhattan(length, getP, getQ) {
5040 return getDistance(length, getP, getQ, 0, addAbsDiff);
5041 },
5042 max: function max(length, getP, getQ) {
5043 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
5044 }
5045 }; // in case the user accidentally doesn't use camel case
5046
5047 distances['squared-euclidean'] = distances['squaredEuclidean'];
5048 distances['squaredeuclidean'] = distances['squaredEuclidean'];
5049 function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
5050 var impl;
5051
5052 if (fn(method)) {
5053 impl = method;
5054 } else {
5055 impl = distances[method] || distances.euclidean;
5056 }
5057
5058 if (length === 0 && fn(method)) {
5059 return impl(nodeP, nodeQ);
5060 } else {
5061 return impl(length, getP, getQ, nodeP, nodeQ);
5062 }
5063 }
5064
5065 var defaults$5 = defaults({
5066 k: 2,
5067 m: 2,
5068 sensitivityThreshold: 0.0001,
5069 distance: 'euclidean',
5070 maxIterations: 10,
5071 attributes: [],
5072 testMode: false,
5073 testCentroids: null
5074 });
5075
5076 var setOptions$1 = function setOptions(options) {
5077 return defaults$5(options);
5078 };
5079 /* eslint-enable */
5080
5081
5082 var getDist = function getDist(type, node, centroid, attributes, mode) {
5083 var noNodeP = mode !== 'kMedoids';
5084 var getP = noNodeP ? function (i) {
5085 return centroid[i];
5086 } : function (i) {
5087 return attributes[i](centroid);
5088 };
5089
5090 var getQ = function getQ(i) {
5091 return attributes[i](node);
5092 };
5093
5094 var nodeP = centroid;
5095 var nodeQ = node;
5096 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
5097 };
5098
5099 var randomCentroids = function randomCentroids(nodes, k, attributes) {
5100 var ndim = attributes.length;
5101 var min = new Array(ndim);
5102 var max = new Array(ndim);
5103 var centroids = new Array(k);
5104 var centroid = null; // Find min, max values for each attribute dimension
5105
5106 for (var i = 0; i < ndim; i++) {
5107 min[i] = nodes.min(attributes[i]).value;
5108 max[i] = nodes.max(attributes[i]).value;
5109 } // Build k centroids, each represented as an n-dim feature vector
5110
5111
5112 for (var c = 0; c < k; c++) {
5113 centroid = [];
5114
5115 for (var _i = 0; _i < ndim; _i++) {
5116 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
5117 }
5118
5119 centroids[c] = centroid;
5120 }
5121
5122 return centroids;
5123 };
5124
5125 var classify = function classify(node, centroids, distance, attributes, type) {
5126 var min = Infinity;
5127 var index = 0;
5128
5129 for (var i = 0; i < centroids.length; i++) {
5130 var dist = getDist(distance, node, centroids[i], attributes, type);
5131
5132 if (dist < min) {
5133 min = dist;
5134 index = i;
5135 }
5136 }
5137
5138 return index;
5139 };
5140
5141 var buildCluster = function buildCluster(centroid, nodes, assignment) {
5142 var cluster = [];
5143 var node = null;
5144
5145 for (var n = 0; n < nodes.length; n++) {
5146 node = nodes[n];
5147
5148 if (assignment[node.id()] === centroid) {
5149 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
5150 cluster.push(node);
5151 }
5152 }
5153
5154 return cluster;
5155 };
5156
5157 var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
5158 return Math.abs(v2 - v1) <= sensitivityThreshold;
5159 };
5160
5161 var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
5162 for (var i = 0; i < v1.length; i++) {
5163 for (var j = 0; j < v1[i].length; j++) {
5164 var diff = Math.abs(v1[i][j] - v2[i][j]);
5165
5166 if (diff > sensitivityThreshold) {
5167 return false;
5168 }
5169 }
5170 }
5171
5172 return true;
5173 };
5174
5175 var seenBefore = function seenBefore(node, medoids, n) {
5176 for (var i = 0; i < n; i++) {
5177 if (node === medoids[i]) return true;
5178 }
5179
5180 return false;
5181 };
5182
5183 var randomMedoids = function randomMedoids(nodes, k) {
5184 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
5185 // so we need to check to see if we've already seen or chose this node before.
5186
5187 if (nodes.length < 50) {
5188 // Randomly select k medoids from the n nodes
5189 for (var i = 0; i < k; i++) {
5190 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).
5191 // Instead choose a different random node.
5192
5193 while (seenBefore(node, medoids, i)) {
5194 node = nodes[Math.floor(Math.random() * nodes.length)];
5195 }
5196
5197 medoids[i] = node;
5198 }
5199 } else {
5200 // Relatively large data set, so pretty safe to not check and just select random nodes
5201 for (var _i2 = 0; _i2 < k; _i2++) {
5202 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
5203 }
5204 }
5205
5206 return medoids;
5207 };
5208
5209 var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
5210 var cost = 0;
5211
5212 for (var n = 0; n < cluster.length; n++) {
5213 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
5214 }
5215
5216 return cost;
5217 };
5218
5219 var kMeans = function kMeans(options) {
5220 var cy = this.cy();
5221 var nodes = this.nodes();
5222 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
5223
5224 var opts = setOptions$1(options); // Begin k-means algorithm
5225
5226 var clusters = new Array(opts.k);
5227 var assignment = {};
5228 var centroids; // Step 1: Initialize centroid positions
5229
5230 if (opts.testMode) {
5231 if (typeof opts.testCentroids === 'number') {
5232 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5233 } else if (_typeof(opts.testCentroids) === 'object') {
5234 centroids = opts.testCentroids;
5235 } else {
5236 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5237 }
5238 } else {
5239 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5240 }
5241
5242 var isStillMoving = true;
5243 var iterations = 0;
5244
5245 while (isStillMoving && iterations < opts.maxIterations) {
5246 // Step 2: Assign nodes to the nearest centroid
5247 for (var n = 0; n < nodes.length; n++) {
5248 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5249
5250 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5251 } // Step 3: For each of the k clusters, update its centroid
5252
5253
5254 isStillMoving = false;
5255
5256 for (var c = 0; c < opts.k; c++) {
5257 // Get all nodes that belong to this cluster
5258 var cluster = buildCluster(c, nodes, assignment);
5259
5260 if (cluster.length === 0) {
5261 // If cluster is empty, break out early & move to next cluster
5262 continue;
5263 } // Update centroids by calculating avg of all nodes within the cluster.
5264
5265
5266 var ndim = opts.attributes.length;
5267 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5268
5269 var newCentroid = new Array(ndim);
5270 var sum = new Array(ndim);
5271
5272 for (var d = 0; d < ndim; d++) {
5273 sum[d] = 0.0;
5274
5275 for (var i = 0; i < cluster.length; i++) {
5276 node = cluster[i];
5277 sum[d] += opts.attributes[d](node);
5278 }
5279
5280 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
5281
5282 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5283 isStillMoving = true;
5284 }
5285 }
5286
5287 centroids[c] = newCentroid;
5288 clusters[c] = cy.collection(cluster);
5289 }
5290
5291 iterations++;
5292 }
5293
5294 return clusters;
5295 };
5296
5297 var kMedoids = function kMedoids(options) {
5298 var cy = this.cy();
5299 var nodes = this.nodes();
5300 var node = null;
5301 var opts = setOptions$1(options); // Begin k-medoids algorithm
5302
5303 var clusters = new Array(opts.k);
5304 var medoids;
5305 var assignment = {};
5306 var curCost;
5307 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5308 // Step 1: Initialize k medoids
5309
5310 if (opts.testMode) {
5311 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5312 medoids = opts.testCentroids;
5313 } else {
5314 medoids = randomMedoids(nodes, opts.k);
5315 }
5316 } else {
5317 medoids = randomMedoids(nodes, opts.k);
5318 }
5319
5320 var isStillMoving = true;
5321 var iterations = 0;
5322
5323 while (isStillMoving && iterations < opts.maxIterations) {
5324 // Step 2: Assign nodes to the nearest medoid
5325 for (var n = 0; n < nodes.length; n++) {
5326 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5327
5328 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5329 }
5330
5331 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
5332 // select the node with the lowest configuration cost as new medoid.
5333
5334 for (var m = 0; m < medoids.length; m++) {
5335 // Get all nodes that belong to this medoid
5336 var cluster = buildCluster(m, nodes, assignment);
5337
5338 if (cluster.length === 0) {
5339 // If cluster is empty, break out early & move to next cluster
5340 continue;
5341 }
5342
5343 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5344 // Select different medoid if its configuration has the lowest cost
5345
5346 for (var _n = 0; _n < cluster.length; _n++) {
5347 curCost = findCost(cluster[_n], cluster, opts.attributes);
5348
5349 if (curCost < minCosts[m]) {
5350 minCosts[m] = curCost;
5351 medoids[m] = cluster[_n];
5352 isStillMoving = true;
5353 }
5354 }
5355
5356 clusters[m] = cy.collection(cluster);
5357 }
5358
5359 iterations++;
5360 }
5361
5362 return clusters;
5363 };
5364
5365 var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5366 var numerator, denominator;
5367
5368 for (var n = 0; n < nodes.length; n++) {
5369 for (var c = 0; c < centroids.length; c++) {
5370 weight[n][c] = Math.pow(U[n][c], opts.m);
5371 }
5372 }
5373
5374 for (var _c = 0; _c < centroids.length; _c++) {
5375 for (var dim = 0; dim < opts.attributes.length; dim++) {
5376 numerator = 0;
5377 denominator = 0;
5378
5379 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5380 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5381 denominator += weight[_n2][_c];
5382 }
5383
5384 centroids[_c][dim] = numerator / denominator;
5385 }
5386 }
5387 };
5388
5389 var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5390 // Save previous step
5391 for (var i = 0; i < U.length; i++) {
5392 _U[i] = U[i].slice();
5393 }
5394
5395 var sum, numerator, denominator;
5396 var pow = 2 / (opts.m - 1);
5397
5398 for (var c = 0; c < centroids.length; c++) {
5399 for (var n = 0; n < nodes.length; n++) {
5400 sum = 0;
5401
5402 for (var k = 0; k < centroids.length; k++) {
5403 // against all other centroids
5404 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5405 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5406 sum += Math.pow(numerator / denominator, pow);
5407 }
5408
5409 U[n][c] = 1 / sum;
5410 }
5411 }
5412 };
5413
5414 var assign$1 = function assign(nodes, U, opts, cy) {
5415 var clusters = new Array(opts.k);
5416
5417 for (var c = 0; c < clusters.length; c++) {
5418 clusters[c] = [];
5419 }
5420
5421 var max;
5422 var index;
5423
5424 for (var n = 0; n < U.length; n++) {
5425 // for each node (U is N x C matrix)
5426 max = -Infinity;
5427 index = -1; // Determine which cluster the node is most likely to belong in
5428
5429 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5430 if (U[n][_c2] > max) {
5431 max = U[n][_c2];
5432 index = _c2;
5433 }
5434 }
5435
5436 clusters[index].push(nodes[n]);
5437 } // Turn every array into a collection of nodes
5438
5439
5440 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5441 clusters[_c3] = cy.collection(clusters[_c3]);
5442 }
5443
5444 return clusters;
5445 };
5446
5447 var fuzzyCMeans = function fuzzyCMeans(options) {
5448 var cy = this.cy();
5449 var nodes = this.nodes();
5450 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
5451
5452 var clusters;
5453 var centroids;
5454 var U;
5455
5456 var _U;
5457
5458 var weight; // Step 1: Initialize letiables.
5459
5460 _U = new Array(nodes.length);
5461
5462 for (var i = 0; i < nodes.length; i++) {
5463 // N x C matrix
5464 _U[i] = new Array(opts.k);
5465 }
5466
5467 U = new Array(nodes.length);
5468
5469 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5470 // N x C matrix
5471 U[_i3] = new Array(opts.k);
5472 }
5473
5474 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5475 var total = 0;
5476
5477 for (var j = 0; j < opts.k; j++) {
5478 U[_i4][j] = Math.random();
5479 total += U[_i4][j];
5480 }
5481
5482 for (var _j = 0; _j < opts.k; _j++) {
5483 U[_i4][_j] = U[_i4][_j] / total;
5484 }
5485 }
5486
5487 centroids = new Array(opts.k);
5488
5489 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5490 centroids[_i5] = new Array(opts.attributes.length);
5491 }
5492
5493 weight = new Array(nodes.length);
5494
5495 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5496 // N x C matrix
5497 weight[_i6] = new Array(opts.k);
5498 } // end init FCM
5499
5500
5501 var isStillMoving = true;
5502 var iterations = 0;
5503
5504 while (isStillMoving && iterations < opts.maxIterations) {
5505 isStillMoving = false; // Step 2: Calculate the centroids for each step.
5506
5507 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
5508
5509 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
5510
5511 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5512 isStillMoving = true;
5513 }
5514
5515 iterations++;
5516 } // Assign nodes to clusters with highest probability.
5517
5518
5519 clusters = assign$1(nodes, U, opts, cy);
5520 return {
5521 clusters: clusters,
5522 degreeOfMembership: U
5523 };
5524 };
5525
5526 var kClustering = {
5527 kMeans: kMeans,
5528 kMedoids: kMedoids,
5529 fuzzyCMeans: fuzzyCMeans,
5530 fcm: fuzzyCMeans
5531 };
5532
5533 // Implemented by Zoe Xi @zoexi for GSOC 2016
5534 var defaults$6 = defaults({
5535 distance: 'euclidean',
5536 // distance metric to compare nodes
5537 linkage: 'min',
5538 // linkage criterion : how to determine the distance between clusters of nodes
5539 mode: 'threshold',
5540 // mode:'threshold' => clusters must be threshold distance apart
5541 threshold: Infinity,
5542 // the distance threshold
5543 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5544 addDendrogram: false,
5545 // whether to add the dendrogram to the graph for viz
5546 dendrogramDepth: 0,
5547 // depth at which dendrogram branches are merged into the returned clusters
5548 attributes: [] // array of attr functions
5549
5550 });
5551 var linkageAliases = {
5552 'single': 'min',
5553 'complete': 'max'
5554 };
5555
5556 var setOptions$2 = function setOptions(options) {
5557 var opts = defaults$6(options);
5558 var preferredAlias = linkageAliases[opts.linkage];
5559
5560 if (preferredAlias != null) {
5561 opts.linkage = preferredAlias;
5562 }
5563
5564 return opts;
5565 };
5566
5567 var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5568 // Find two closest clusters from cached mins
5569 var minKey = 0;
5570 var min = Infinity;
5571 var dist;
5572 var attrs = opts.attributes;
5573
5574 var getDist = function getDist(n1, n2) {
5575 return clusteringDistance(opts.distance, attrs.length, function (i) {
5576 return attrs[i](n1);
5577 }, function (i) {
5578 return attrs[i](n2);
5579 }, n1, n2);
5580 };
5581
5582 for (var i = 0; i < clusters.length; i++) {
5583 var key = clusters[i].key;
5584 var _dist = dists[key][mins[key]];
5585
5586 if (_dist < min) {
5587 minKey = key;
5588 min = _dist;
5589 }
5590 }
5591
5592 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5593 return false;
5594 }
5595
5596 var c1 = index[minKey];
5597 var c2 = index[mins[minKey]];
5598 var merged; // Merge two closest clusters
5599
5600 if (opts.mode === 'dendrogram') {
5601 merged = {
5602 left: c1,
5603 right: c2,
5604 key: c1.key
5605 };
5606 } else {
5607 merged = {
5608 value: c1.value.concat(c2.value),
5609 key: c1.key
5610 };
5611 }
5612
5613 clusters[c1.index] = merged;
5614 clusters.splice(c2.index, 1);
5615 index[c1.key] = merged; // Update distances with new merged cluster
5616
5617 for (var _i = 0; _i < clusters.length; _i++) {
5618 var cur = clusters[_i];
5619
5620 if (c1.key === cur.key) {
5621 dist = Infinity;
5622 } else if (opts.linkage === 'min') {
5623 dist = dists[c1.key][cur.key];
5624
5625 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5626 dist = dists[c2.key][cur.key];
5627 }
5628 } else if (opts.linkage === 'max') {
5629 dist = dists[c1.key][cur.key];
5630
5631 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5632 dist = dists[c2.key][cur.key];
5633 }
5634 } else if (opts.linkage === 'mean') {
5635 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5636 } else {
5637 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5638 }
5639
5640 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5641 } // Update cached mins
5642
5643
5644 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5645 var key1 = clusters[_i2].key;
5646
5647 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5648 var _min = key1;
5649
5650 for (var j = 0; j < clusters.length; j++) {
5651 var key2 = clusters[j].key;
5652
5653 if (dists[key1][key2] < dists[key1][_min]) {
5654 _min = key2;
5655 }
5656 }
5657
5658 mins[key1] = _min;
5659 }
5660
5661 clusters[_i2].index = _i2;
5662 } // Clean up meta data used for clustering
5663
5664
5665 c1.key = c2.key = c1.index = c2.index = null;
5666 return true;
5667 };
5668
5669 var getAllChildren = function getAllChildren(root, arr, cy) {
5670 if (!root) return;
5671
5672 if (root.value) {
5673 arr.push(root.value);
5674 } else {
5675 if (root.left) getAllChildren(root.left, arr);
5676 if (root.right) getAllChildren(root.right, arr);
5677 }
5678 };
5679
5680 var buildDendrogram = function buildDendrogram(root, cy) {
5681 if (!root) return '';
5682
5683 if (root.left && root.right) {
5684 var leftStr = buildDendrogram(root.left, cy);
5685 var rightStr = buildDendrogram(root.right, cy);
5686 var node = cy.add({
5687 group: 'nodes',
5688 data: {
5689 id: leftStr + ',' + rightStr
5690 }
5691 });
5692 cy.add({
5693 group: 'edges',
5694 data: {
5695 source: leftStr,
5696 target: node.id()
5697 }
5698 });
5699 cy.add({
5700 group: 'edges',
5701 data: {
5702 source: rightStr,
5703 target: node.id()
5704 }
5705 });
5706 return node.id();
5707 } else if (root.value) {
5708 return root.value.id();
5709 }
5710 };
5711
5712 var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5713 if (!root) return [];
5714 var left = [],
5715 right = [],
5716 leaves = [];
5717
5718 if (k === 0) {
5719 // don't cut tree, simply return all nodes as 1 single cluster
5720 if (root.left) getAllChildren(root.left, left);
5721 if (root.right) getAllChildren(root.right, right);
5722 leaves = left.concat(right);
5723 return [cy.collection(leaves)];
5724 } else if (k === 1) {
5725 // cut at root
5726 if (root.value) {
5727 // leaf node
5728 return [cy.collection(root.value)];
5729 } else {
5730 if (root.left) getAllChildren(root.left, left);
5731 if (root.right) getAllChildren(root.right, right);
5732 return [cy.collection(left), cy.collection(right)];
5733 }
5734 } else {
5735 if (root.value) {
5736 return [cy.collection(root.value)];
5737 } else {
5738 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5739 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5740 return left.concat(right);
5741 }
5742 }
5743 };
5744 /* eslint-enable */
5745
5746
5747 var hierarchicalClustering = function hierarchicalClustering(options) {
5748 var cy = this.cy();
5749 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5750
5751 var opts = setOptions$2(options);
5752 var attrs = opts.attributes;
5753
5754 var getDist = function getDist(n1, n2) {
5755 return clusteringDistance(opts.distance, attrs.length, function (i) {
5756 return attrs[i](n1);
5757 }, function (i) {
5758 return attrs[i](n2);
5759 }, n1, n2);
5760 }; // Begin hierarchical algorithm
5761
5762
5763 var clusters = [];
5764 var dists = []; // distances between each pair of clusters
5765
5766 var mins = []; // closest cluster for each cluster
5767
5768 var index = []; // hash of all clusters by key
5769 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5770
5771 for (var n = 0; n < nodes.length; n++) {
5772 var cluster = {
5773 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5774 key: n,
5775 index: n
5776 };
5777 clusters[n] = cluster;
5778 index[n] = cluster;
5779 dists[n] = [];
5780 mins[n] = 0;
5781 } // Calculate the distance between each pair of clusters
5782
5783
5784 for (var i = 0; i < clusters.length; i++) {
5785 for (var j = 0; j <= i; j++) {
5786 var dist = void 0;
5787
5788 if (opts.mode === 'dendrogram') {
5789 // modes store cluster values differently
5790 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5791 } else {
5792 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5793 }
5794
5795 dists[i][j] = dist;
5796 dists[j][i] = dist;
5797
5798 if (dist < dists[i][mins[i]]) {
5799 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5800 }
5801 }
5802 } // Find the closest pair of clusters and merge them into a single cluster.
5803 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5804
5805
5806 var merged = mergeClosest(clusters, index, dists, mins, opts);
5807
5808 while (merged) {
5809 merged = mergeClosest(clusters, index, dists, mins, opts);
5810 }
5811
5812 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5813 // in addition to returning the clusters.
5814
5815 if (opts.mode === 'dendrogram') {
5816 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5817 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5818 } else {
5819 // Regular mode simply returns the clusters
5820 retClusters = new Array(clusters.length);
5821 clusters.forEach(function (cluster, i) {
5822 // Clean up meta data used for clustering
5823 cluster.key = cluster.index = null;
5824 retClusters[i] = cy.collection(cluster.value);
5825 });
5826 }
5827
5828 return retClusters;
5829 };
5830
5831 var hierarchicalClustering$1 = {
5832 hierarchicalClustering: hierarchicalClustering,
5833 hca: hierarchicalClustering
5834 };
5835
5836 // Implemented by Zoe Xi @zoexi for GSOC 2016
5837 var defaults$7 = defaults({
5838 distance: 'euclidean',
5839 // distance metric to compare attributes between two nodes
5840 preference: 'median',
5841 // suitability of a data point to serve as an exemplar
5842 damping: 0.8,
5843 // damping factor between [0.5, 1)
5844 maxIterations: 1000,
5845 // max number of iterations to run
5846 minIterations: 100,
5847 // min number of iterations to run in order for clustering to stop
5848 attributes: [// functions to quantify the similarity between any two points
5849 // e.g. node => node.data('weight')
5850 ]
5851 });
5852
5853 var setOptions$3 = function setOptions(options) {
5854 var dmp = options.damping;
5855 var pref = options.preference;
5856
5857 if (!(0.5 <= dmp && dmp < 1)) {
5858 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5859 }
5860
5861 var validPrefs = ['median', 'mean', 'min', 'max'];
5862
5863 if (!(validPrefs.some(function (v) {
5864 return v === pref;
5865 }) || number(pref))) {
5866 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5867 return "'".concat(p, "'");
5868 }).join(', '), "] or a number. Got: ").concat(pref));
5869 }
5870
5871 return defaults$7(options);
5872 };
5873 /* eslint-enable */
5874
5875
5876 var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5877 var attr = function attr(n, i) {
5878 return attributes[i](n);
5879 }; // nb negative because similarity should have an inverse relationship to distance
5880
5881
5882 return -clusteringDistance(type, attributes.length, function (i) {
5883 return attr(n1, i);
5884 }, function (i) {
5885 return attr(n2, i);
5886 }, n1, n2);
5887 };
5888
5889 var getPreference = function getPreference(S, preference) {
5890 // larger preference = greater # of clusters
5891 var p = null;
5892
5893 if (preference === 'median') {
5894 p = median(S);
5895 } else if (preference === 'mean') {
5896 p = mean(S);
5897 } else if (preference === 'min') {
5898 p = min(S);
5899 } else if (preference === 'max') {
5900 p = max(S);
5901 } else {
5902 // Custom preference number, as set by user
5903 p = preference;
5904 }
5905
5906 return p;
5907 };
5908
5909 var findExemplars = function findExemplars(n, R, A) {
5910 var indices = [];
5911
5912 for (var i = 0; i < n; i++) {
5913 if (R[i * n + i] + A[i * n + i] > 0) {
5914 indices.push(i);
5915 }
5916 }
5917
5918 return indices;
5919 };
5920
5921 var assignClusters = function assignClusters(n, S, exemplars) {
5922 var clusters = [];
5923
5924 for (var i = 0; i < n; i++) {
5925 var index = -1;
5926 var max = -Infinity;
5927
5928 for (var ei = 0; ei < exemplars.length; ei++) {
5929 var e = exemplars[ei];
5930
5931 if (S[i * n + e] > max) {
5932 index = e;
5933 max = S[i * n + e];
5934 }
5935 }
5936
5937 if (index > 0) {
5938 clusters.push(index);
5939 }
5940 }
5941
5942 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5943 clusters[exemplars[_ei]] = exemplars[_ei];
5944 }
5945
5946 return clusters;
5947 };
5948
5949 var assign$2 = function assign(n, S, exemplars) {
5950 var clusters = assignClusters(n, S, exemplars);
5951
5952 for (var ei = 0; ei < exemplars.length; ei++) {
5953 var ii = [];
5954
5955 for (var c = 0; c < clusters.length; c++) {
5956 if (clusters[c] === exemplars[ei]) {
5957 ii.push(c);
5958 }
5959 }
5960
5961 var maxI = -1;
5962 var maxSum = -Infinity;
5963
5964 for (var i = 0; i < ii.length; i++) {
5965 var sum = 0;
5966
5967 for (var j = 0; j < ii.length; j++) {
5968 sum += S[ii[j] * n + ii[i]];
5969 }
5970
5971 if (sum > maxSum) {
5972 maxI = i;
5973 maxSum = sum;
5974 }
5975 }
5976
5977 exemplars[ei] = ii[maxI];
5978 }
5979
5980 clusters = assignClusters(n, S, exemplars);
5981 return clusters;
5982 };
5983
5984 var affinityPropagation = function affinityPropagation(options) {
5985 var cy = this.cy();
5986 var nodes = this.nodes();
5987 var opts = setOptions$3(options); // Map each node to its position in node array
5988
5989 var id2position = {};
5990
5991 for (var i = 0; i < nodes.length; i++) {
5992 id2position[nodes[i].id()] = i;
5993 } // Begin affinity propagation algorithm
5994
5995
5996 var n; // number of data points
5997
5998 var n2; // size of matrices
5999
6000 var S; // similarity matrix (1D array)
6001
6002 var p; // preference/suitability of a data point to serve as an exemplar
6003
6004 var R; // responsibility matrix (1D array)
6005
6006 var A; // availability matrix (1D array)
6007
6008 n = nodes.length;
6009 n2 = n * n; // Initialize and build S similarity matrix
6010
6011 S = new Array(n2);
6012
6013 for (var _i = 0; _i < n2; _i++) {
6014 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
6015 }
6016
6017 for (var _i2 = 0; _i2 < n; _i2++) {
6018 for (var j = 0; j < n; j++) {
6019 if (_i2 !== j) {
6020 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
6021 }
6022 }
6023 } // Place preferences on the diagonal of S
6024
6025
6026 p = getPreference(S, opts.preference);
6027
6028 for (var _i3 = 0; _i3 < n; _i3++) {
6029 S[_i3 * n + _i3] = p;
6030 } // Initialize R responsibility matrix
6031
6032
6033 R = new Array(n2);
6034
6035 for (var _i4 = 0; _i4 < n2; _i4++) {
6036 R[_i4] = 0.0;
6037 } // Initialize A availability matrix
6038
6039
6040 A = new Array(n2);
6041
6042 for (var _i5 = 0; _i5 < n2; _i5++) {
6043 A[_i5] = 0.0;
6044 }
6045
6046 var old = new Array(n);
6047 var Rp = new Array(n);
6048 var se = new Array(n);
6049
6050 for (var _i6 = 0; _i6 < n; _i6++) {
6051 old[_i6] = 0.0;
6052 Rp[_i6] = 0.0;
6053 se[_i6] = 0;
6054 }
6055
6056 var e = new Array(n * opts.minIterations);
6057
6058 for (var _i7 = 0; _i7 < e.length; _i7++) {
6059 e[_i7] = 0;
6060 }
6061
6062 var iter;
6063
6064 for (iter = 0; iter < opts.maxIterations; iter++) {
6065 // main algorithmic loop
6066 // Update R responsibility matrix
6067 for (var _i8 = 0; _i8 < n; _i8++) {
6068 var max = -Infinity,
6069 max2 = -Infinity,
6070 maxI = -1,
6071 AS = 0.0;
6072
6073 for (var _j = 0; _j < n; _j++) {
6074 old[_j] = R[_i8 * n + _j];
6075 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
6076
6077 if (AS >= max) {
6078 max2 = max;
6079 max = AS;
6080 maxI = _j;
6081 } else if (AS > max2) {
6082 max2 = AS;
6083 }
6084 }
6085
6086 for (var _j2 = 0; _j2 < n; _j2++) {
6087 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
6088 }
6089
6090 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
6091 } // Update A availability matrix
6092
6093
6094 for (var _i9 = 0; _i9 < n; _i9++) {
6095 var sum = 0;
6096
6097 for (var _j3 = 0; _j3 < n; _j3++) {
6098 old[_j3] = A[_j3 * n + _i9];
6099 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
6100 sum += Rp[_j3];
6101 }
6102
6103 sum -= Rp[_i9];
6104 Rp[_i9] = R[_i9 * n + _i9];
6105 sum += Rp[_i9];
6106
6107 for (var _j4 = 0; _j4 < n; _j4++) {
6108 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
6109 }
6110
6111 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
6112 } // Check for convergence
6113
6114
6115 var K = 0;
6116
6117 for (var _i10 = 0; _i10 < n; _i10++) {
6118 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
6119 e[iter % opts.minIterations * n + _i10] = E;
6120 K += E;
6121 }
6122
6123 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
6124 var _sum = 0;
6125
6126 for (var _i11 = 0; _i11 < n; _i11++) {
6127 se[_i11] = 0;
6128
6129 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
6130 se[_i11] += e[_j5 * n + _i11];
6131 }
6132
6133 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
6134 _sum++;
6135 }
6136 }
6137
6138 if (_sum === n) {
6139 // then we have convergence
6140 break;
6141 }
6142 }
6143 } // Identify exemplars (cluster centers)
6144
6145
6146 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
6147
6148 var clusterIndices = assign$2(n, S, exemplarsIndices);
6149 var clusters = {};
6150
6151 for (var c = 0; c < exemplarsIndices.length; c++) {
6152 clusters[exemplarsIndices[c]] = [];
6153 }
6154
6155 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
6156 var pos = id2position[nodes[_i12].id()];
6157
6158 var clusterIndex = clusterIndices[pos];
6159
6160 if (clusterIndex != null) {
6161 // the node may have not been assigned a cluster if no valid attributes were specified
6162 clusters[clusterIndex].push(nodes[_i12]);
6163 }
6164 }
6165
6166 var retClusters = new Array(exemplarsIndices.length);
6167
6168 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
6169 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
6170 }
6171
6172 return retClusters;
6173 };
6174
6175 var affinityPropagation$1 = {
6176 affinityPropagation: affinityPropagation,
6177 ap: affinityPropagation
6178 };
6179
6180 var hierholzerDefaults = defaults({
6181 root: undefined,
6182 directed: false
6183 });
6184 var elesfn$b = {
6185 hierholzer: function hierholzer(options) {
6186 if (!plainObject(options)) {
6187 var args = arguments;
6188 options = {
6189 root: args[0],
6190 directed: args[1]
6191 };
6192 }
6193
6194 var _hierholzerDefaults = hierholzerDefaults(options),
6195 root = _hierholzerDefaults.root,
6196 directed = _hierholzerDefaults.directed;
6197
6198 var eles = this;
6199 var dflag = false;
6200 var oddIn;
6201 var oddOut;
6202 var startVertex;
6203 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
6204 var nodes = {};
6205 var edges = {};
6206
6207 if (directed) {
6208 eles.forEach(function (ele) {
6209 var id = ele.id();
6210
6211 if (ele.isNode()) {
6212 var ind = ele.indegree(true);
6213 var outd = ele.outdegree(true);
6214 var d1 = ind - outd;
6215 var d2 = outd - ind;
6216
6217 if (d1 == 1) {
6218 if (oddIn) dflag = true;else oddIn = id;
6219 } else if (d2 == 1) {
6220 if (oddOut) dflag = true;else oddOut = id;
6221 } else if (d2 > 1 || d1 > 1) {
6222 dflag = true;
6223 }
6224
6225 nodes[id] = [];
6226 ele.outgoers().forEach(function (e) {
6227 if (e.isEdge()) nodes[id].push(e.id());
6228 });
6229 } else {
6230 edges[id] = [undefined, ele.target().id()];
6231 }
6232 });
6233 } else {
6234 eles.forEach(function (ele) {
6235 var id = ele.id();
6236
6237 if (ele.isNode()) {
6238 var d = ele.degree(true);
6239
6240 if (d % 2) {
6241 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
6242 }
6243
6244 nodes[id] = [];
6245 ele.connectedEdges().forEach(function (e) {
6246 return nodes[id].push(e.id());
6247 });
6248 } else {
6249 edges[id] = [ele.source().id(), ele.target().id()];
6250 }
6251 });
6252 }
6253
6254 var result = {
6255 found: false,
6256 trail: undefined
6257 };
6258 if (dflag) return result;else if (oddOut && oddIn) {
6259 if (directed) {
6260 if (startVertex && oddOut != startVertex) {
6261 return result;
6262 }
6263
6264 startVertex = oddOut;
6265 } else {
6266 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
6267 return result;
6268 } else if (!startVertex) {
6269 startVertex = oddOut;
6270 }
6271 }
6272 } else {
6273 if (!startVertex) startVertex = eles[0].id();
6274 }
6275
6276 var walk = function walk(v) {
6277 var currentNode = v;
6278 var subtour = [v];
6279 var adj, adjTail, adjHead;
6280
6281 while (nodes[currentNode].length) {
6282 adj = nodes[currentNode].shift();
6283 adjTail = edges[adj][0];
6284 adjHead = edges[adj][1];
6285
6286 if (currentNode != adjHead) {
6287 nodes[adjHead] = nodes[adjHead].filter(function (e) {
6288 return e != adj;
6289 });
6290 currentNode = adjHead;
6291 } else if (!directed && currentNode != adjTail) {
6292 nodes[adjTail] = nodes[adjTail].filter(function (e) {
6293 return e != adj;
6294 });
6295 currentNode = adjTail;
6296 }
6297
6298 subtour.unshift(adj);
6299 subtour.unshift(currentNode);
6300 }
6301
6302 return subtour;
6303 };
6304
6305 var trail = [];
6306 var subtour = [];
6307 subtour = walk(startVertex);
6308
6309 while (subtour.length != 1) {
6310 if (nodes[subtour[0]].length == 0) {
6311 trail.unshift(eles.getElementById(subtour.shift()));
6312 trail.unshift(eles.getElementById(subtour.shift()));
6313 } else {
6314 subtour = walk(subtour.shift()).concat(subtour);
6315 }
6316 }
6317
6318 trail.unshift(eles.getElementById(subtour.shift())); // final node
6319
6320 for (var d in nodes) {
6321 if (nodes[d].length) {
6322 return result;
6323 }
6324 }
6325
6326 result.found = true;
6327 result.trail = this.spawn(trail);
6328 return result;
6329 }
6330 };
6331
6332 var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
6333 var eles = this;
6334 var nodes = {};
6335 var id = 0;
6336 var edgeCount = 0;
6337 var components = [];
6338 var stack = [];
6339 var visitedEdges = {};
6340
6341 var buildComponent = function buildComponent(x, y) {
6342 var i = stack.length - 1;
6343 var cutset = [];
6344 var component = eles.spawn();
6345
6346 while (stack[i].x != x || stack[i].y != y) {
6347 cutset.push(stack.pop().edge);
6348 i--;
6349 }
6350
6351 cutset.push(stack.pop().edge);
6352 cutset.forEach(function (edge) {
6353 var connectedNodes = edge.connectedNodes().intersection(eles);
6354 component.merge(edge);
6355 connectedNodes.forEach(function (node) {
6356 var nodeId = node.id();
6357 var connectedEdges = node.connectedEdges().intersection(eles);
6358 component.merge(node);
6359
6360 if (!nodes[nodeId].cutVertex) {
6361 component.merge(connectedEdges);
6362 } else {
6363 component.merge(connectedEdges.filter(function (edge) {
6364 return edge.isLoop();
6365 }));
6366 }
6367 });
6368 });
6369 components.push(component);
6370 };
6371
6372 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
6373 if (root === parent) edgeCount += 1;
6374 nodes[currentNode] = {
6375 id: id,
6376 low: id++,
6377 cutVertex: false
6378 };
6379 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
6380
6381 if (edges.size() === 0) {
6382 components.push(eles.spawn(eles.getElementById(currentNode)));
6383 } else {
6384 var sourceId, targetId, otherNodeId, edgeId;
6385 edges.forEach(function (edge) {
6386 sourceId = edge.source().id();
6387 targetId = edge.target().id();
6388 otherNodeId = sourceId === currentNode ? targetId : sourceId;
6389
6390 if (otherNodeId !== parent) {
6391 edgeId = edge.id();
6392
6393 if (!visitedEdges[edgeId]) {
6394 visitedEdges[edgeId] = true;
6395 stack.push({
6396 x: currentNode,
6397 y: otherNodeId,
6398 edge: edge
6399 });
6400 }
6401
6402 if (!(otherNodeId in nodes)) {
6403 biconnectedSearch(root, otherNodeId, currentNode);
6404 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6405
6406 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6407 nodes[currentNode].cutVertex = true;
6408 buildComponent(currentNode, otherNodeId);
6409 }
6410 } else {
6411 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6412 }
6413 }
6414 });
6415 }
6416 };
6417
6418 eles.forEach(function (ele) {
6419 if (ele.isNode()) {
6420 var nodeId = ele.id();
6421
6422 if (!(nodeId in nodes)) {
6423 edgeCount = 0;
6424 biconnectedSearch(nodeId, nodeId);
6425 nodes[nodeId].cutVertex = edgeCount > 1;
6426 }
6427 }
6428 });
6429 var cutVertices = Object.keys(nodes).filter(function (id) {
6430 return nodes[id].cutVertex;
6431 }).map(function (id) {
6432 return eles.getElementById(id);
6433 });
6434 return {
6435 cut: eles.spawn(cutVertices),
6436 components: components
6437 };
6438 };
6439
6440 var hopcroftTarjanBiconnected$1 = {
6441 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6442 htbc: hopcroftTarjanBiconnected,
6443 htb: hopcroftTarjanBiconnected,
6444 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6445 };
6446
6447 var tarjanStronglyConnected = function tarjanStronglyConnected() {
6448 var eles = this;
6449 var nodes = {};
6450 var index = 0;
6451 var components = [];
6452 var stack = [];
6453 var cut = eles.spawn(eles);
6454
6455 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6456 stack.push(sourceNodeId);
6457 nodes[sourceNodeId] = {
6458 index: index,
6459 low: index++,
6460 explored: false
6461 };
6462 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6463 connectedEdges.forEach(function (edge) {
6464 var targetNodeId = edge.target().id();
6465
6466 if (targetNodeId !== sourceNodeId) {
6467 if (!(targetNodeId in nodes)) {
6468 stronglyConnectedSearch(targetNodeId);
6469 }
6470
6471 if (!nodes[targetNodeId].explored) {
6472 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6473 }
6474 }
6475 });
6476
6477 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6478 var componentNodes = eles.spawn();
6479
6480 for (;;) {
6481 var nodeId = stack.pop();
6482 componentNodes.merge(eles.getElementById(nodeId));
6483 nodes[nodeId].low = nodes[sourceNodeId].index;
6484 nodes[nodeId].explored = true;
6485
6486 if (nodeId === sourceNodeId) {
6487 break;
6488 }
6489 }
6490
6491 var componentEdges = componentNodes.edgesWith(componentNodes);
6492 var component = componentNodes.merge(componentEdges);
6493 components.push(component);
6494 cut = cut.difference(component);
6495 }
6496 };
6497
6498 eles.forEach(function (ele) {
6499 if (ele.isNode()) {
6500 var nodeId = ele.id();
6501
6502 if (!(nodeId in nodes)) {
6503 stronglyConnectedSearch(nodeId);
6504 }
6505 }
6506 });
6507 return {
6508 cut: cut,
6509 components: components
6510 };
6511 };
6512
6513 var tarjanStronglyConnected$1 = {
6514 tarjanStronglyConnected: tarjanStronglyConnected,
6515 tsc: tarjanStronglyConnected,
6516 tscc: tarjanStronglyConnected,
6517 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6518 };
6519
6520 var elesfn$c = {};
6521 [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) {
6522 extend(elesfn$c, props);
6523 });
6524
6525 /*!
6526 Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6527 Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6528 Licensed under The MIT License (http://opensource.org/licenses/MIT)
6529 */
6530
6531 /* promise states [Promises/A+ 2.1] */
6532 var STATE_PENDING = 0;
6533 /* [Promises/A+ 2.1.1] */
6534
6535 var STATE_FULFILLED = 1;
6536 /* [Promises/A+ 2.1.2] */
6537
6538 var STATE_REJECTED = 2;
6539 /* [Promises/A+ 2.1.3] */
6540
6541 /* promise object constructor */
6542
6543 var api = function api(executor) {
6544 /* optionally support non-constructor/plain-function call */
6545 if (!(this instanceof api)) return new api(executor);
6546 /* initialize object */
6547
6548 this.id = 'Thenable/1.0.7';
6549 this.state = STATE_PENDING;
6550 /* initial state */
6551
6552 this.fulfillValue = undefined;
6553 /* initial value */
6554
6555 /* [Promises/A+ 1.3, 2.1.2.2] */
6556
6557 this.rejectReason = undefined;
6558 /* initial reason */
6559
6560 /* [Promises/A+ 1.5, 2.1.3.2] */
6561
6562 this.onFulfilled = [];
6563 /* initial handlers */
6564
6565 this.onRejected = [];
6566 /* initial handlers */
6567
6568 /* provide optional information-hiding proxy */
6569
6570 this.proxy = {
6571 then: this.then.bind(this)
6572 };
6573 /* support optional executor function */
6574
6575 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6576 };
6577 /* promise API methods */
6578
6579
6580 api.prototype = {
6581 /* promise resolving methods */
6582 fulfill: function fulfill(value) {
6583 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6584 },
6585 reject: function reject(value) {
6586 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6587 },
6588
6589 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6590 then: function then(onFulfilled, onRejected) {
6591 var curr = this;
6592 var next = new api();
6593 /* [Promises/A+ 2.2.7] */
6594
6595 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
6596 /* [Promises/A+ 2.2.2/2.2.6] */
6597
6598 curr.onRejected.push(resolver(onRejected, next, 'reject'));
6599 /* [Promises/A+ 2.2.3/2.2.6] */
6600
6601 execute(curr);
6602 return next.proxy;
6603 /* [Promises/A+ 2.2.7, 3.3] */
6604 }
6605 };
6606 /* deliver an action */
6607
6608 var deliver = function deliver(curr, state, name, value) {
6609 if (curr.state === STATE_PENDING) {
6610 curr.state = state;
6611 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6612
6613 curr[name] = value;
6614 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6615
6616 execute(curr);
6617 }
6618
6619 return curr;
6620 };
6621 /* execute all handlers */
6622
6623
6624 var execute = function execute(curr) {
6625 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6626 };
6627 /* execute particular set of handlers */
6628
6629
6630 var execute_handlers = function execute_handlers(curr, name, value) {
6631 /* global setImmediate: true */
6632
6633 /* global setTimeout: true */
6634
6635 /* short-circuit processing */
6636 if (curr[name].length === 0) return;
6637 /* iterate over all handlers, exactly once */
6638
6639 var handlers = curr[name];
6640 curr[name] = [];
6641 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6642
6643 var func = function func() {
6644 for (var i = 0; i < handlers.length; i++) {
6645 handlers[i](value);
6646 }
6647 /* [Promises/A+ 2.2.5] */
6648
6649 };
6650 /* execute procedure asynchronously */
6651
6652 /* [Promises/A+ 2.2.4, 3.1] */
6653
6654
6655 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6656 };
6657 /* generate a resolver function */
6658
6659
6660 var resolver = function resolver(cb, next, method) {
6661 return function (value) {
6662 if (typeof cb !== 'function')
6663 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6664 next[method].call(next, value);
6665 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
6666 else {
6667 var result;
6668
6669 try {
6670 result = cb(value);
6671 }
6672 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
6673 catch (e) {
6674 next.reject(e);
6675 /* [Promises/A+ 2.2.7.2] */
6676
6677 return;
6678 }
6679
6680 resolve(next, result);
6681 /* [Promises/A+ 2.2.7.1] */
6682 }
6683 };
6684 };
6685 /* "Promise Resolution Procedure" */
6686
6687 /* [Promises/A+ 2.3] */
6688
6689
6690 var resolve = function resolve(promise, x) {
6691 /* sanity check arguments */
6692
6693 /* [Promises/A+ 2.3.1] */
6694 if (promise === x || promise.proxy === x) {
6695 promise.reject(new TypeError('cannot resolve promise with itself'));
6696 return;
6697 }
6698 /* surgically check for a "then" method
6699 (mainly to just call the "getter" of "then" only once) */
6700
6701
6702 var then;
6703
6704 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6705 try {
6706 then = x.then;
6707 }
6708 /* [Promises/A+ 2.3.3.1, 3.5] */
6709 catch (e) {
6710 promise.reject(e);
6711 /* [Promises/A+ 2.3.3.2] */
6712
6713 return;
6714 }
6715 }
6716 /* handle own Thenables [Promises/A+ 2.3.2]
6717 and similar "thenables" [Promises/A+ 2.3.3] */
6718
6719
6720 if (typeof then === 'function') {
6721 var resolved = false;
6722
6723 try {
6724 /* call retrieved "then" method */
6725
6726 /* [Promises/A+ 2.3.3.3] */
6727 then.call(x,
6728 /* resolvePromise */
6729
6730 /* [Promises/A+ 2.3.3.3.1] */
6731 function (y) {
6732 if (resolved) return;
6733 resolved = true;
6734 /* [Promises/A+ 2.3.3.3.3] */
6735
6736 if (y === x)
6737 /* [Promises/A+ 3.6] */
6738 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6739 },
6740 /* rejectPromise */
6741
6742 /* [Promises/A+ 2.3.3.3.2] */
6743 function (r) {
6744 if (resolved) return;
6745 resolved = true;
6746 /* [Promises/A+ 2.3.3.3.3] */
6747
6748 promise.reject(r);
6749 });
6750 } catch (e) {
6751 if (!resolved)
6752 /* [Promises/A+ 2.3.3.3.3] */
6753 promise.reject(e);
6754 /* [Promises/A+ 2.3.3.3.4] */
6755 }
6756
6757 return;
6758 }
6759 /* handle other values */
6760
6761
6762 promise.fulfill(x);
6763 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6764 }; // so we always have Promise.all()
6765
6766
6767 api.all = function (ps) {
6768 return new api(function (resolveAll, rejectAll) {
6769 var vals = new Array(ps.length);
6770 var doneCount = 0;
6771
6772 var fulfill = function fulfill(i, val) {
6773 vals[i] = val;
6774 doneCount++;
6775
6776 if (doneCount === ps.length) {
6777 resolveAll(vals);
6778 }
6779 };
6780
6781 for (var i = 0; i < ps.length; i++) {
6782 (function (i) {
6783 var p = ps[i];
6784 var isPromise = p != null && p.then != null;
6785
6786 if (isPromise) {
6787 p.then(function (val) {
6788 fulfill(i, val);
6789 }, function (err) {
6790 rejectAll(err);
6791 });
6792 } else {
6793 var val = p;
6794 fulfill(i, val);
6795 }
6796 })(i);
6797 }
6798 });
6799 };
6800
6801 api.resolve = function (val) {
6802 return new api(function (resolve, reject) {
6803 resolve(val);
6804 });
6805 };
6806
6807 api.reject = function (val) {
6808 return new api(function (resolve, reject) {
6809 reject(val);
6810 });
6811 };
6812
6813 var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6814
6815 var Animation = function Animation(target, opts, opts2) {
6816 var isCore = core(target);
6817 var isEle = !isCore;
6818
6819 var _p = this._private = extend({
6820 duration: 1000
6821 }, opts, opts2);
6822
6823 _p.target = target;
6824 _p.style = _p.style || _p.css;
6825 _p.started = false;
6826 _p.playing = false;
6827 _p.hooked = false;
6828 _p.applying = false;
6829 _p.progress = 0;
6830 _p.completes = [];
6831 _p.frames = [];
6832
6833 if (_p.complete && fn(_p.complete)) {
6834 _p.completes.push(_p.complete);
6835 }
6836
6837 if (isEle) {
6838 var pos = target.position();
6839 _p.startPosition = _p.startPosition || {
6840 x: pos.x,
6841 y: pos.y
6842 };
6843 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6844 }
6845
6846 if (isCore) {
6847 var pan = target.pan();
6848 _p.startPan = {
6849 x: pan.x,
6850 y: pan.y
6851 };
6852 _p.startZoom = target.zoom();
6853 } // for future timeline/animations impl
6854
6855
6856 this.length = 1;
6857 this[0] = this;
6858 };
6859
6860 var anifn = Animation.prototype;
6861 extend(anifn, {
6862 instanceString: function instanceString() {
6863 return 'animation';
6864 },
6865 hook: function hook() {
6866 var _p = this._private;
6867
6868 if (!_p.hooked) {
6869 // add to target's animation queue
6870 var q;
6871 var tAni = _p.target._private.animation;
6872
6873 if (_p.queue) {
6874 q = tAni.queue;
6875 } else {
6876 q = tAni.current;
6877 }
6878
6879 q.push(this); // add to the animation loop pool
6880
6881 if (elementOrCollection(_p.target)) {
6882 _p.target.cy().addToAnimationPool(_p.target);
6883 }
6884
6885 _p.hooked = true;
6886 }
6887
6888 return this;
6889 },
6890 play: function play() {
6891 var _p = this._private; // autorewind
6892
6893 if (_p.progress === 1) {
6894 _p.progress = 0;
6895 }
6896
6897 _p.playing = true;
6898 _p.started = false; // needs to be started by animation loop
6899
6900 _p.stopped = false;
6901 this.hook(); // the animation loop will start the animation...
6902
6903 return this;
6904 },
6905 playing: function playing() {
6906 return this._private.playing;
6907 },
6908 apply: function apply() {
6909 var _p = this._private;
6910 _p.applying = true;
6911 _p.started = false; // needs to be started by animation loop
6912
6913 _p.stopped = false;
6914 this.hook(); // the animation loop will apply the animation at this progress
6915
6916 return this;
6917 },
6918 applying: function applying() {
6919 return this._private.applying;
6920 },
6921 pause: function pause() {
6922 var _p = this._private;
6923 _p.playing = false;
6924 _p.started = false;
6925 return this;
6926 },
6927 stop: function stop() {
6928 var _p = this._private;
6929 _p.playing = false;
6930 _p.started = false;
6931 _p.stopped = true; // to be removed from animation queues
6932
6933 return this;
6934 },
6935 rewind: function rewind() {
6936 return this.progress(0);
6937 },
6938 fastforward: function fastforward() {
6939 return this.progress(1);
6940 },
6941 time: function time(t) {
6942 var _p = this._private;
6943
6944 if (t === undefined) {
6945 return _p.progress * _p.duration;
6946 } else {
6947 return this.progress(t / _p.duration);
6948 }
6949 },
6950 progress: function progress(p) {
6951 var _p = this._private;
6952 var wasPlaying = _p.playing;
6953
6954 if (p === undefined) {
6955 return _p.progress;
6956 } else {
6957 if (wasPlaying) {
6958 this.pause();
6959 }
6960
6961 _p.progress = p;
6962 _p.started = false;
6963
6964 if (wasPlaying) {
6965 this.play();
6966 }
6967 }
6968
6969 return this;
6970 },
6971 completed: function completed() {
6972 return this._private.progress === 1;
6973 },
6974 reverse: function reverse() {
6975 var _p = this._private;
6976 var wasPlaying = _p.playing;
6977
6978 if (wasPlaying) {
6979 this.pause();
6980 }
6981
6982 _p.progress = 1 - _p.progress;
6983 _p.started = false;
6984
6985 var swap = function swap(a, b) {
6986 var _pa = _p[a];
6987
6988 if (_pa == null) {
6989 return;
6990 }
6991
6992 _p[a] = _p[b];
6993 _p[b] = _pa;
6994 };
6995
6996 swap('zoom', 'startZoom');
6997 swap('pan', 'startPan');
6998 swap('position', 'startPosition'); // swap styles
6999
7000 if (_p.style) {
7001 for (var i = 0; i < _p.style.length; i++) {
7002 var prop = _p.style[i];
7003 var name = prop.name;
7004 var startStyleProp = _p.startStyle[name];
7005 _p.startStyle[name] = prop;
7006 _p.style[i] = startStyleProp;
7007 }
7008 }
7009
7010 if (wasPlaying) {
7011 this.play();
7012 }
7013
7014 return this;
7015 },
7016 promise: function promise(type) {
7017 var _p = this._private;
7018 var arr;
7019
7020 switch (type) {
7021 case 'frame':
7022 arr = _p.frames;
7023 break;
7024
7025 default:
7026 case 'complete':
7027 case 'completed':
7028 arr = _p.completes;
7029 }
7030
7031 return new Promise$1(function (resolve, reject) {
7032 arr.push(function () {
7033 resolve();
7034 });
7035 });
7036 }
7037 });
7038 anifn.complete = anifn.completed;
7039 anifn.run = anifn.play;
7040 anifn.running = anifn.playing;
7041
7042 var define = {
7043 animated: function animated() {
7044 return function animatedImpl() {
7045 var self = this;
7046 var selfIsArrayLike = self.length !== undefined;
7047 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7048
7049 var cy = this._private.cy || this;
7050
7051 if (!cy.styleEnabled()) {
7052 return false;
7053 }
7054
7055 var ele = all[0];
7056
7057 if (ele) {
7058 return ele._private.animation.current.length > 0;
7059 }
7060 };
7061 },
7062 // animated
7063 clearQueue: function clearQueue() {
7064 return function clearQueueImpl() {
7065 var self = this;
7066 var selfIsArrayLike = self.length !== undefined;
7067 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7068
7069 var cy = this._private.cy || this;
7070
7071 if (!cy.styleEnabled()) {
7072 return this;
7073 }
7074
7075 for (var i = 0; i < all.length; i++) {
7076 var ele = all[i];
7077 ele._private.animation.queue = [];
7078 }
7079
7080 return this;
7081 };
7082 },
7083 // clearQueue
7084 delay: function delay() {
7085 return function delayImpl(time, complete) {
7086 var cy = this._private.cy || this;
7087
7088 if (!cy.styleEnabled()) {
7089 return this;
7090 }
7091
7092 return this.animate({
7093 delay: time,
7094 duration: time,
7095 complete: complete
7096 });
7097 };
7098 },
7099 // delay
7100 delayAnimation: function delayAnimation() {
7101 return function delayAnimationImpl(time, complete) {
7102 var cy = this._private.cy || this;
7103
7104 if (!cy.styleEnabled()) {
7105 return this;
7106 }
7107
7108 return this.animation({
7109 delay: time,
7110 duration: time,
7111 complete: complete
7112 });
7113 };
7114 },
7115 // delay
7116 animation: function animation() {
7117 return function animationImpl(properties, params) {
7118 var self = this;
7119 var selfIsArrayLike = self.length !== undefined;
7120 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7121
7122 var cy = this._private.cy || this;
7123 var isCore = !selfIsArrayLike;
7124 var isEles = !isCore;
7125
7126 if (!cy.styleEnabled()) {
7127 return this;
7128 }
7129
7130 var style = cy.style();
7131 properties = extend({}, properties, params);
7132 var propertiesEmpty = Object.keys(properties).length === 0;
7133
7134 if (propertiesEmpty) {
7135 return new Animation(all[0], properties); // nothing to animate
7136 }
7137
7138 if (properties.duration === undefined) {
7139 properties.duration = 400;
7140 }
7141
7142 switch (properties.duration) {
7143 case 'slow':
7144 properties.duration = 600;
7145 break;
7146
7147 case 'fast':
7148 properties.duration = 200;
7149 break;
7150 }
7151
7152 if (isEles) {
7153 properties.style = style.getPropsList(properties.style || properties.css);
7154 properties.css = undefined;
7155 }
7156
7157 if (isEles && properties.renderedPosition != null) {
7158 var rpos = properties.renderedPosition;
7159 var pan = cy.pan();
7160 var zoom = cy.zoom();
7161 properties.position = renderedToModelPosition(rpos, zoom, pan);
7162 } // override pan w/ panBy if set
7163
7164
7165 if (isCore && properties.panBy != null) {
7166 var panBy = properties.panBy;
7167 var cyPan = cy.pan();
7168 properties.pan = {
7169 x: cyPan.x + panBy.x,
7170 y: cyPan.y + panBy.y
7171 };
7172 } // override pan w/ center if set
7173
7174
7175 var center = properties.center || properties.centre;
7176
7177 if (isCore && center != null) {
7178 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
7179
7180 if (centerPan != null) {
7181 properties.pan = centerPan;
7182 }
7183 } // override pan & zoom w/ fit if set
7184
7185
7186 if (isCore && properties.fit != null) {
7187 var fit = properties.fit;
7188 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
7189
7190 if (fitVp != null) {
7191 properties.pan = fitVp.pan;
7192 properties.zoom = fitVp.zoom;
7193 }
7194 } // override zoom (& potentially pan) w/ zoom obj if set
7195
7196
7197 if (isCore && plainObject(properties.zoom)) {
7198 var vp = cy.getZoomedViewport(properties.zoom);
7199
7200 if (vp != null) {
7201 if (vp.zoomed) {
7202 properties.zoom = vp.zoom;
7203 }
7204
7205 if (vp.panned) {
7206 properties.pan = vp.pan;
7207 }
7208 } else {
7209 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
7210 }
7211 }
7212
7213 return new Animation(all[0], properties);
7214 };
7215 },
7216 // animate
7217 animate: function animate() {
7218 return function animateImpl(properties, params) {
7219 var self = this;
7220 var selfIsArrayLike = self.length !== undefined;
7221 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7222
7223 var cy = this._private.cy || this;
7224
7225 if (!cy.styleEnabled()) {
7226 return this;
7227 }
7228
7229 if (params) {
7230 properties = extend({}, properties, params);
7231 } // manually hook and run the animation
7232
7233
7234 for (var i = 0; i < all.length; i++) {
7235 var ele = all[i];
7236 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
7237 var ani = ele.animation(properties, queue ? {
7238 queue: true
7239 } : undefined);
7240 ani.play();
7241 }
7242
7243 return this; // chaining
7244 };
7245 },
7246 // animate
7247 stop: function stop() {
7248 return function stopImpl(clearQueue, jumpToEnd) {
7249 var self = this;
7250 var selfIsArrayLike = self.length !== undefined;
7251 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7252
7253 var cy = this._private.cy || this;
7254
7255 if (!cy.styleEnabled()) {
7256 return this;
7257 }
7258
7259 for (var i = 0; i < all.length; i++) {
7260 var ele = all[i];
7261 var _p = ele._private;
7262 var anis = _p.animation.current;
7263
7264 for (var j = 0; j < anis.length; j++) {
7265 var ani = anis[j];
7266 var ani_p = ani._private;
7267
7268 if (jumpToEnd) {
7269 // next iteration of the animation loop, the animation
7270 // will go straight to the end and be removed
7271 ani_p.duration = 0;
7272 }
7273 } // clear the queue of future animations
7274
7275
7276 if (clearQueue) {
7277 _p.animation.queue = [];
7278 }
7279
7280 if (!jumpToEnd) {
7281 _p.animation.current = [];
7282 }
7283 } // we have to notify (the animation loop doesn't do it for us on `stop`)
7284
7285
7286 cy.notify('draw');
7287 return this;
7288 };
7289 } // stop
7290
7291 }; // define
7292
7293 var define$1 = {
7294 // access data field
7295 data: function data(params) {
7296 var defaults = {
7297 field: 'data',
7298 bindingEvent: 'data',
7299 allowBinding: false,
7300 allowSetting: false,
7301 allowGetting: false,
7302 settingEvent: 'data',
7303 settingTriggersEvent: false,
7304 triggerFnName: 'trigger',
7305 immutableKeys: {},
7306 // key => true if immutable
7307 updateStyle: false,
7308 beforeGet: function beforeGet(self) {},
7309 beforeSet: function beforeSet(self, obj) {},
7310 onSet: function onSet(self) {},
7311 canSet: function canSet(self) {
7312 return true;
7313 }
7314 };
7315 params = extend({}, defaults, params);
7316 return function dataImpl(name, value) {
7317 var p = params;
7318 var self = this;
7319 var selfIsArrayLike = self.length !== undefined;
7320 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7321
7322 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
7323
7324 if (string(name)) {
7325 // set or get property
7326 // .data('foo')
7327 if (p.allowGetting && value === undefined) {
7328 // get
7329 var ret;
7330
7331 if (single) {
7332 p.beforeGet(single);
7333 ret = single._private[p.field][name];
7334 }
7335
7336 return ret; // .data('foo', 'bar')
7337 } else if (p.allowSetting && value !== undefined) {
7338 // set
7339 var valid = !p.immutableKeys[name];
7340
7341 if (valid) {
7342 var change = _defineProperty({}, name, value);
7343
7344 p.beforeSet(self, change);
7345
7346 for (var i = 0, l = all.length; i < l; i++) {
7347 var ele = all[i];
7348
7349 if (p.canSet(ele)) {
7350 ele._private[p.field][name] = value;
7351 }
7352 } // update mappers if asked
7353
7354
7355 if (p.updateStyle) {
7356 self.updateStyle();
7357 } // call onSet callback
7358
7359
7360 p.onSet(self);
7361
7362 if (p.settingTriggersEvent) {
7363 self[p.triggerFnName](p.settingEvent);
7364 }
7365 }
7366 } // .data({ 'foo': 'bar' })
7367
7368 } else if (p.allowSetting && plainObject(name)) {
7369 // extend
7370 var obj = name;
7371 var k, v;
7372 var keys = Object.keys(obj);
7373 p.beforeSet(self, obj);
7374
7375 for (var _i = 0; _i < keys.length; _i++) {
7376 k = keys[_i];
7377 v = obj[k];
7378
7379 var _valid = !p.immutableKeys[k];
7380
7381 if (_valid) {
7382 for (var j = 0; j < all.length; j++) {
7383 var _ele = all[j];
7384
7385 if (p.canSet(_ele)) {
7386 _ele._private[p.field][k] = v;
7387 }
7388 }
7389 }
7390 } // update mappers if asked
7391
7392
7393 if (p.updateStyle) {
7394 self.updateStyle();
7395 } // call onSet callback
7396
7397
7398 p.onSet(self);
7399
7400 if (p.settingTriggersEvent) {
7401 self[p.triggerFnName](p.settingEvent);
7402 } // .data(function(){ ... })
7403
7404 } else if (p.allowBinding && fn(name)) {
7405 // bind to event
7406 var fn$1 = name;
7407 self.on(p.bindingEvent, fn$1); // .data()
7408 } else if (p.allowGetting && name === undefined) {
7409 // get whole object
7410 var _ret;
7411
7412 if (single) {
7413 p.beforeGet(single);
7414 _ret = single._private[p.field];
7415 }
7416
7417 return _ret;
7418 }
7419
7420 return self; // maintain chainability
7421 }; // function
7422 },
7423 // data
7424 // remove data field
7425 removeData: function removeData(params) {
7426 var defaults = {
7427 field: 'data',
7428 event: 'data',
7429 triggerFnName: 'trigger',
7430 triggerEvent: false,
7431 immutableKeys: {} // key => true if immutable
7432
7433 };
7434 params = extend({}, defaults, params);
7435 return function removeDataImpl(names) {
7436 var p = params;
7437 var self = this;
7438 var selfIsArrayLike = self.length !== undefined;
7439 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7440 // .removeData('foo bar')
7441
7442 if (string(names)) {
7443 // then get the list of keys, and delete them
7444 var keys = names.split(/\s+/);
7445 var l = keys.length;
7446
7447 for (var i = 0; i < l; i++) {
7448 // delete each non-empty key
7449 var key = keys[i];
7450
7451 if (emptyString(key)) {
7452 continue;
7453 }
7454
7455 var valid = !p.immutableKeys[key]; // not valid if immutable
7456
7457 if (valid) {
7458 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
7459 all[i_a]._private[p.field][key] = undefined;
7460 }
7461 }
7462 }
7463
7464 if (p.triggerEvent) {
7465 self[p.triggerFnName](p.event);
7466 } // .removeData()
7467
7468 } else if (names === undefined) {
7469 // then delete all keys
7470 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
7471 var _privateFields = all[_i_a]._private[p.field];
7472
7473 var _keys = Object.keys(_privateFields);
7474
7475 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
7476 var _key = _keys[_i2];
7477 var validKeyToDelete = !p.immutableKeys[_key];
7478
7479 if (validKeyToDelete) {
7480 _privateFields[_key] = undefined;
7481 }
7482 }
7483 }
7484
7485 if (p.triggerEvent) {
7486 self[p.triggerFnName](p.event);
7487 }
7488 }
7489
7490 return self; // maintain chaining
7491 }; // function
7492 } // removeData
7493
7494 }; // define
7495
7496 var define$2 = {
7497 eventAliasesOn: function eventAliasesOn(proto) {
7498 var p = proto;
7499 p.addListener = p.listen = p.bind = p.on;
7500 p.unlisten = p.unbind = p.off = p.removeListener;
7501 p.trigger = p.emit; // this is just a wrapper alias of .on()
7502
7503 p.pon = p.promiseOn = function (events, selector) {
7504 var self = this;
7505 var args = Array.prototype.slice.call(arguments, 0);
7506 return new Promise$1(function (resolve, reject) {
7507 var callback = function callback(e) {
7508 self.off.apply(self, offArgs);
7509 resolve(e);
7510 };
7511
7512 var onArgs = args.concat([callback]);
7513 var offArgs = onArgs.concat([]);
7514 self.on.apply(self, onArgs);
7515 });
7516 };
7517 }
7518 }; // define
7519
7520 // use this module to cherry pick functions into your prototype
7521 var define$3 = {};
7522 [define, define$1, define$2].forEach(function (m) {
7523 extend(define$3, m);
7524 });
7525
7526 var elesfn$d = {
7527 animate: define$3.animate(),
7528 animation: define$3.animation(),
7529 animated: define$3.animated(),
7530 clearQueue: define$3.clearQueue(),
7531 delay: define$3.delay(),
7532 delayAnimation: define$3.delayAnimation(),
7533 stop: define$3.stop()
7534 };
7535
7536 var elesfn$e = {
7537 classes: function classes(_classes) {
7538 var self = this;
7539
7540 if (_classes === undefined) {
7541 var ret = [];
7542
7543 self[0]._private.classes.forEach(function (cls) {
7544 return ret.push(cls);
7545 });
7546
7547 return ret;
7548 } else if (!array(_classes)) {
7549 // extract classes from string
7550 _classes = (_classes || '').match(/\S+/g) || [];
7551 }
7552
7553 var changed = [];
7554 var classesSet = new Set$1(_classes); // check and update each ele
7555
7556 for (var j = 0; j < self.length; j++) {
7557 var ele = self[j];
7558 var _p = ele._private;
7559 var eleClasses = _p.classes;
7560 var changedEle = false; // check if ele has all of the passed classes
7561
7562 for (var i = 0; i < _classes.length; i++) {
7563 var cls = _classes[i];
7564 var eleHasClass = eleClasses.has(cls);
7565
7566 if (!eleHasClass) {
7567 changedEle = true;
7568 break;
7569 }
7570 } // check if ele has classes outside of those passed
7571
7572
7573 if (!changedEle) {
7574 changedEle = eleClasses.size !== _classes.length;
7575 }
7576
7577 if (changedEle) {
7578 _p.classes = classesSet;
7579 changed.push(ele);
7580 }
7581 } // trigger update style on those eles that had class changes
7582
7583
7584 if (changed.length > 0) {
7585 this.spawn(changed).updateStyle().emit('class');
7586 }
7587
7588 return self;
7589 },
7590 addClass: function addClass(classes) {
7591 return this.toggleClass(classes, true);
7592 },
7593 hasClass: function hasClass(className) {
7594 var ele = this[0];
7595 return ele != null && ele._private.classes.has(className);
7596 },
7597 toggleClass: function toggleClass(classes, toggle) {
7598 if (!array(classes)) {
7599 // extract classes from string
7600 classes = classes.match(/\S+/g) || [];
7601 }
7602
7603 var self = this;
7604 var toggleUndefd = toggle === undefined;
7605 var changed = []; // eles who had classes changed
7606
7607 for (var i = 0, il = self.length; i < il; i++) {
7608 var ele = self[i];
7609 var eleClasses = ele._private.classes;
7610 var changedEle = false;
7611
7612 for (var j = 0; j < classes.length; j++) {
7613 var cls = classes[j];
7614 var hasClass = eleClasses.has(cls);
7615 var changedNow = false;
7616
7617 if (toggle || toggleUndefd && !hasClass) {
7618 eleClasses.add(cls);
7619 changedNow = true;
7620 } else if (!toggle || toggleUndefd && hasClass) {
7621 eleClasses["delete"](cls);
7622 changedNow = true;
7623 }
7624
7625 if (!changedEle && changedNow) {
7626 changed.push(ele);
7627 changedEle = true;
7628 }
7629 } // for j classes
7630
7631 } // for i eles
7632 // trigger update style on those eles that had class changes
7633
7634
7635 if (changed.length > 0) {
7636 this.spawn(changed).updateStyle().emit('class');
7637 }
7638
7639 return self;
7640 },
7641 removeClass: function removeClass(classes) {
7642 return this.toggleClass(classes, false);
7643 },
7644 flashClass: function flashClass(classes, duration) {
7645 var self = this;
7646
7647 if (duration == null) {
7648 duration = 250;
7649 } else if (duration === 0) {
7650 return self; // nothing to do really
7651 }
7652
7653 self.addClass(classes);
7654 setTimeout(function () {
7655 self.removeClass(classes);
7656 }, duration);
7657 return self;
7658 }
7659 };
7660 elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
7661
7662 var tokens = {
7663 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
7664 // chars we need to escape in let names, etc
7665 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
7666 // binary comparison op (used in data selectors)
7667 boolOp: '\\?|\\!|\\^',
7668 // boolean (unary) operators (used in data selectors)
7669 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
7670 // string literals (used in data selectors) -- doublequotes | singlequotes
7671 number: number$1,
7672 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
7673 meta: 'degree|indegree|outdegree',
7674 // allowed metadata fields (i.e. allowed functions to use from Collection)
7675 separator: '\\s*,\\s*',
7676 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
7677 descendant: '\\s+',
7678 child: '\\s+>\\s+',
7679 subject: '\\$',
7680 group: 'node|edge|\\*',
7681 directedEdge: '\\s+->\\s+',
7682 undirectedEdge: '\\s+<->\\s+'
7683 };
7684 tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
7685
7686 tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7687
7688 tokens.className = tokens.variable; // a class name (follows variable conventions)
7689
7690 tokens.id = tokens.variable; // an element id (follows variable conventions)
7691
7692 (function () {
7693 var ops, op, i; // add @ variants to comparatorOp
7694
7695 ops = tokens.comparatorOp.split('|');
7696
7697 for (i = 0; i < ops.length; i++) {
7698 op = ops[i];
7699 tokens.comparatorOp += '|@' + op;
7700 } // add ! variants to comparatorOp
7701
7702
7703 ops = tokens.comparatorOp.split('|');
7704
7705 for (i = 0; i < ops.length; i++) {
7706 op = ops[i];
7707
7708 if (op.indexOf('!') >= 0) {
7709 continue;
7710 } // skip ops that explicitly contain !
7711
7712
7713 if (op === '=') {
7714 continue;
7715 } // skip = b/c != is explicitly defined
7716
7717
7718 tokens.comparatorOp += '|\\!' + op;
7719 }
7720 })();
7721
7722 /**
7723 * Make a new query object
7724 *
7725 * @prop type {Type} The type enum (int) of the query
7726 * @prop checks List of checks to make against an ele to test for a match
7727 */
7728 var newQuery = function newQuery() {
7729 return {
7730 checks: []
7731 };
7732 };
7733
7734 /**
7735 * A check type enum-like object. Uses integer values for fast match() lookup.
7736 * The ordering does not matter as long as the ints are unique.
7737 */
7738 var Type = {
7739 /** E.g. node */
7740 GROUP: 0,
7741
7742 /** A collection of elements */
7743 COLLECTION: 1,
7744
7745 /** A filter(ele) function */
7746 FILTER: 2,
7747
7748 /** E.g. [foo > 1] */
7749 DATA_COMPARE: 3,
7750
7751 /** E.g. [foo] */
7752 DATA_EXIST: 4,
7753
7754 /** E.g. [?foo] */
7755 DATA_BOOL: 5,
7756
7757 /** E.g. [[degree > 2]] */
7758 META_COMPARE: 6,
7759
7760 /** E.g. :selected */
7761 STATE: 7,
7762
7763 /** E.g. #foo */
7764 ID: 8,
7765
7766 /** E.g. .foo */
7767 CLASS: 9,
7768
7769 /** E.g. #foo <-> #bar */
7770 UNDIRECTED_EDGE: 10,
7771
7772 /** E.g. #foo -> #bar */
7773 DIRECTED_EDGE: 11,
7774
7775 /** E.g. $#foo -> #bar */
7776 NODE_SOURCE: 12,
7777
7778 /** E.g. #foo -> $#bar */
7779 NODE_TARGET: 13,
7780
7781 /** E.g. $#foo <-> #bar */
7782 NODE_NEIGHBOR: 14,
7783
7784 /** E.g. #foo > #bar */
7785 CHILD: 15,
7786
7787 /** E.g. #foo #bar */
7788 DESCENDANT: 16,
7789
7790 /** E.g. $#foo > #bar */
7791 PARENT: 17,
7792
7793 /** E.g. $#foo #bar */
7794 ANCESTOR: 18,
7795
7796 /** E.g. #foo > $bar > #baz */
7797 COMPOUND_SPLIT: 19,
7798
7799 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7800 TRUE: 20
7801 };
7802
7803 var stateSelectors = [{
7804 selector: ':selected',
7805 matches: function matches(ele) {
7806 return ele.selected();
7807 }
7808 }, {
7809 selector: ':unselected',
7810 matches: function matches(ele) {
7811 return !ele.selected();
7812 }
7813 }, {
7814 selector: ':selectable',
7815 matches: function matches(ele) {
7816 return ele.selectable();
7817 }
7818 }, {
7819 selector: ':unselectable',
7820 matches: function matches(ele) {
7821 return !ele.selectable();
7822 }
7823 }, {
7824 selector: ':locked',
7825 matches: function matches(ele) {
7826 return ele.locked();
7827 }
7828 }, {
7829 selector: ':unlocked',
7830 matches: function matches(ele) {
7831 return !ele.locked();
7832 }
7833 }, {
7834 selector: ':visible',
7835 matches: function matches(ele) {
7836 return ele.visible();
7837 }
7838 }, {
7839 selector: ':hidden',
7840 matches: function matches(ele) {
7841 return !ele.visible();
7842 }
7843 }, {
7844 selector: ':transparent',
7845 matches: function matches(ele) {
7846 return ele.transparent();
7847 }
7848 }, {
7849 selector: ':grabbed',
7850 matches: function matches(ele) {
7851 return ele.grabbed();
7852 }
7853 }, {
7854 selector: ':free',
7855 matches: function matches(ele) {
7856 return !ele.grabbed();
7857 }
7858 }, {
7859 selector: ':removed',
7860 matches: function matches(ele) {
7861 return ele.removed();
7862 }
7863 }, {
7864 selector: ':inside',
7865 matches: function matches(ele) {
7866 return !ele.removed();
7867 }
7868 }, {
7869 selector: ':grabbable',
7870 matches: function matches(ele) {
7871 return ele.grabbable();
7872 }
7873 }, {
7874 selector: ':ungrabbable',
7875 matches: function matches(ele) {
7876 return !ele.grabbable();
7877 }
7878 }, {
7879 selector: ':animated',
7880 matches: function matches(ele) {
7881 return ele.animated();
7882 }
7883 }, {
7884 selector: ':unanimated',
7885 matches: function matches(ele) {
7886 return !ele.animated();
7887 }
7888 }, {
7889 selector: ':parent',
7890 matches: function matches(ele) {
7891 return ele.isParent();
7892 }
7893 }, {
7894 selector: ':childless',
7895 matches: function matches(ele) {
7896 return ele.isChildless();
7897 }
7898 }, {
7899 selector: ':child',
7900 matches: function matches(ele) {
7901 return ele.isChild();
7902 }
7903 }, {
7904 selector: ':orphan',
7905 matches: function matches(ele) {
7906 return ele.isOrphan();
7907 }
7908 }, {
7909 selector: ':nonorphan',
7910 matches: function matches(ele) {
7911 return ele.isChild();
7912 }
7913 }, {
7914 selector: ':compound',
7915 matches: function matches(ele) {
7916 if (ele.isNode()) {
7917 return ele.isParent();
7918 } else {
7919 return ele.source().isParent() || ele.target().isParent();
7920 }
7921 }
7922 }, {
7923 selector: ':loop',
7924 matches: function matches(ele) {
7925 return ele.isLoop();
7926 }
7927 }, {
7928 selector: ':simple',
7929 matches: function matches(ele) {
7930 return ele.isSimple();
7931 }
7932 }, {
7933 selector: ':active',
7934 matches: function matches(ele) {
7935 return ele.active();
7936 }
7937 }, {
7938 selector: ':inactive',
7939 matches: function matches(ele) {
7940 return !ele.active();
7941 }
7942 }, {
7943 selector: ':backgrounding',
7944 matches: function matches(ele) {
7945 return ele.backgrounding();
7946 }
7947 }, {
7948 selector: ':nonbackgrounding',
7949 matches: function matches(ele) {
7950 return !ele.backgrounding();
7951 }
7952 }].sort(function (a, b) {
7953 // n.b. selectors that are starting substrings of others must have the longer ones first
7954 return descending(a.selector, b.selector);
7955 });
7956
7957 var lookup = function () {
7958 var selToFn = {};
7959 var s;
7960
7961 for (var i = 0; i < stateSelectors.length; i++) {
7962 s = stateSelectors[i];
7963 selToFn[s.selector] = s.matches;
7964 }
7965
7966 return selToFn;
7967 }();
7968
7969 var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7970 return lookup[sel](ele);
7971 };
7972 var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7973 return s.selector;
7974 }).join('|') + ')';
7975
7976 // so that values get compared properly in Selector.filter()
7977
7978 var cleanMetaChars = function cleanMetaChars(str) {
7979 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7980 return $1;
7981 });
7982 };
7983
7984 var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7985 selector[selector.length - 1] = replacementQuery;
7986 }; // NOTE: add new expression syntax here to have it recognised by the parser;
7987 // - a query contains all adjacent (i.e. no separator in between) expressions;
7988 // - the current query is stored in selector[i]
7989 // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7990
7991
7992 var exprs = [{
7993 name: 'group',
7994 // just used for identifying when debugging
7995 query: true,
7996 regex: '(' + tokens.group + ')',
7997 populate: function populate(selector, query, _ref) {
7998 var _ref2 = _slicedToArray(_ref, 1),
7999 group = _ref2[0];
8000
8001 query.checks.push({
8002 type: Type.GROUP,
8003 value: group === '*' ? group : group + 's'
8004 });
8005 }
8006 }, {
8007 name: 'state',
8008 query: true,
8009 regex: stateSelectorRegex,
8010 populate: function populate(selector, query, _ref3) {
8011 var _ref4 = _slicedToArray(_ref3, 1),
8012 state = _ref4[0];
8013
8014 query.checks.push({
8015 type: Type.STATE,
8016 value: state
8017 });
8018 }
8019 }, {
8020 name: 'id',
8021 query: true,
8022 regex: '\\#(' + tokens.id + ')',
8023 populate: function populate(selector, query, _ref5) {
8024 var _ref6 = _slicedToArray(_ref5, 1),
8025 id = _ref6[0];
8026
8027 query.checks.push({
8028 type: Type.ID,
8029 value: cleanMetaChars(id)
8030 });
8031 }
8032 }, {
8033 name: 'className',
8034 query: true,
8035 regex: '\\.(' + tokens.className + ')',
8036 populate: function populate(selector, query, _ref7) {
8037 var _ref8 = _slicedToArray(_ref7, 1),
8038 className = _ref8[0];
8039
8040 query.checks.push({
8041 type: Type.CLASS,
8042 value: cleanMetaChars(className)
8043 });
8044 }
8045 }, {
8046 name: 'dataExists',
8047 query: true,
8048 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
8049 populate: function populate(selector, query, _ref9) {
8050 var _ref10 = _slicedToArray(_ref9, 1),
8051 variable = _ref10[0];
8052
8053 query.checks.push({
8054 type: Type.DATA_EXIST,
8055 field: cleanMetaChars(variable)
8056 });
8057 }
8058 }, {
8059 name: 'dataCompare',
8060 query: true,
8061 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
8062 populate: function populate(selector, query, _ref11) {
8063 var _ref12 = _slicedToArray(_ref11, 3),
8064 variable = _ref12[0],
8065 comparatorOp = _ref12[1],
8066 value = _ref12[2];
8067
8068 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
8069
8070 if (valueIsString) {
8071 value = value.substring(1, value.length - 1);
8072 } else {
8073 value = parseFloat(value);
8074 }
8075
8076 query.checks.push({
8077 type: Type.DATA_COMPARE,
8078 field: cleanMetaChars(variable),
8079 operator: comparatorOp,
8080 value: value
8081 });
8082 }
8083 }, {
8084 name: 'dataBool',
8085 query: true,
8086 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
8087 populate: function populate(selector, query, _ref13) {
8088 var _ref14 = _slicedToArray(_ref13, 2),
8089 boolOp = _ref14[0],
8090 variable = _ref14[1];
8091
8092 query.checks.push({
8093 type: Type.DATA_BOOL,
8094 field: cleanMetaChars(variable),
8095 operator: boolOp
8096 });
8097 }
8098 }, {
8099 name: 'metaCompare',
8100 query: true,
8101 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
8102 populate: function populate(selector, query, _ref15) {
8103 var _ref16 = _slicedToArray(_ref15, 3),
8104 meta = _ref16[0],
8105 comparatorOp = _ref16[1],
8106 number = _ref16[2];
8107
8108 query.checks.push({
8109 type: Type.META_COMPARE,
8110 field: cleanMetaChars(meta),
8111 operator: comparatorOp,
8112 value: parseFloat(number)
8113 });
8114 }
8115 }, {
8116 name: 'nextQuery',
8117 separator: true,
8118 regex: tokens.separator,
8119 populate: function populate(selector, query) {
8120 var currentSubject = selector.currentSubject;
8121 var edgeCount = selector.edgeCount;
8122 var compoundCount = selector.compoundCount;
8123 var lastQ = selector[selector.length - 1];
8124
8125 if (currentSubject != null) {
8126 lastQ.subject = currentSubject;
8127 selector.currentSubject = null;
8128 }
8129
8130 lastQ.edgeCount = edgeCount;
8131 lastQ.compoundCount = compoundCount;
8132 selector.edgeCount = 0;
8133 selector.compoundCount = 0; // go on to next query
8134
8135 var nextQuery = selector[selector.length++] = newQuery();
8136 return nextQuery; // this is the new query to be filled by the following exprs
8137 }
8138 }, {
8139 name: 'directedEdge',
8140 separator: true,
8141 regex: tokens.directedEdge,
8142 populate: function populate(selector, query) {
8143 if (selector.currentSubject == null) {
8144 // undirected edge
8145 var edgeQuery = newQuery();
8146 var source = query;
8147 var target = newQuery();
8148 edgeQuery.checks.push({
8149 type: Type.DIRECTED_EDGE,
8150 source: source,
8151 target: target
8152 }); // the query in the selector should be the edge rather than the source
8153
8154 replaceLastQuery(selector, query, edgeQuery);
8155 selector.edgeCount++; // we're now populating the target query with expressions that follow
8156
8157 return target;
8158 } else {
8159 // source/target
8160 var srcTgtQ = newQuery();
8161 var _source = query;
8162
8163 var _target = newQuery();
8164
8165 srcTgtQ.checks.push({
8166 type: Type.NODE_SOURCE,
8167 source: _source,
8168 target: _target
8169 }); // the query in the selector should be the neighbourhood rather than the node
8170
8171 replaceLastQuery(selector, query, srcTgtQ);
8172 selector.edgeCount++;
8173 return _target; // now populating the target with the following expressions
8174 }
8175 }
8176 }, {
8177 name: 'undirectedEdge',
8178 separator: true,
8179 regex: tokens.undirectedEdge,
8180 populate: function populate(selector, query) {
8181 if (selector.currentSubject == null) {
8182 // undirected edge
8183 var edgeQuery = newQuery();
8184 var source = query;
8185 var target = newQuery();
8186 edgeQuery.checks.push({
8187 type: Type.UNDIRECTED_EDGE,
8188 nodes: [source, target]
8189 }); // the query in the selector should be the edge rather than the source
8190
8191 replaceLastQuery(selector, query, edgeQuery);
8192 selector.edgeCount++; // we're now populating the target query with expressions that follow
8193
8194 return target;
8195 } else {
8196 // neighbourhood
8197 var nhoodQ = newQuery();
8198 var node = query;
8199 var neighbor = newQuery();
8200 nhoodQ.checks.push({
8201 type: Type.NODE_NEIGHBOR,
8202 node: node,
8203 neighbor: neighbor
8204 }); // the query in the selector should be the neighbourhood rather than the node
8205
8206 replaceLastQuery(selector, query, nhoodQ);
8207 return neighbor; // now populating the neighbor with following expressions
8208 }
8209 }
8210 }, {
8211 name: 'child',
8212 separator: true,
8213 regex: tokens.child,
8214 populate: function populate(selector, query) {
8215 if (selector.currentSubject == null) {
8216 // default: child query
8217 var parentChildQuery = newQuery();
8218 var child = newQuery();
8219 var parent = selector[selector.length - 1];
8220 parentChildQuery.checks.push({
8221 type: Type.CHILD,
8222 parent: parent,
8223 child: child
8224 }); // the query in the selector should be the '>' itself
8225
8226 replaceLastQuery(selector, query, parentChildQuery);
8227 selector.compoundCount++; // we're now populating the child query with expressions that follow
8228
8229 return child;
8230 } else if (selector.currentSubject === query) {
8231 // compound split query
8232 var compound = newQuery();
8233 var left = selector[selector.length - 1];
8234 var right = newQuery();
8235 var subject = newQuery();
8236
8237 var _child = newQuery();
8238
8239 var _parent = newQuery(); // set up the root compound q
8240
8241
8242 compound.checks.push({
8243 type: Type.COMPOUND_SPLIT,
8244 left: left,
8245 right: right,
8246 subject: subject
8247 }); // populate the subject and replace the q at the old spot (within left) with TRUE
8248
8249 subject.checks = query.checks; // take the checks from the left
8250
8251 query.checks = [{
8252 type: Type.TRUE
8253 }]; // checks under left refs the subject implicitly
8254 // set up the right q
8255
8256 _parent.checks.push({
8257 type: Type.TRUE
8258 }); // parent implicitly refs the subject
8259
8260
8261 right.checks.push({
8262 type: Type.PARENT,
8263 // type is swapped on right side queries
8264 parent: _parent,
8265 child: _child // empty for now
8266
8267 });
8268 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
8269
8270 selector.currentSubject = subject;
8271 selector.compoundCount++;
8272 return _child; // now populating the right side's child
8273 } else {
8274 // parent query
8275 // info for parent query
8276 var _parent2 = newQuery();
8277
8278 var _child2 = newQuery();
8279
8280 var pcQChecks = [{
8281 type: Type.PARENT,
8282 parent: _parent2,
8283 child: _child2
8284 }]; // the parent-child query takes the place of the query previously being populated
8285
8286 _parent2.checks = query.checks; // the previous query contains the checks for the parent
8287
8288 query.checks = pcQChecks; // pc query takes over
8289
8290 selector.compoundCount++;
8291 return _child2; // we're now populating the child
8292 }
8293 }
8294 }, {
8295 name: 'descendant',
8296 separator: true,
8297 regex: tokens.descendant,
8298 populate: function populate(selector, query) {
8299 if (selector.currentSubject == null) {
8300 // default: descendant query
8301 var ancChQuery = newQuery();
8302 var descendant = newQuery();
8303 var ancestor = selector[selector.length - 1];
8304 ancChQuery.checks.push({
8305 type: Type.DESCENDANT,
8306 ancestor: ancestor,
8307 descendant: descendant
8308 }); // the query in the selector should be the '>' itself
8309
8310 replaceLastQuery(selector, query, ancChQuery);
8311 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
8312
8313 return descendant;
8314 } else if (selector.currentSubject === query) {
8315 // compound split query
8316 var compound = newQuery();
8317 var left = selector[selector.length - 1];
8318 var right = newQuery();
8319 var subject = newQuery();
8320
8321 var _descendant = newQuery();
8322
8323 var _ancestor = newQuery(); // set up the root compound q
8324
8325
8326 compound.checks.push({
8327 type: Type.COMPOUND_SPLIT,
8328 left: left,
8329 right: right,
8330 subject: subject
8331 }); // populate the subject and replace the q at the old spot (within left) with TRUE
8332
8333 subject.checks = query.checks; // take the checks from the left
8334
8335 query.checks = [{
8336 type: Type.TRUE
8337 }]; // checks under left refs the subject implicitly
8338 // set up the right q
8339
8340 _ancestor.checks.push({
8341 type: Type.TRUE
8342 }); // ancestor implicitly refs the subject
8343
8344
8345 right.checks.push({
8346 type: Type.ANCESTOR,
8347 // type is swapped on right side queries
8348 ancestor: _ancestor,
8349 descendant: _descendant // empty for now
8350
8351 });
8352 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
8353
8354 selector.currentSubject = subject;
8355 selector.compoundCount++;
8356 return _descendant; // now populating the right side's descendant
8357 } else {
8358 // ancestor query
8359 // info for parent query
8360 var _ancestor2 = newQuery();
8361
8362 var _descendant2 = newQuery();
8363
8364 var adQChecks = [{
8365 type: Type.ANCESTOR,
8366 ancestor: _ancestor2,
8367 descendant: _descendant2
8368 }]; // the parent-child query takes the place of the query previously being populated
8369
8370 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
8371
8372 query.checks = adQChecks; // pc query takes over
8373
8374 selector.compoundCount++;
8375 return _descendant2; // we're now populating the child
8376 }
8377 }
8378 }, {
8379 name: 'subject',
8380 modifier: true,
8381 regex: tokens.subject,
8382 populate: function populate(selector, query) {
8383 if (selector.currentSubject != null && selector.currentSubject !== query) {
8384 warn('Redefinition of subject in selector `' + selector.toString() + '`');
8385 return false;
8386 }
8387
8388 selector.currentSubject = query;
8389 var topQ = selector[selector.length - 1];
8390 var topChk = topQ.checks[0];
8391 var topType = topChk == null ? null : topChk.type;
8392
8393 if (topType === Type.DIRECTED_EDGE) {
8394 // directed edge with subject on the target
8395 // change to target node check
8396 topChk.type = Type.NODE_TARGET;
8397 } else if (topType === Type.UNDIRECTED_EDGE) {
8398 // undirected edge with subject on the second node
8399 // change to neighbor check
8400 topChk.type = Type.NODE_NEIGHBOR;
8401 topChk.node = topChk.nodes[1]; // second node is subject
8402
8403 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
8404
8405 topChk.nodes = null;
8406 }
8407 }
8408 }];
8409 exprs.forEach(function (e) {
8410 return e.regexObj = new RegExp('^' + e.regex);
8411 });
8412
8413 /**
8414 * Of all the expressions, find the first match in the remaining text.
8415 * @param {string} remaining The remaining text to parse
8416 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
8417 */
8418
8419 var consumeExpr = function consumeExpr(remaining) {
8420 var expr;
8421 var match;
8422 var name;
8423
8424 for (var j = 0; j < exprs.length; j++) {
8425 var e = exprs[j];
8426 var n = e.name;
8427 var m = remaining.match(e.regexObj);
8428
8429 if (m != null) {
8430 match = m;
8431 expr = e;
8432 name = n;
8433 var consumed = m[0];
8434 remaining = remaining.substring(consumed.length);
8435 break; // we've consumed one expr, so we can return now
8436 }
8437 }
8438
8439 return {
8440 expr: expr,
8441 match: match,
8442 name: name,
8443 remaining: remaining
8444 };
8445 };
8446 /**
8447 * Consume all the leading whitespace
8448 * @param {string} remaining The text to consume
8449 * @returns The text with the leading whitespace removed
8450 */
8451
8452
8453 var consumeWhitespace = function consumeWhitespace(remaining) {
8454 var match = remaining.match(/^\s+/);
8455
8456 if (match) {
8457 var consumed = match[0];
8458 remaining = remaining.substring(consumed.length);
8459 }
8460
8461 return remaining;
8462 };
8463 /**
8464 * Parse the string and store the parsed representation in the Selector.
8465 * @param {string} selector The selector string
8466 * @returns `true` if the selector was successfully parsed, `false` otherwise
8467 */
8468
8469
8470 var parse = function parse(selector) {
8471 var self = this;
8472 var remaining = self.inputText = selector;
8473 var currentQuery = self[0] = newQuery();
8474 self.length = 1;
8475 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
8476
8477 for (;;) {
8478 var exprInfo = consumeExpr(remaining);
8479
8480 if (exprInfo.expr == null) {
8481 warn('The selector `' + selector + '`is invalid');
8482 return false;
8483 } else {
8484 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
8485
8486 var ret = exprInfo.expr.populate(self, currentQuery, args);
8487
8488 if (ret === false) {
8489 return false; // exit if population failed
8490 } else if (ret != null) {
8491 currentQuery = ret; // change the current query to be filled if the expr specifies
8492 }
8493 }
8494
8495 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
8496
8497 if (remaining.match(/^\s*$/)) {
8498 break;
8499 }
8500 }
8501
8502 var lastQ = self[self.length - 1];
8503
8504 if (self.currentSubject != null) {
8505 lastQ.subject = self.currentSubject;
8506 }
8507
8508 lastQ.edgeCount = self.edgeCount;
8509 lastQ.compoundCount = self.compoundCount;
8510
8511 for (var i = 0; i < self.length; i++) {
8512 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
8513
8514 if (q.compoundCount > 0 && q.edgeCount > 0) {
8515 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
8516 return false;
8517 }
8518
8519 if (q.edgeCount > 1) {
8520 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
8521 return false;
8522 } else if (q.edgeCount === 1) {
8523 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.');
8524 }
8525 }
8526
8527 return true; // success
8528 };
8529 /**
8530 * Get the selector represented as a string. This value uses default formatting,
8531 * so things like spacing may differ from the input text passed to the constructor.
8532 * @returns {string} The selector string
8533 */
8534
8535
8536 var toString = function toString() {
8537 if (this.toStringCache != null) {
8538 return this.toStringCache;
8539 }
8540
8541 var clean = function clean(obj) {
8542 if (obj == null) {
8543 return '';
8544 } else {
8545 return obj;
8546 }
8547 };
8548
8549 var cleanVal = function cleanVal(val) {
8550 if (string(val)) {
8551 return '"' + val + '"';
8552 } else {
8553 return clean(val);
8554 }
8555 };
8556
8557 var space = function space(val) {
8558 return ' ' + val + ' ';
8559 };
8560
8561 var checkToString = function checkToString(check, subject) {
8562 var type = check.type,
8563 value = check.value;
8564
8565 switch (type) {
8566 case Type.GROUP:
8567 {
8568 var group = clean(value);
8569 return group.substring(0, group.length - 1);
8570 }
8571
8572 case Type.DATA_COMPARE:
8573 {
8574 var field = check.field,
8575 operator = check.operator;
8576 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
8577 }
8578
8579 case Type.DATA_BOOL:
8580 {
8581 var _operator = check.operator,
8582 _field = check.field;
8583 return '[' + clean(_operator) + _field + ']';
8584 }
8585
8586 case Type.DATA_EXIST:
8587 {
8588 var _field2 = check.field;
8589 return '[' + _field2 + ']';
8590 }
8591
8592 case Type.META_COMPARE:
8593 {
8594 var _operator2 = check.operator,
8595 _field3 = check.field;
8596 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
8597 }
8598
8599 case Type.STATE:
8600 {
8601 return value;
8602 }
8603
8604 case Type.ID:
8605 {
8606 return '#' + value;
8607 }
8608
8609 case Type.CLASS:
8610 {
8611 return '.' + value;
8612 }
8613
8614 case Type.PARENT:
8615 case Type.CHILD:
8616 {
8617 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
8618 }
8619
8620 case Type.ANCESTOR:
8621 case Type.DESCENDANT:
8622 {
8623 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
8624 }
8625
8626 case Type.COMPOUND_SPLIT:
8627 {
8628 var lhs = queryToString(check.left, subject);
8629 var sub = queryToString(check.subject, subject);
8630 var rhs = queryToString(check.right, subject);
8631 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
8632 }
8633
8634 case Type.TRUE:
8635 {
8636 return '';
8637 }
8638 }
8639 };
8640
8641 var queryToString = function queryToString(query, subject) {
8642 return query.checks.reduce(function (str, chk, i) {
8643 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
8644 }, '');
8645 };
8646
8647 var str = '';
8648
8649 for (var i = 0; i < this.length; i++) {
8650 var query = this[i];
8651 str += queryToString(query, query.subject);
8652
8653 if (this.length > 1 && i < this.length - 1) {
8654 str += ', ';
8655 }
8656 }
8657
8658 this.toStringCache = str;
8659 return str;
8660 };
8661 var parse$1 = {
8662 parse: parse,
8663 toString: toString
8664 };
8665
8666 var valCmp = function valCmp(fieldVal, operator, value) {
8667 var matches;
8668 var isFieldStr = string(fieldVal);
8669 var isFieldNum = number(fieldVal);
8670 var isValStr = string(value);
8671 var fieldStr, valStr;
8672 var caseInsensitive = false;
8673 var notExpr = false;
8674 var isIneqCmp = false;
8675
8676 if (operator.indexOf('!') >= 0) {
8677 operator = operator.replace('!', '');
8678 notExpr = true;
8679 }
8680
8681 if (operator.indexOf('@') >= 0) {
8682 operator = operator.replace('@', '');
8683 caseInsensitive = true;
8684 }
8685
8686 if (isFieldStr || isValStr || caseInsensitive) {
8687 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8688 valStr = '' + value;
8689 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8690 // even if we're comparing numbers
8691
8692
8693 if (caseInsensitive) {
8694 fieldVal = fieldStr = fieldStr.toLowerCase();
8695 value = valStr = valStr.toLowerCase();
8696 }
8697
8698 switch (operator) {
8699 case '*=':
8700 matches = fieldStr.indexOf(valStr) >= 0;
8701 break;
8702
8703 case '$=':
8704 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8705 break;
8706
8707 case '^=':
8708 matches = fieldStr.indexOf(valStr) === 0;
8709 break;
8710
8711 case '=':
8712 matches = fieldVal === value;
8713 break;
8714
8715 case '>':
8716 isIneqCmp = true;
8717 matches = fieldVal > value;
8718 break;
8719
8720 case '>=':
8721 isIneqCmp = true;
8722 matches = fieldVal >= value;
8723 break;
8724
8725 case '<':
8726 isIneqCmp = true;
8727 matches = fieldVal < value;
8728 break;
8729
8730 case '<=':
8731 isIneqCmp = true;
8732 matches = fieldVal <= value;
8733 break;
8734
8735 default:
8736 matches = false;
8737 break;
8738 } // apply the not op, but null vals for inequalities should always stay non-matching
8739
8740
8741 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8742 matches = !matches;
8743 }
8744
8745 return matches;
8746 };
8747 var boolCmp = function boolCmp(fieldVal, operator) {
8748 switch (operator) {
8749 case '?':
8750 return fieldVal ? true : false;
8751
8752 case '!':
8753 return fieldVal ? false : true;
8754
8755 case '^':
8756 return fieldVal === undefined;
8757 }
8758 };
8759 var existCmp = function existCmp(fieldVal) {
8760 return fieldVal !== undefined;
8761 };
8762 var data = function data(ele, field) {
8763 return ele.data(field);
8764 };
8765 var meta = function meta(ele, field) {
8766 return ele[field]();
8767 };
8768
8769 /** A lookup of `match(check, ele)` functions by `Type` int */
8770
8771 var match = [];
8772 /**
8773 * Returns whether the query matches for the element
8774 * @param query The `{ type, value, ... }` query object
8775 * @param ele The element to compare against
8776 */
8777
8778 var matches = function matches(query, ele) {
8779 return query.checks.every(function (chk) {
8780 return match[chk.type](chk, ele);
8781 });
8782 };
8783
8784 match[Type.GROUP] = function (check, ele) {
8785 var group = check.value;
8786 return group === '*' || group === ele.group();
8787 };
8788
8789 match[Type.STATE] = function (check, ele) {
8790 var stateSelector = check.value;
8791 return stateSelectorMatches(stateSelector, ele);
8792 };
8793
8794 match[Type.ID] = function (check, ele) {
8795 var id = check.value;
8796 return ele.id() === id;
8797 };
8798
8799 match[Type.CLASS] = function (check, ele) {
8800 var cls = check.value;
8801 return ele.hasClass(cls);
8802 };
8803
8804 match[Type.META_COMPARE] = function (check, ele) {
8805 var field = check.field,
8806 operator = check.operator,
8807 value = check.value;
8808 return valCmp(meta(ele, field), operator, value);
8809 };
8810
8811 match[Type.DATA_COMPARE] = function (check, ele) {
8812 var field = check.field,
8813 operator = check.operator,
8814 value = check.value;
8815 return valCmp(data(ele, field), operator, value);
8816 };
8817
8818 match[Type.DATA_BOOL] = function (check, ele) {
8819 var field = check.field,
8820 operator = check.operator;
8821 return boolCmp(data(ele, field), operator);
8822 };
8823
8824 match[Type.DATA_EXIST] = function (check, ele) {
8825 var field = check.field,
8826 operator = check.operator;
8827 return existCmp(data(ele, field));
8828 };
8829
8830 match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8831 var qA = check.nodes[0];
8832 var qB = check.nodes[1];
8833 var src = ele.source();
8834 var tgt = ele.target();
8835 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8836 };
8837
8838 match[Type.NODE_NEIGHBOR] = function (check, ele) {
8839 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8840 return n.isNode() && matches(check.neighbor, n);
8841 });
8842 };
8843
8844 match[Type.DIRECTED_EDGE] = function (check, ele) {
8845 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8846 };
8847
8848 match[Type.NODE_SOURCE] = function (check, ele) {
8849 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8850 return n.isNode() && matches(check.target, n);
8851 });
8852 };
8853
8854 match[Type.NODE_TARGET] = function (check, ele) {
8855 return matches(check.target, ele) && ele.incomers().some(function (n) {
8856 return n.isNode() && matches(check.source, n);
8857 });
8858 };
8859
8860 match[Type.CHILD] = function (check, ele) {
8861 return matches(check.child, ele) && matches(check.parent, ele.parent());
8862 };
8863
8864 match[Type.PARENT] = function (check, ele) {
8865 return matches(check.parent, ele) && ele.children().some(function (c) {
8866 return matches(check.child, c);
8867 });
8868 };
8869
8870 match[Type.DESCENDANT] = function (check, ele) {
8871 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8872 return matches(check.ancestor, a);
8873 });
8874 };
8875
8876 match[Type.ANCESTOR] = function (check, ele) {
8877 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8878 return matches(check.descendant, d);
8879 });
8880 };
8881
8882 match[Type.COMPOUND_SPLIT] = function (check, ele) {
8883 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8884 };
8885
8886 match[Type.TRUE] = function () {
8887 return true;
8888 };
8889
8890 match[Type.COLLECTION] = function (check, ele) {
8891 var collection = check.value;
8892 return collection.has(ele);
8893 };
8894
8895 match[Type.FILTER] = function (check, ele) {
8896 var filter = check.value;
8897 return filter(ele);
8898 };
8899
8900 var filter = function filter(collection) {
8901 var self = this; // for 1 id #foo queries, just get the element
8902
8903 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8904 return collection.getElementById(self[0].checks[0].value).collection();
8905 }
8906
8907 var selectorFunction = function selectorFunction(element) {
8908 for (var j = 0; j < self.length; j++) {
8909 var query = self[j];
8910
8911 if (matches(query, element)) {
8912 return true;
8913 }
8914 }
8915
8916 return false;
8917 };
8918
8919 if (self.text() == null) {
8920 selectorFunction = function selectorFunction() {
8921 return true;
8922 };
8923 }
8924
8925 return collection.filter(selectorFunction);
8926 }; // filter
8927 // does selector match a single element?
8928
8929
8930 var matches$1 = function matches$1(ele) {
8931 var self = this;
8932
8933 for (var j = 0; j < self.length; j++) {
8934 var query = self[j];
8935
8936 if (matches(query, ele)) {
8937 return true;
8938 }
8939 }
8940
8941 return false;
8942 }; // matches
8943
8944
8945 var matching = {
8946 matches: matches$1,
8947 filter: filter
8948 };
8949
8950 var Selector = function Selector(selector) {
8951 this.inputText = selector;
8952 this.currentSubject = null;
8953 this.compoundCount = 0;
8954 this.edgeCount = 0;
8955 this.length = 0;
8956
8957 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8958 this.addQuery({
8959 checks: [{
8960 type: Type.COLLECTION,
8961 value: selector.collection()
8962 }]
8963 });
8964 } else if (fn(selector)) {
8965 this.addQuery({
8966 checks: [{
8967 type: Type.FILTER,
8968 value: selector
8969 }]
8970 });
8971 } else if (string(selector)) {
8972 if (!this.parse(selector)) {
8973 this.invalid = true;
8974 }
8975 } else {
8976 error('A selector must be created from a string; found ');
8977 }
8978 };
8979
8980 var selfn = Selector.prototype;
8981 [parse$1, matching].forEach(function (p) {
8982 return extend(selfn, p);
8983 });
8984
8985 selfn.text = function () {
8986 return this.inputText;
8987 };
8988
8989 selfn.size = function () {
8990 return this.length;
8991 };
8992
8993 selfn.eq = function (i) {
8994 return this[i];
8995 };
8996
8997 selfn.sameText = function (otherSel) {
8998 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8999 };
9000
9001 selfn.addQuery = function (q) {
9002 this[this.length++] = q;
9003 };
9004
9005 selfn.selector = selfn.toString;
9006
9007 var elesfn$f = {
9008 allAre: function allAre(selector) {
9009 var selObj = new Selector(selector);
9010 return this.every(function (ele) {
9011 return selObj.matches(ele);
9012 });
9013 },
9014 is: function is(selector) {
9015 var selObj = new Selector(selector);
9016 return this.some(function (ele) {
9017 return selObj.matches(ele);
9018 });
9019 },
9020 some: function some(fn, thisArg) {
9021 for (var i = 0; i < this.length; i++) {
9022 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9023
9024 if (ret) {
9025 return true;
9026 }
9027 }
9028
9029 return false;
9030 },
9031 every: function every(fn, thisArg) {
9032 for (var i = 0; i < this.length; i++) {
9033 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9034
9035 if (!ret) {
9036 return false;
9037 }
9038 }
9039
9040 return true;
9041 },
9042 same: function same(collection) {
9043 // cheap collection ref check
9044 if (this === collection) {
9045 return true;
9046 }
9047
9048 collection = this.cy().collection(collection);
9049 var thisLength = this.length;
9050 var collectionLength = collection.length; // cheap length check
9051
9052 if (thisLength !== collectionLength) {
9053 return false;
9054 } // cheap element ref check
9055
9056
9057 if (thisLength === 1) {
9058 return this[0] === collection[0];
9059 }
9060
9061 return this.every(function (ele) {
9062 return collection.hasElementWithId(ele.id());
9063 });
9064 },
9065 anySame: function anySame(collection) {
9066 collection = this.cy().collection(collection);
9067 return this.some(function (ele) {
9068 return collection.hasElementWithId(ele.id());
9069 });
9070 },
9071 allAreNeighbors: function allAreNeighbors(collection) {
9072 collection = this.cy().collection(collection);
9073 var nhood = this.neighborhood();
9074 return collection.every(function (ele) {
9075 return nhood.hasElementWithId(ele.id());
9076 });
9077 },
9078 contains: function contains(collection) {
9079 collection = this.cy().collection(collection);
9080 var self = this;
9081 return collection.every(function (ele) {
9082 return self.hasElementWithId(ele.id());
9083 });
9084 }
9085 };
9086 elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
9087 elesfn$f.has = elesfn$f.contains;
9088 elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
9089
9090 var cache = function cache(fn, name) {
9091 return function traversalCache(arg1, arg2, arg3, arg4) {
9092 var selectorOrEles = arg1;
9093 var eles = this;
9094 var key;
9095
9096 if (selectorOrEles == null) {
9097 key = '';
9098 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
9099 key = selectorOrEles.id();
9100 }
9101
9102 if (eles.length === 1 && key) {
9103 var _p = eles[0]._private;
9104 var tch = _p.traversalCache = _p.traversalCache || {};
9105 var ch = tch[name] = tch[name] || [];
9106 var hash = hashString(key);
9107 var cacheHit = ch[hash];
9108
9109 if (cacheHit) {
9110 return cacheHit;
9111 } else {
9112 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
9113 }
9114 } else {
9115 return fn.call(eles, arg1, arg2, arg3, arg4);
9116 }
9117 };
9118 };
9119
9120 var elesfn$g = {
9121 parent: function parent(selector) {
9122 var parents = []; // optimisation for single ele call
9123
9124 if (this.length === 1) {
9125 var parent = this[0]._private.parent;
9126
9127 if (parent) {
9128 return parent;
9129 }
9130 }
9131
9132 for (var i = 0; i < this.length; i++) {
9133 var ele = this[i];
9134 var _parent = ele._private.parent;
9135
9136 if (_parent) {
9137 parents.push(_parent);
9138 }
9139 }
9140
9141 return this.spawn(parents, {
9142 unique: true
9143 }).filter(selector);
9144 },
9145 parents: function parents(selector) {
9146 var parents = [];
9147 var eles = this.parent();
9148
9149 while (eles.nonempty()) {
9150 for (var i = 0; i < eles.length; i++) {
9151 var ele = eles[i];
9152 parents.push(ele);
9153 }
9154
9155 eles = eles.parent();
9156 }
9157
9158 return this.spawn(parents, {
9159 unique: true
9160 }).filter(selector);
9161 },
9162 commonAncestors: function commonAncestors(selector) {
9163 var ancestors;
9164
9165 for (var i = 0; i < this.length; i++) {
9166 var ele = this[i];
9167 var parents = ele.parents();
9168 ancestors = ancestors || parents;
9169 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
9170 }
9171
9172 return ancestors.filter(selector);
9173 },
9174 orphans: function orphans(selector) {
9175 return this.stdFilter(function (ele) {
9176 return ele.isOrphan();
9177 }).filter(selector);
9178 },
9179 nonorphans: function nonorphans(selector) {
9180 return this.stdFilter(function (ele) {
9181 return ele.isChild();
9182 }).filter(selector);
9183 },
9184 children: cache(function (selector) {
9185 var children = [];
9186
9187 for (var i = 0; i < this.length; i++) {
9188 var ele = this[i];
9189 var eleChildren = ele._private.children;
9190
9191 for (var j = 0; j < eleChildren.length; j++) {
9192 children.push(eleChildren[j]);
9193 }
9194 }
9195
9196 return this.spawn(children, {
9197 unique: true
9198 }).filter(selector);
9199 }, 'children'),
9200 siblings: function siblings(selector) {
9201 return this.parent().children().not(this).filter(selector);
9202 },
9203 isParent: function isParent() {
9204 var ele = this[0];
9205
9206 if (ele) {
9207 return ele.isNode() && ele._private.children.length !== 0;
9208 }
9209 },
9210 isChildless: function isChildless() {
9211 var ele = this[0];
9212
9213 if (ele) {
9214 return ele.isNode() && ele._private.children.length === 0;
9215 }
9216 },
9217 isChild: function isChild() {
9218 var ele = this[0];
9219
9220 if (ele) {
9221 return ele.isNode() && ele._private.parent != null;
9222 }
9223 },
9224 isOrphan: function isOrphan() {
9225 var ele = this[0];
9226
9227 if (ele) {
9228 return ele.isNode() && ele._private.parent == null;
9229 }
9230 },
9231 descendants: function descendants(selector) {
9232 var elements = [];
9233
9234 function add(eles) {
9235 for (var i = 0; i < eles.length; i++) {
9236 var ele = eles[i];
9237 elements.push(ele);
9238
9239 if (ele.children().nonempty()) {
9240 add(ele.children());
9241 }
9242 }
9243 }
9244
9245 add(this.children());
9246 return this.spawn(elements, {
9247 unique: true
9248 }).filter(selector);
9249 }
9250 };
9251
9252 function forEachCompound(eles, fn, includeSelf, recursiveStep) {
9253 var q = [];
9254 var did = new Set$1();
9255 var cy = eles.cy();
9256 var hasCompounds = cy.hasCompoundNodes();
9257
9258 for (var i = 0; i < eles.length; i++) {
9259 var ele = eles[i];
9260
9261 if (includeSelf) {
9262 q.push(ele);
9263 } else if (hasCompounds) {
9264 recursiveStep(q, did, ele);
9265 }
9266 }
9267
9268 while (q.length > 0) {
9269 var _ele = q.shift();
9270
9271 fn(_ele);
9272 did.add(_ele.id());
9273
9274 if (hasCompounds) {
9275 recursiveStep(q, did, _ele);
9276 }
9277 }
9278
9279 return eles;
9280 }
9281
9282 function addChildren(q, did, ele) {
9283 if (ele.isParent()) {
9284 var children = ele._private.children;
9285
9286 for (var i = 0; i < children.length; i++) {
9287 var child = children[i];
9288
9289 if (!did.has(child.id())) {
9290 q.push(child);
9291 }
9292 }
9293 }
9294 } // very efficient version of eles.add( eles.descendants() ).forEach()
9295 // for internal use
9296
9297
9298 elesfn$g.forEachDown = function (fn) {
9299 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9300 return forEachCompound(this, fn, includeSelf, addChildren);
9301 };
9302
9303 function addParent(q, did, ele) {
9304 if (ele.isChild()) {
9305 var parent = ele._private.parent;
9306
9307 if (!did.has(parent.id())) {
9308 q.push(parent);
9309 }
9310 }
9311 }
9312
9313 elesfn$g.forEachUp = function (fn) {
9314 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9315 return forEachCompound(this, fn, includeSelf, addParent);
9316 };
9317
9318 function addParentAndChildren(q, did, ele) {
9319 addParent(q, did, ele);
9320 addChildren(q, did, ele);
9321 }
9322
9323 elesfn$g.forEachUpAndDown = function (fn) {
9324 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9325 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
9326 }; // aliases
9327
9328
9329 elesfn$g.ancestors = elesfn$g.parents;
9330
9331 var fn$1, elesfn$h;
9332 fn$1 = elesfn$h = {
9333 data: define$3.data({
9334 field: 'data',
9335 bindingEvent: 'data',
9336 allowBinding: true,
9337 allowSetting: true,
9338 settingEvent: 'data',
9339 settingTriggersEvent: true,
9340 triggerFnName: 'trigger',
9341 allowGetting: true,
9342 immutableKeys: {
9343 'id': true,
9344 'source': true,
9345 'target': true,
9346 'parent': true
9347 },
9348 updateStyle: true
9349 }),
9350 removeData: define$3.removeData({
9351 field: 'data',
9352 event: 'data',
9353 triggerFnName: 'trigger',
9354 triggerEvent: true,
9355 immutableKeys: {
9356 'id': true,
9357 'source': true,
9358 'target': true,
9359 'parent': true
9360 },
9361 updateStyle: true
9362 }),
9363 scratch: define$3.data({
9364 field: 'scratch',
9365 bindingEvent: 'scratch',
9366 allowBinding: true,
9367 allowSetting: true,
9368 settingEvent: 'scratch',
9369 settingTriggersEvent: true,
9370 triggerFnName: 'trigger',
9371 allowGetting: true,
9372 updateStyle: true
9373 }),
9374 removeScratch: define$3.removeData({
9375 field: 'scratch',
9376 event: 'scratch',
9377 triggerFnName: 'trigger',
9378 triggerEvent: true,
9379 updateStyle: true
9380 }),
9381 rscratch: define$3.data({
9382 field: 'rscratch',
9383 allowBinding: false,
9384 allowSetting: true,
9385 settingTriggersEvent: false,
9386 allowGetting: true
9387 }),
9388 removeRscratch: define$3.removeData({
9389 field: 'rscratch',
9390 triggerEvent: false
9391 }),
9392 id: function id() {
9393 var ele = this[0];
9394
9395 if (ele) {
9396 return ele._private.data.id;
9397 }
9398 }
9399 }; // aliases
9400
9401 fn$1.attr = fn$1.data;
9402 fn$1.removeAttr = fn$1.removeData;
9403 var data$1 = elesfn$h;
9404
9405 var elesfn$i = {};
9406
9407 function defineDegreeFunction(callback) {
9408 return function (includeLoops) {
9409 var self = this;
9410
9411 if (includeLoops === undefined) {
9412 includeLoops = true;
9413 }
9414
9415 if (self.length === 0) {
9416 return;
9417 }
9418
9419 if (self.isNode() && !self.removed()) {
9420 var degree = 0;
9421 var node = self[0];
9422 var connectedEdges = node._private.edges;
9423
9424 for (var i = 0; i < connectedEdges.length; i++) {
9425 var edge = connectedEdges[i];
9426
9427 if (!includeLoops && edge.isLoop()) {
9428 continue;
9429 }
9430
9431 degree += callback(node, edge);
9432 }
9433
9434 return degree;
9435 } else {
9436 return;
9437 }
9438 };
9439 }
9440
9441 extend(elesfn$i, {
9442 degree: defineDegreeFunction(function (node, edge) {
9443 if (edge.source().same(edge.target())) {
9444 return 2;
9445 } else {
9446 return 1;
9447 }
9448 }),
9449 indegree: defineDegreeFunction(function (node, edge) {
9450 if (edge.target().same(node)) {
9451 return 1;
9452 } else {
9453 return 0;
9454 }
9455 }),
9456 outdegree: defineDegreeFunction(function (node, edge) {
9457 if (edge.source().same(node)) {
9458 return 1;
9459 } else {
9460 return 0;
9461 }
9462 })
9463 });
9464
9465 function defineDegreeBoundsFunction(degreeFn, callback) {
9466 return function (includeLoops) {
9467 var ret;
9468 var nodes = this.nodes();
9469
9470 for (var i = 0; i < nodes.length; i++) {
9471 var ele = nodes[i];
9472 var degree = ele[degreeFn](includeLoops);
9473
9474 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
9475 ret = degree;
9476 }
9477 }
9478
9479 return ret;
9480 };
9481 }
9482
9483 extend(elesfn$i, {
9484 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
9485 return degree < min;
9486 }),
9487 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
9488 return degree > max;
9489 }),
9490 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
9491 return degree < min;
9492 }),
9493 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
9494 return degree > max;
9495 }),
9496 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
9497 return degree < min;
9498 }),
9499 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
9500 return degree > max;
9501 })
9502 });
9503 extend(elesfn$i, {
9504 totalDegree: function totalDegree(includeLoops) {
9505 var total = 0;
9506 var nodes = this.nodes();
9507
9508 for (var i = 0; i < nodes.length; i++) {
9509 total += nodes[i].degree(includeLoops);
9510 }
9511
9512 return total;
9513 }
9514 });
9515
9516 var fn$2, elesfn$j;
9517
9518 var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
9519 for (var i = 0; i < eles.length; i++) {
9520 var ele = eles[i];
9521
9522 if (!ele.locked()) {
9523 var oldPos = ele._private.position;
9524 var delta = {
9525 x: newPos.x != null ? newPos.x - oldPos.x : 0,
9526 y: newPos.y != null ? newPos.y - oldPos.y : 0
9527 };
9528
9529 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
9530 ele.children().shift(delta, silent);
9531 }
9532
9533 ele.shiftCachedBoundingBox(delta);
9534 }
9535 }
9536 };
9537
9538 var positionDef = {
9539 field: 'position',
9540 bindingEvent: 'position',
9541 allowBinding: true,
9542 allowSetting: true,
9543 settingEvent: 'position',
9544 settingTriggersEvent: true,
9545 triggerFnName: 'emitAndNotify',
9546 allowGetting: true,
9547 validKeys: ['x', 'y'],
9548 beforeGet: function beforeGet(ele) {
9549 ele.updateCompoundBounds();
9550 },
9551 beforeSet: function beforeSet(eles, newPos) {
9552 beforePositionSet(eles, newPos, false);
9553 },
9554 onSet: function onSet(eles) {
9555 eles.dirtyCompoundBoundsCache();
9556 },
9557 canSet: function canSet(ele) {
9558 return !ele.locked();
9559 }
9560 };
9561 fn$2 = elesfn$j = {
9562 position: define$3.data(positionDef),
9563 // position but no notification to renderer
9564 silentPosition: define$3.data(extend({}, positionDef, {
9565 allowBinding: false,
9566 allowSetting: true,
9567 settingTriggersEvent: false,
9568 allowGetting: false,
9569 beforeSet: function beforeSet(eles, newPos) {
9570 beforePositionSet(eles, newPos, true);
9571 }
9572 })),
9573 positions: function positions(pos, silent) {
9574 if (plainObject(pos)) {
9575 if (silent) {
9576 this.silentPosition(pos);
9577 } else {
9578 this.position(pos);
9579 }
9580 } else if (fn(pos)) {
9581 var _fn = pos;
9582 var cy = this.cy();
9583 cy.startBatch();
9584
9585 for (var i = 0; i < this.length; i++) {
9586 var ele = this[i];
9587
9588 var _pos = void 0;
9589
9590 if (_pos = _fn(ele, i)) {
9591 if (silent) {
9592 ele.silentPosition(_pos);
9593 } else {
9594 ele.position(_pos);
9595 }
9596 }
9597 }
9598
9599 cy.endBatch();
9600 }
9601
9602 return this; // chaining
9603 },
9604 silentPositions: function silentPositions(pos) {
9605 return this.positions(pos, true);
9606 },
9607 shift: function shift(dim, val, silent) {
9608 var delta;
9609
9610 if (plainObject(dim)) {
9611 delta = {
9612 x: number(dim.x) ? dim.x : 0,
9613 y: number(dim.y) ? dim.y : 0
9614 };
9615 silent = val;
9616 } else if (string(dim) && number(val)) {
9617 delta = {
9618 x: 0,
9619 y: 0
9620 };
9621 delta[dim] = val;
9622 }
9623
9624 if (delta != null) {
9625 var cy = this.cy();
9626 cy.startBatch();
9627
9628 for (var i = 0; i < this.length; i++) {
9629 var ele = this[i];
9630 var pos = ele.position();
9631 var newPos = {
9632 x: pos.x + delta.x,
9633 y: pos.y + delta.y
9634 };
9635
9636 if (silent) {
9637 ele.silentPosition(newPos);
9638 } else {
9639 ele.position(newPos);
9640 }
9641 }
9642
9643 cy.endBatch();
9644 }
9645
9646 return this;
9647 },
9648 silentShift: function silentShift(dim, val) {
9649 if (plainObject(dim)) {
9650 this.shift(dim, true);
9651 } else if (string(dim) && number(val)) {
9652 this.shift(dim, val, true);
9653 }
9654
9655 return this;
9656 },
9657 // get/set the rendered (i.e. on screen) positon of the element
9658 renderedPosition: function renderedPosition(dim, val) {
9659 var ele = this[0];
9660 var cy = this.cy();
9661 var zoom = cy.zoom();
9662 var pan = cy.pan();
9663 var rpos = plainObject(dim) ? dim : undefined;
9664 var setting = rpos !== undefined || val !== undefined && string(dim);
9665
9666 if (ele && ele.isNode()) {
9667 // must have an element and must be a node to return position
9668 if (setting) {
9669 for (var i = 0; i < this.length; i++) {
9670 var _ele = this[i];
9671
9672 if (val !== undefined) {
9673 // set one dimension
9674 _ele.position(dim, (val - pan[dim]) / zoom);
9675 } else if (rpos !== undefined) {
9676 // set whole position
9677 _ele.position(renderedToModelPosition(rpos, zoom, pan));
9678 }
9679 }
9680 } else {
9681 // getting
9682 var pos = ele.position();
9683 rpos = modelToRenderedPosition(pos, zoom, pan);
9684
9685 if (dim === undefined) {
9686 // then return the whole rendered position
9687 return rpos;
9688 } else {
9689 // then return the specified dimension
9690 return rpos[dim];
9691 }
9692 }
9693 } else if (!setting) {
9694 return undefined; // for empty collection case
9695 }
9696
9697 return this; // chaining
9698 },
9699 // get/set the position relative to the parent
9700 relativePosition: function relativePosition(dim, val) {
9701 var ele = this[0];
9702 var cy = this.cy();
9703 var ppos = plainObject(dim) ? dim : undefined;
9704 var setting = ppos !== undefined || val !== undefined && string(dim);
9705 var hasCompoundNodes = cy.hasCompoundNodes();
9706
9707 if (ele && ele.isNode()) {
9708 // must have an element and must be a node to return position
9709 if (setting) {
9710 for (var i = 0; i < this.length; i++) {
9711 var _ele2 = this[i];
9712 var parent = hasCompoundNodes ? _ele2.parent() : null;
9713 var hasParent = parent && parent.length > 0;
9714 var relativeToParent = hasParent;
9715
9716 if (hasParent) {
9717 parent = parent[0];
9718 }
9719
9720 var origin = relativeToParent ? parent.position() : {
9721 x: 0,
9722 y: 0
9723 };
9724
9725 if (val !== undefined) {
9726 // set one dimension
9727 _ele2.position(dim, val + origin[dim]);
9728 } else if (ppos !== undefined) {
9729 // set whole position
9730 _ele2.position({
9731 x: ppos.x + origin.x,
9732 y: ppos.y + origin.y
9733 });
9734 }
9735 }
9736 } else {
9737 // getting
9738 var pos = ele.position();
9739
9740 var _parent = hasCompoundNodes ? ele.parent() : null;
9741
9742 var _hasParent = _parent && _parent.length > 0;
9743
9744 var _relativeToParent = _hasParent;
9745
9746 if (_hasParent) {
9747 _parent = _parent[0];
9748 }
9749
9750 var _origin = _relativeToParent ? _parent.position() : {
9751 x: 0,
9752 y: 0
9753 };
9754
9755 ppos = {
9756 x: pos.x - _origin.x,
9757 y: pos.y - _origin.y
9758 };
9759
9760 if (dim === undefined) {
9761 // then return the whole rendered position
9762 return ppos;
9763 } else {
9764 // then return the specified dimension
9765 return ppos[dim];
9766 }
9767 }
9768 } else if (!setting) {
9769 return undefined; // for empty collection case
9770 }
9771
9772 return this; // chaining
9773 }
9774 }; // aliases
9775
9776 fn$2.modelPosition = fn$2.point = fn$2.position;
9777 fn$2.modelPositions = fn$2.points = fn$2.positions;
9778 fn$2.renderedPoint = fn$2.renderedPosition;
9779 fn$2.relativePoint = fn$2.relativePosition;
9780 var position = elesfn$j;
9781
9782 var fn$3, elesfn$k;
9783 fn$3 = elesfn$k = {};
9784
9785 elesfn$k.renderedBoundingBox = function (options) {
9786 var bb = this.boundingBox(options);
9787 var cy = this.cy();
9788 var zoom = cy.zoom();
9789 var pan = cy.pan();
9790 var x1 = bb.x1 * zoom + pan.x;
9791 var x2 = bb.x2 * zoom + pan.x;
9792 var y1 = bb.y1 * zoom + pan.y;
9793 var y2 = bb.y2 * zoom + pan.y;
9794 return {
9795 x1: x1,
9796 x2: x2,
9797 y1: y1,
9798 y2: y2,
9799 w: x2 - x1,
9800 h: y2 - y1
9801 };
9802 };
9803
9804 elesfn$k.dirtyCompoundBoundsCache = function () {
9805 var cy = this.cy();
9806
9807 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9808 return this;
9809 }
9810
9811 this.forEachUp(function (ele) {
9812 if (ele.isParent()) {
9813 var _p = ele._private;
9814 _p.compoundBoundsClean = false;
9815 _p.bbCache = null;
9816 ele.emitAndNotify('bounds');
9817 }
9818 });
9819 return this;
9820 };
9821
9822 elesfn$k.updateCompoundBounds = function () {
9823 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9824 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9825
9826 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9827 return this;
9828 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9829
9830
9831 if (!force && cy.batching()) {
9832 return this;
9833 }
9834
9835 function update(parent) {
9836 if (!parent.isParent()) {
9837 return;
9838 }
9839
9840 var _p = parent._private;
9841 var children = parent.children();
9842 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9843 var min = {
9844 width: {
9845 val: parent.pstyle('min-width').pfValue,
9846 left: parent.pstyle('min-width-bias-left'),
9847 right: parent.pstyle('min-width-bias-right')
9848 },
9849 height: {
9850 val: parent.pstyle('min-height').pfValue,
9851 top: parent.pstyle('min-height-bias-top'),
9852 bottom: parent.pstyle('min-height-bias-bottom')
9853 }
9854 };
9855 var bb = children.boundingBox({
9856 includeLabels: includeLabels,
9857 includeOverlays: false,
9858 // updating the compound bounds happens outside of the regular
9859 // cache cycle (i.e. before fired events)
9860 useCache: false
9861 });
9862 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9863
9864 if (bb.w === 0 || bb.h === 0) {
9865 bb = {
9866 w: parent.pstyle('width').pfValue,
9867 h: parent.pstyle('height').pfValue
9868 };
9869 bb.x1 = pos.x - bb.w / 2;
9870 bb.x2 = pos.x + bb.w / 2;
9871 bb.y1 = pos.y - bb.h / 2;
9872 bb.y2 = pos.y + bb.h / 2;
9873 }
9874
9875 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9876 var biasDiff = 0;
9877 var biasComplementDiff = 0;
9878 var biasTotal = propBias + propBiasComplement;
9879
9880 if (propDiff > 0 && biasTotal > 0) {
9881 biasDiff = propBias / biasTotal * propDiff;
9882 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9883 }
9884
9885 return {
9886 biasDiff: biasDiff,
9887 biasComplementDiff: biasComplementDiff
9888 };
9889 }
9890
9891 function computePaddingValues(width, height, paddingObject, relativeTo) {
9892 // Assuming percentage is number from 0 to 1
9893 if (paddingObject.units === '%') {
9894 switch (relativeTo) {
9895 case 'width':
9896 return width > 0 ? paddingObject.pfValue * width : 0;
9897
9898 case 'height':
9899 return height > 0 ? paddingObject.pfValue * height : 0;
9900
9901 case 'average':
9902 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9903
9904 case 'min':
9905 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9906
9907 case 'max':
9908 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9909
9910 default:
9911 return 0;
9912 }
9913 } else if (paddingObject.units === 'px') {
9914 return paddingObject.pfValue;
9915 } else {
9916 return 0;
9917 }
9918 }
9919
9920 var leftVal = min.width.left.value;
9921
9922 if (min.width.left.units === 'px' && min.width.val > 0) {
9923 leftVal = leftVal * 100 / min.width.val;
9924 }
9925
9926 var rightVal = min.width.right.value;
9927
9928 if (min.width.right.units === 'px' && min.width.val > 0) {
9929 rightVal = rightVal * 100 / min.width.val;
9930 }
9931
9932 var topVal = min.height.top.value;
9933
9934 if (min.height.top.units === 'px' && min.height.val > 0) {
9935 topVal = topVal * 100 / min.height.val;
9936 }
9937
9938 var bottomVal = min.height.bottom.value;
9939
9940 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9941 bottomVal = bottomVal * 100 / min.height.val;
9942 }
9943
9944 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9945 var diffLeft = widthBiasDiffs.biasDiff;
9946 var diffRight = widthBiasDiffs.biasComplementDiff;
9947 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9948 var diffTop = heightBiasDiffs.biasDiff;
9949 var diffBottom = heightBiasDiffs.biasComplementDiff;
9950 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9951 _p.autoWidth = Math.max(bb.w, min.width.val);
9952 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9953 _p.autoHeight = Math.max(bb.h, min.height.val);
9954 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9955 }
9956
9957 for (var i = 0; i < this.length; i++) {
9958 var ele = this[i];
9959 var _p = ele._private;
9960
9961 if (!_p.compoundBoundsClean) {
9962 update(ele);
9963
9964 if (!cy.batching()) {
9965 _p.compoundBoundsClean = true;
9966 }
9967 }
9968 }
9969
9970 return this;
9971 };
9972
9973 var noninf = function noninf(x) {
9974 if (x === Infinity || x === -Infinity) {
9975 return 0;
9976 }
9977
9978 return x;
9979 };
9980
9981 var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9982 // don't update with zero area boxes
9983 if (x2 - x1 === 0 || y2 - y1 === 0) {
9984 return;
9985 } // don't update with null dim
9986
9987
9988 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9989 return;
9990 }
9991
9992 b.x1 = x1 < b.x1 ? x1 : b.x1;
9993 b.x2 = x2 > b.x2 ? x2 : b.x2;
9994 b.y1 = y1 < b.y1 ? y1 : b.y1;
9995 b.y2 = y2 > b.y2 ? y2 : b.y2;
9996 b.w = b.x2 - b.x1;
9997 b.h = b.y2 - b.y1;
9998 };
9999
10000 var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
10001 if (b2 == null) {
10002 return b;
10003 }
10004
10005 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
10006 };
10007
10008 var prefixedProperty = function prefixedProperty(obj, field, prefix) {
10009 return getPrefixedProperty(obj, field, prefix);
10010 };
10011
10012 var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
10013 if (ele.cy().headless()) {
10014 return;
10015 }
10016
10017 var _p = ele._private;
10018 var rstyle = _p.rstyle;
10019 var halfArW = rstyle.arrowWidth / 2;
10020 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
10021 var x;
10022 var y;
10023
10024 if (arrowType !== 'none') {
10025 if (prefix === 'source') {
10026 x = rstyle.srcX;
10027 y = rstyle.srcY;
10028 } else if (prefix === 'target') {
10029 x = rstyle.tgtX;
10030 y = rstyle.tgtY;
10031 } else {
10032 x = rstyle.midX;
10033 y = rstyle.midY;
10034 } // always store the individual arrow bounds
10035
10036
10037 var bbs = _p.arrowBounds = _p.arrowBounds || {};
10038 var bb = bbs[prefix] = bbs[prefix] || {};
10039 bb.x1 = x - halfArW;
10040 bb.y1 = y - halfArW;
10041 bb.x2 = x + halfArW;
10042 bb.y2 = y + halfArW;
10043 bb.w = bb.x2 - bb.x1;
10044 bb.h = bb.y2 - bb.y1;
10045 expandBoundingBox(bb, 1);
10046 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
10047 }
10048 };
10049
10050 var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
10051 if (ele.cy().headless()) {
10052 return;
10053 }
10054
10055 var prefixDash;
10056
10057 if (prefix) {
10058 prefixDash = prefix + '-';
10059 } else {
10060 prefixDash = '';
10061 }
10062
10063 var _p = ele._private;
10064 var rstyle = _p.rstyle;
10065 var label = ele.pstyle(prefixDash + 'label').strValue;
10066
10067 if (label) {
10068 var halign = ele.pstyle('text-halign');
10069 var valign = ele.pstyle('text-valign');
10070 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
10071 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
10072 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
10073 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
10074 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
10075 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
10076 var isEdge = ele.isEdge();
10077 var rotation = ele.pstyle(prefixDash + 'text-rotation');
10078 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
10079 var borderWidth = ele.pstyle('text-border-width').pfValue;
10080 var halfBorderWidth = borderWidth / 2;
10081 var padding = ele.pstyle('text-background-padding').pfValue;
10082 var lh = labelHeight;
10083 var lw = labelWidth;
10084 var lw_2 = lw / 2;
10085 var lh_2 = lh / 2;
10086 var lx1, lx2, ly1, ly2;
10087
10088 if (isEdge) {
10089 lx1 = labelX - lw_2;
10090 lx2 = labelX + lw_2;
10091 ly1 = labelY - lh_2;
10092 ly2 = labelY + lh_2;
10093 } else {
10094 switch (halign.value) {
10095 case 'left':
10096 lx1 = labelX - lw;
10097 lx2 = labelX;
10098 break;
10099
10100 case 'center':
10101 lx1 = labelX - lw_2;
10102 lx2 = labelX + lw_2;
10103 break;
10104
10105 case 'right':
10106 lx1 = labelX;
10107 lx2 = labelX + lw;
10108 break;
10109 }
10110
10111 switch (valign.value) {
10112 case 'top':
10113 ly1 = labelY - lh;
10114 ly2 = labelY;
10115 break;
10116
10117 case 'center':
10118 ly1 = labelY - lh_2;
10119 ly2 = labelY + lh_2;
10120 break;
10121
10122 case 'bottom':
10123 ly1 = labelY;
10124 ly2 = labelY + lh;
10125 break;
10126 }
10127 } // shift by margin and expand by outline and border
10128
10129
10130 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
10131 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
10132 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
10133 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
10134
10135 var bbPrefix = prefix || 'main';
10136 var bbs = _p.labelBounds;
10137 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
10138 bb.x1 = lx1;
10139 bb.y1 = ly1;
10140 bb.x2 = lx2;
10141 bb.y2 = ly2;
10142 bb.w = lx2 - lx1;
10143 bb.h = ly2 - ly1;
10144 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
10145
10146 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
10147 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
10148
10149 if (isAutorotate || isPfValue) {
10150 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
10151 var cos = Math.cos(theta);
10152 var sin = Math.sin(theta); // rotation point (default value for center-center)
10153
10154 var xo = (lx1 + lx2) / 2;
10155 var yo = (ly1 + ly2) / 2;
10156
10157 if (!isEdge) {
10158 switch (halign.value) {
10159 case 'left':
10160 xo = lx2;
10161 break;
10162
10163 case 'right':
10164 xo = lx1;
10165 break;
10166 }
10167
10168 switch (valign.value) {
10169 case 'top':
10170 yo = ly2;
10171 break;
10172
10173 case 'bottom':
10174 yo = ly1;
10175 break;
10176 }
10177 }
10178
10179 var rotate = function rotate(x, y) {
10180 x = x - xo;
10181 y = y - yo;
10182 return {
10183 x: x * cos - y * sin + xo,
10184 y: x * sin + y * cos + yo
10185 };
10186 };
10187
10188 var px1y1 = rotate(lx1, ly1);
10189 var px1y2 = rotate(lx1, ly2);
10190 var px2y1 = rotate(lx2, ly1);
10191 var px2y2 = rotate(lx2, ly2);
10192 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10193 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10194 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10195 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10196 }
10197
10198 var bbPrefixRot = bbPrefix + 'Rot';
10199 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
10200 bbRot.x1 = lx1;
10201 bbRot.y1 = ly1;
10202 bbRot.x2 = lx2;
10203 bbRot.y2 = ly2;
10204 bbRot.w = lx2 - lx1;
10205 bbRot.h = ly2 - ly1;
10206 updateBounds(bounds, lx1, ly1, lx2, ly2);
10207 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
10208 }
10209
10210 return bounds;
10211 }; // get the bounding box of the elements (in raw model position)
10212
10213
10214 var boundingBoxImpl = function boundingBoxImpl(ele, options) {
10215 var cy = ele._private.cy;
10216 var styleEnabled = cy.styleEnabled();
10217 var headless = cy.headless();
10218 var bounds = makeBoundingBox();
10219 var _p = ele._private;
10220 var isNode = ele.isNode();
10221 var isEdge = ele.isEdge();
10222 var ex1, ex2, ey1, ey2; // extrema of body / lines
10223
10224 var x, y; // node pos
10225
10226 var rstyle = _p.rstyle;
10227 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
10228 // (other factors like width values will be considered later in this function anyway)
10229
10230 var isDisplayed = function isDisplayed(ele) {
10231 return ele.pstyle('display').value !== 'none';
10232 };
10233
10234 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
10235 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
10236
10237 if (displayed) {
10238 // displayed suffices, since we will find zero area eles anyway
10239 var overlayOpacity = 0;
10240 var overlayPadding = 0;
10241
10242 if (styleEnabled && options.includeOverlays) {
10243 overlayOpacity = ele.pstyle('overlay-opacity').value;
10244
10245 if (overlayOpacity !== 0) {
10246 overlayPadding = ele.pstyle('overlay-padding').value;
10247 }
10248 }
10249
10250 var w = 0;
10251 var wHalf = 0;
10252
10253 if (styleEnabled) {
10254 w = ele.pstyle('width').pfValue;
10255 wHalf = w / 2;
10256 }
10257
10258 if (isNode && options.includeNodes) {
10259 var pos = ele.position();
10260 x = pos.x;
10261 y = pos.y;
10262
10263 var _w = ele.outerWidth();
10264
10265 var halfW = _w / 2;
10266 var h = ele.outerHeight();
10267 var halfH = h / 2; // handle node dimensions
10268 /////////////////////////
10269
10270 ex1 = x - halfW;
10271 ex2 = x + halfW;
10272 ey1 = y - halfH;
10273 ey2 = y + halfH;
10274 updateBounds(bounds, ex1, ey1, ex2, ey2);
10275 } else if (isEdge && options.includeEdges) {
10276 if (styleEnabled && !headless) {
10277 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
10278 //////////////////////////////////////////////
10279
10280 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10281 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10282 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10283 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
10284
10285 ex1 -= wHalf;
10286 ex2 += wHalf;
10287 ey1 -= wHalf;
10288 ey2 += wHalf;
10289 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
10290 ////////////////
10291
10292 if (curveStyle === 'haystack') {
10293 var hpts = rstyle.haystackPts;
10294
10295 if (hpts && hpts.length === 2) {
10296 ex1 = hpts[0].x;
10297 ey1 = hpts[0].y;
10298 ex2 = hpts[1].x;
10299 ey2 = hpts[1].y;
10300
10301 if (ex1 > ex2) {
10302 var temp = ex1;
10303 ex1 = ex2;
10304 ex2 = temp;
10305 }
10306
10307 if (ey1 > ey2) {
10308 var _temp = ey1;
10309 ey1 = ey2;
10310 ey2 = _temp;
10311 }
10312
10313 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
10314 }
10315 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
10316 var pts;
10317
10318 switch (curveStyle) {
10319 case 'bezier':
10320 case 'unbundled-bezier':
10321 pts = rstyle.bezierPts;
10322 break;
10323
10324 case 'segments':
10325 case 'taxi':
10326 pts = rstyle.linePts;
10327 break;
10328 }
10329
10330 if (pts != null) {
10331 for (var j = 0; j < pts.length; j++) {
10332 var pt = pts[j];
10333 ex1 = pt.x - wHalf;
10334 ex2 = pt.x + wHalf;
10335 ey1 = pt.y - wHalf;
10336 ey2 = pt.y + wHalf;
10337 updateBounds(bounds, ex1, ey1, ex2, ey2);
10338 }
10339 }
10340 } // bezier-like or segment-like edge
10341
10342 } else {
10343 // headless or style disabled
10344 // fallback on source and target positions
10345 //////////////////////////////////////////
10346 var n1 = ele.source();
10347 var n1pos = n1.position();
10348 var n2 = ele.target();
10349 var n2pos = n2.position();
10350 ex1 = n1pos.x;
10351 ex2 = n2pos.x;
10352 ey1 = n1pos.y;
10353 ey2 = n2pos.y;
10354
10355 if (ex1 > ex2) {
10356 var _temp2 = ex1;
10357 ex1 = ex2;
10358 ex2 = _temp2;
10359 }
10360
10361 if (ey1 > ey2) {
10362 var _temp3 = ey1;
10363 ey1 = ey2;
10364 ey2 = _temp3;
10365 } // take into account edge width
10366
10367
10368 ex1 -= wHalf;
10369 ex2 += wHalf;
10370 ey1 -= wHalf;
10371 ey2 += wHalf;
10372 updateBounds(bounds, ex1, ey1, ex2, ey2);
10373 } // headless or style disabled
10374
10375 } // edges
10376 // handle edge arrow size
10377 /////////////////////////
10378
10379
10380 if (styleEnabled && options.includeEdges && isEdge) {
10381 updateBoundsFromArrow(bounds, ele, 'mid-source');
10382 updateBoundsFromArrow(bounds, ele, 'mid-target');
10383 updateBoundsFromArrow(bounds, ele, 'source');
10384 updateBoundsFromArrow(bounds, ele, 'target');
10385 } // ghost
10386 ////////
10387
10388
10389 if (styleEnabled) {
10390 var ghost = ele.pstyle('ghost').value === 'yes';
10391
10392 if (ghost) {
10393 var gx = ele.pstyle('ghost-offset-x').pfValue;
10394 var gy = ele.pstyle('ghost-offset-y').pfValue;
10395 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
10396 }
10397 } // always store the body bounds separately from the labels
10398
10399
10400 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
10401 assignBoundingBox(bbBody, bounds);
10402 expandBoundingBoxSides(bbBody, manualExpansion);
10403 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
10404 // overlay
10405 //////////
10406
10407 if (styleEnabled) {
10408 ex1 = bounds.x1;
10409 ex2 = bounds.x2;
10410 ey1 = bounds.y1;
10411 ey2 = bounds.y2;
10412 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
10413 } // always store the body bounds separately from the labels
10414
10415
10416 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
10417 assignBoundingBox(bbOverlay, bounds);
10418 expandBoundingBoxSides(bbOverlay, manualExpansion);
10419 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
10420 // handle label dimensions
10421 //////////////////////////
10422
10423 var bbLabels = _p.labelBounds = _p.labelBounds || {};
10424
10425 if (bbLabels.all != null) {
10426 clearBoundingBox(bbLabels.all);
10427 } else {
10428 bbLabels.all = makeBoundingBox();
10429 }
10430
10431 if (styleEnabled && options.includeLabels) {
10432 if (options.includeMainLabels) {
10433 updateBoundsFromLabel(bounds, ele, null);
10434 }
10435
10436 if (isEdge) {
10437 if (options.includeSourceLabels) {
10438 updateBoundsFromLabel(bounds, ele, 'source');
10439 }
10440
10441 if (options.includeTargetLabels) {
10442 updateBoundsFromLabel(bounds, ele, 'target');
10443 }
10444 }
10445 } // style enabled for labels
10446
10447 } // if displayed
10448
10449
10450 bounds.x1 = noninf(bounds.x1);
10451 bounds.y1 = noninf(bounds.y1);
10452 bounds.x2 = noninf(bounds.x2);
10453 bounds.y2 = noninf(bounds.y2);
10454 bounds.w = noninf(bounds.x2 - bounds.x1);
10455 bounds.h = noninf(bounds.y2 - bounds.y1);
10456
10457 if (bounds.w > 0 && bounds.h > 0 && displayed) {
10458 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
10459
10460 expandBoundingBox(bounds, 1);
10461 }
10462
10463 return bounds;
10464 };
10465
10466 var getKey = function getKey(opts) {
10467 var i = 0;
10468
10469 var tf = function tf(val) {
10470 return (val ? 1 : 0) << i++;
10471 };
10472
10473 var key = 0;
10474 key += tf(opts.incudeNodes);
10475 key += tf(opts.includeEdges);
10476 key += tf(opts.includeLabels);
10477 key += tf(opts.includeMainLabels);
10478 key += tf(opts.includeSourceLabels);
10479 key += tf(opts.includeTargetLabels);
10480 key += tf(opts.includeOverlays);
10481 return key;
10482 };
10483
10484 var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
10485 if (ele.isEdge()) {
10486 var p1 = ele.source().position();
10487 var p2 = ele.target().position();
10488
10489 var r = function r(x) {
10490 return Math.round(x);
10491 };
10492
10493 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
10494 } else {
10495 return 0;
10496 }
10497 };
10498
10499 var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
10500 var _p = ele._private;
10501 var bb;
10502 var isEdge = ele.isEdge();
10503 var key = opts == null ? defBbOptsKey : getKey(opts);
10504 var usingDefOpts = key === defBbOptsKey;
10505 var currPosKey = getBoundingBoxPosKey(ele);
10506 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10507 var useCache = opts.useCache && isPosKeySame;
10508
10509 var isDirty = function isDirty(ele) {
10510 return ele._private.bbCache == null;
10511 };
10512
10513 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
10514
10515 if (needRecalc) {
10516 if (!isPosKeySame) {
10517 ele.recalculateRenderedStyle();
10518 }
10519
10520 bb = boundingBoxImpl(ele, defBbOpts);
10521 _p.bbCache = bb;
10522 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
10523 _p.bbCachePosKey = currPosKey;
10524 } else {
10525 bb = _p.bbCache;
10526 }
10527
10528 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
10529 var shift = assignShiftToBoundingBox;
10530 var delta = _p.bbCacheShift;
10531
10532 var safeShift = function safeShift(bb, delta) {
10533 if (bb != null) {
10534 shift(bb, delta);
10535 }
10536 };
10537
10538 shift(bb, delta);
10539 var bodyBounds = _p.bodyBounds,
10540 overlayBounds = _p.overlayBounds,
10541 labelBounds = _p.labelBounds,
10542 arrowBounds = _p.arrowBounds;
10543 safeShift(bodyBounds, delta);
10544 safeShift(overlayBounds, delta);
10545
10546 if (arrowBounds != null) {
10547 safeShift(arrowBounds.source, delta);
10548 safeShift(arrowBounds.target, delta);
10549 safeShift(arrowBounds['mid-source'], delta);
10550 safeShift(arrowBounds['mid-target'], delta);
10551 }
10552
10553 if (labelBounds != null) {
10554 safeShift(labelBounds.main, delta);
10555 safeShift(labelBounds.all, delta);
10556 safeShift(labelBounds.source, delta);
10557 safeShift(labelBounds.target, delta);
10558 }
10559 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
10560
10561
10562 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
10563
10564 if (!usingDefOpts) {
10565 var isNode = ele.isNode();
10566 bb = makeBoundingBox();
10567
10568 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
10569 if (opts.includeOverlays) {
10570 updateBoundsFromBox(bb, _p.overlayBounds);
10571 } else {
10572 updateBoundsFromBox(bb, _p.bodyBounds);
10573 }
10574 }
10575
10576 if (opts.includeLabels) {
10577 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
10578 updateBoundsFromBox(bb, _p.labelBounds.all);
10579 } else {
10580 if (opts.includeMainLabels) {
10581 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
10582 }
10583
10584 if (opts.includeSourceLabels) {
10585 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
10586 }
10587
10588 if (opts.includeTargetLabels) {
10589 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
10590 }
10591 }
10592 }
10593
10594 bb.w = bb.x2 - bb.x1;
10595 bb.h = bb.y2 - bb.y1;
10596 }
10597
10598 return bb;
10599 };
10600
10601 var defBbOpts = {
10602 includeNodes: true,
10603 includeEdges: true,
10604 includeLabels: true,
10605 includeMainLabels: true,
10606 includeSourceLabels: true,
10607 includeTargetLabels: true,
10608 includeOverlays: true,
10609 useCache: true
10610 };
10611 var defBbOptsKey = getKey(defBbOpts);
10612 var filledBbOpts = defaults(defBbOpts);
10613
10614 elesfn$k.boundingBox = function (options) {
10615 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
10616 // specified s.t. the cache is used, so check for this case to make it faster by
10617 // avoiding the overhead of the rest of the function
10618
10619 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
10620 if (options === undefined) {
10621 options = defBbOpts;
10622 } else {
10623 options = filledBbOpts(options);
10624 }
10625
10626 bounds = cachedBoundingBoxImpl(this[0], options);
10627 } else {
10628 bounds = makeBoundingBox();
10629 options = options || defBbOpts;
10630 var opts = filledBbOpts(options);
10631 var eles = this;
10632 var cy = eles.cy();
10633 var styleEnabled = cy.styleEnabled();
10634
10635 if (styleEnabled) {
10636 for (var i = 0; i < eles.length; i++) {
10637 var ele = eles[i];
10638 var _p = ele._private;
10639 var currPosKey = getBoundingBoxPosKey(ele);
10640 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10641 var useCache = opts.useCache && isPosKeySame;
10642 ele.recalculateRenderedStyle(useCache);
10643 }
10644 }
10645
10646 this.updateCompoundBounds();
10647
10648 for (var _i = 0; _i < eles.length; _i++) {
10649 var _ele = eles[_i];
10650 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
10651 }
10652 }
10653
10654 bounds.x1 = noninf(bounds.x1);
10655 bounds.y1 = noninf(bounds.y1);
10656 bounds.x2 = noninf(bounds.x2);
10657 bounds.y2 = noninf(bounds.y2);
10658 bounds.w = noninf(bounds.x2 - bounds.x1);
10659 bounds.h = noninf(bounds.y2 - bounds.y1);
10660 return bounds;
10661 };
10662
10663 elesfn$k.dirtyBoundingBoxCache = function () {
10664 for (var i = 0; i < this.length; i++) {
10665 var _p = this[i]._private;
10666 _p.bbCache = null;
10667 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
10668 _p.bbCachePosKey = null;
10669 _p.bodyBounds = null;
10670 _p.overlayBounds = null;
10671 _p.labelBounds.all = null;
10672 _p.labelBounds.source = null;
10673 _p.labelBounds.target = null;
10674 _p.labelBounds.main = null;
10675 _p.labelBounds.sourceRot = null;
10676 _p.labelBounds.targetRot = null;
10677 _p.labelBounds.mainRot = null;
10678 _p.arrowBounds.source = null;
10679 _p.arrowBounds.target = null;
10680 _p.arrowBounds['mid-source'] = null;
10681 _p.arrowBounds['mid-target'] = null;
10682 }
10683
10684 this.emitAndNotify('bounds');
10685 return this;
10686 };
10687
10688 elesfn$k.shiftCachedBoundingBox = function (delta) {
10689 for (var i = 0; i < this.length; i++) {
10690 var ele = this[i];
10691 var _p = ele._private;
10692 var bb = _p.bbCache;
10693
10694 if (bb != null) {
10695 _p.bbCacheShift.x += delta.x;
10696 _p.bbCacheShift.y += delta.y;
10697 }
10698 }
10699
10700 this.emitAndNotify('bounds');
10701 return this;
10702 }; // private helper to get bounding box for custom node positions
10703 // - good for perf in certain cases but currently requires dirtying the rendered style
10704 // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
10705 // - try to use for only things like discrete layouts where the node position would change anyway
10706
10707
10708 elesfn$k.boundingBoxAt = function (fn) {
10709 var nodes = this.nodes();
10710 var cy = this.cy();
10711 var hasCompoundNodes = cy.hasCompoundNodes();
10712
10713 if (hasCompoundNodes) {
10714 nodes = nodes.filter(function (node) {
10715 return !node.isParent();
10716 });
10717 }
10718
10719 if (plainObject(fn)) {
10720 var obj = fn;
10721
10722 fn = function fn() {
10723 return obj;
10724 };
10725 }
10726
10727 var storeOldPos = function storeOldPos(node, i) {
10728 return node._private.bbAtOldPos = fn(node, i);
10729 };
10730
10731 var getOldPos = function getOldPos(node) {
10732 return node._private.bbAtOldPos;
10733 };
10734
10735 cy.startBatch();
10736 nodes.forEach(storeOldPos).silentPositions(fn);
10737
10738 if (hasCompoundNodes) {
10739 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10740 }
10741
10742 var bb = copyBoundingBox(this.boundingBox({
10743 useCache: false
10744 }));
10745 nodes.silentPositions(getOldPos);
10746 cy.endBatch();
10747 return bb;
10748 };
10749
10750 fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10751 fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10752 var bounds = elesfn$k;
10753
10754 var fn$4, elesfn$l;
10755 fn$4 = elesfn$l = {};
10756
10757 var defineDimFns = function defineDimFns(opts) {
10758 opts.uppercaseName = capitalize(opts.name);
10759 opts.autoName = 'auto' + opts.uppercaseName;
10760 opts.labelName = 'label' + opts.uppercaseName;
10761 opts.outerName = 'outer' + opts.uppercaseName;
10762 opts.uppercaseOuterName = capitalize(opts.outerName);
10763
10764 fn$4[opts.name] = function dimImpl() {
10765 var ele = this[0];
10766 var _p = ele._private;
10767 var cy = _p.cy;
10768 var styleEnabled = cy._private.styleEnabled;
10769
10770 if (ele) {
10771 if (styleEnabled) {
10772 if (ele.isParent()) {
10773 ele.updateCompoundBounds();
10774 return _p[opts.autoName] || 0;
10775 }
10776
10777 var d = ele.pstyle(opts.name);
10778
10779 switch (d.strValue) {
10780 case 'label':
10781 ele.recalculateRenderedStyle();
10782 return _p.rstyle[opts.labelName] || 0;
10783
10784 default:
10785 return d.pfValue;
10786 }
10787 } else {
10788 return 1;
10789 }
10790 }
10791 };
10792
10793 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10794 var ele = this[0];
10795 var _p = ele._private;
10796 var cy = _p.cy;
10797 var styleEnabled = cy._private.styleEnabled;
10798
10799 if (ele) {
10800 if (styleEnabled) {
10801 var dim = ele[opts.name]();
10802 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10803
10804 var padding = 2 * ele.padding();
10805 return dim + border + padding;
10806 } else {
10807 return 1;
10808 }
10809 }
10810 };
10811
10812 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10813 var ele = this[0];
10814
10815 if (ele) {
10816 var d = ele[opts.name]();
10817 return d * this.cy().zoom();
10818 }
10819 };
10820
10821 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10822 var ele = this[0];
10823
10824 if (ele) {
10825 var od = ele[opts.outerName]();
10826 return od * this.cy().zoom();
10827 }
10828 };
10829 };
10830
10831 defineDimFns({
10832 name: 'width'
10833 });
10834 defineDimFns({
10835 name: 'height'
10836 });
10837
10838 elesfn$l.padding = function () {
10839 var ele = this[0];
10840 var _p = ele._private;
10841
10842 if (ele.isParent()) {
10843 ele.updateCompoundBounds();
10844
10845 if (_p.autoPadding !== undefined) {
10846 return _p.autoPadding;
10847 } else {
10848 return ele.pstyle('padding').pfValue;
10849 }
10850 } else {
10851 return ele.pstyle('padding').pfValue;
10852 }
10853 };
10854
10855 elesfn$l.paddedHeight = function () {
10856 var ele = this[0];
10857 return ele.height() + 2 * ele.padding();
10858 };
10859
10860 elesfn$l.paddedWidth = function () {
10861 var ele = this[0];
10862 return ele.width() + 2 * ele.padding();
10863 };
10864
10865 var widthHeight = elesfn$l;
10866
10867 var ifEdge = function ifEdge(ele, getValue) {
10868 if (ele.isEdge()) {
10869 return getValue(ele);
10870 }
10871 };
10872
10873 var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10874 if (ele.isEdge()) {
10875 var cy = ele.cy();
10876 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10877 }
10878 };
10879
10880 var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10881 if (ele.isEdge()) {
10882 var cy = ele.cy();
10883 var pan = cy.pan();
10884 var zoom = cy.zoom();
10885 return getPoints(ele).map(function (p) {
10886 return modelToRenderedPosition(p, zoom, pan);
10887 });
10888 }
10889 };
10890
10891 var controlPoints = function controlPoints(ele) {
10892 return ele.renderer().getControlPoints(ele);
10893 };
10894
10895 var segmentPoints = function segmentPoints(ele) {
10896 return ele.renderer().getSegmentPoints(ele);
10897 };
10898
10899 var sourceEndpoint = function sourceEndpoint(ele) {
10900 return ele.renderer().getSourceEndpoint(ele);
10901 };
10902
10903 var targetEndpoint = function targetEndpoint(ele) {
10904 return ele.renderer().getTargetEndpoint(ele);
10905 };
10906
10907 var midpoint = function midpoint(ele) {
10908 return ele.renderer().getEdgeMidpoint(ele);
10909 };
10910
10911 var pts = {
10912 controlPoints: {
10913 get: controlPoints,
10914 mult: true
10915 },
10916 segmentPoints: {
10917 get: segmentPoints,
10918 mult: true
10919 },
10920 sourceEndpoint: {
10921 get: sourceEndpoint
10922 },
10923 targetEndpoint: {
10924 get: targetEndpoint
10925 },
10926 midpoint: {
10927 get: midpoint
10928 }
10929 };
10930
10931 var renderedName = function renderedName(name) {
10932 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10933 };
10934
10935 var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10936 var spec = pts[name];
10937 var rName = renderedName(name);
10938
10939 obj[name] = function () {
10940 return ifEdge(this, spec.get);
10941 };
10942
10943 if (spec.mult) {
10944 obj[rName] = function () {
10945 return ifEdgeRenderedPositions(this, spec.get);
10946 };
10947 } else {
10948 obj[rName] = function () {
10949 return ifEdgeRenderedPosition(this, spec.get);
10950 };
10951 }
10952
10953 return obj;
10954 }, {});
10955
10956 var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10957
10958 /*!
10959 Event object based on jQuery events, MIT license
10960
10961 https://jquery.org/license/
10962 https://tldrlegal.com/license/mit-license
10963 https://github.com/jquery/jquery/blob/master/src/event.js
10964 */
10965 var Event = function Event(src, props) {
10966 this.recycle(src, props);
10967 };
10968
10969 function returnFalse() {
10970 return false;
10971 }
10972
10973 function returnTrue() {
10974 return true;
10975 } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10976
10977
10978 Event.prototype = {
10979 instanceString: function instanceString() {
10980 return 'event';
10981 },
10982 recycle: function recycle(src, props) {
10983 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10984
10985 if (src != null && src.preventDefault) {
10986 // Browser Event object
10987 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10988 // by a handler lower down the tree; reflect the correct value.
10989
10990 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10991 } else if (src != null && src.type) {
10992 // Plain object containing all event details
10993 props = src;
10994 } else {
10995 // Event string
10996 this.type = src;
10997 } // Put explicitly provided properties onto the event object
10998
10999
11000 if (props != null) {
11001 // more efficient to manually copy fields we use
11002 this.originalEvent = props.originalEvent;
11003 this.type = props.type != null ? props.type : this.type;
11004 this.cy = props.cy;
11005 this.target = props.target;
11006 this.position = props.position;
11007 this.renderedPosition = props.renderedPosition;
11008 this.namespace = props.namespace;
11009 this.layout = props.layout;
11010 }
11011
11012 if (this.cy != null && this.position != null && this.renderedPosition == null) {
11013 // create a rendered position based on the passed position
11014 var pos = this.position;
11015 var zoom = this.cy.zoom();
11016 var pan = this.cy.pan();
11017 this.renderedPosition = {
11018 x: pos.x * zoom + pan.x,
11019 y: pos.y * zoom + pan.y
11020 };
11021 } // Create a timestamp if incoming event doesn't have one
11022
11023
11024 this.timeStamp = src && src.timeStamp || Date.now();
11025 },
11026 preventDefault: function preventDefault() {
11027 this.isDefaultPrevented = returnTrue;
11028 var e = this.originalEvent;
11029
11030 if (!e) {
11031 return;
11032 } // if preventDefault exists run it on the original event
11033
11034
11035 if (e.preventDefault) {
11036 e.preventDefault();
11037 }
11038 },
11039 stopPropagation: function stopPropagation() {
11040 this.isPropagationStopped = returnTrue;
11041 var e = this.originalEvent;
11042
11043 if (!e) {
11044 return;
11045 } // if stopPropagation exists run it on the original event
11046
11047
11048 if (e.stopPropagation) {
11049 e.stopPropagation();
11050 }
11051 },
11052 stopImmediatePropagation: function stopImmediatePropagation() {
11053 this.isImmediatePropagationStopped = returnTrue;
11054 this.stopPropagation();
11055 },
11056 isDefaultPrevented: returnFalse,
11057 isPropagationStopped: returnFalse,
11058 isImmediatePropagationStopped: returnFalse
11059 };
11060
11061 var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
11062
11063 var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
11064
11065 var defaults$8 = {
11066 qualifierCompare: function qualifierCompare(q1, q2) {
11067 return q1 === q2;
11068 },
11069 eventMatches: function eventMatches()
11070 /*context, listener, eventObj*/
11071 {
11072 return true;
11073 },
11074 addEventFields: function addEventFields()
11075 /*context, evt*/
11076 {},
11077 callbackContext: function callbackContext(context
11078 /*, listener, eventObj*/
11079 ) {
11080 return context;
11081 },
11082 beforeEmit: function beforeEmit()
11083 /* context, listener, eventObj */
11084 {},
11085 afterEmit: function afterEmit()
11086 /* context, listener, eventObj */
11087 {},
11088 bubble: function bubble()
11089 /*context*/
11090 {
11091 return false;
11092 },
11093 parent: function parent()
11094 /*context*/
11095 {
11096 return null;
11097 },
11098 context: null
11099 };
11100 var defaultsKeys = Object.keys(defaults$8);
11101 var emptyOpts = {};
11102
11103 function Emitter() {
11104 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
11105 var context = arguments.length > 1 ? arguments[1] : undefined;
11106
11107 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
11108 for (var i = 0; i < defaultsKeys.length; i++) {
11109 var key = defaultsKeys[i];
11110 this[key] = opts[key] || defaults$8[key];
11111 }
11112
11113 this.context = context || this.context;
11114 this.listeners = [];
11115 this.emitting = 0;
11116 }
11117
11118 var p = Emitter.prototype;
11119
11120 var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
11121 if (fn(qualifier)) {
11122 callback = qualifier;
11123 qualifier = null;
11124 }
11125
11126 if (confOverrides) {
11127 if (conf == null) {
11128 conf = confOverrides;
11129 } else {
11130 conf = extend({}, conf, confOverrides);
11131 }
11132 }
11133
11134 var eventList = array(events) ? events : events.split(/\s+/);
11135
11136 for (var i = 0; i < eventList.length; i++) {
11137 var evt = eventList[i];
11138
11139 if (emptyString(evt)) {
11140 continue;
11141 }
11142
11143 var match = evt.match(eventRegex); // type[.namespace]
11144
11145 if (match) {
11146 var type = match[1];
11147 var namespace = match[2] ? match[2] : null;
11148 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
11149
11150 if (ret === false) {
11151 break;
11152 } // allow exiting early
11153
11154 }
11155 }
11156 };
11157
11158 var makeEventObj = function makeEventObj(self, obj) {
11159 self.addEventFields(self.context, obj);
11160 return new Event(obj.type, obj);
11161 };
11162
11163 var forEachEventObj = function forEachEventObj(self, handler, events) {
11164 if (event(events)) {
11165 handler(self, events);
11166 return;
11167 } else if (plainObject(events)) {
11168 handler(self, makeEventObj(self, events));
11169 return;
11170 }
11171
11172 var eventList = array(events) ? events : events.split(/\s+/);
11173
11174 for (var i = 0; i < eventList.length; i++) {
11175 var evt = eventList[i];
11176
11177 if (emptyString(evt)) {
11178 continue;
11179 }
11180
11181 var match = evt.match(eventRegex); // type[.namespace]
11182
11183 if (match) {
11184 var type = match[1];
11185 var namespace = match[2] ? match[2] : null;
11186 var eventObj = makeEventObj(self, {
11187 type: type,
11188 namespace: namespace,
11189 target: self.context
11190 });
11191 handler(self, eventObj);
11192 }
11193 }
11194 };
11195
11196 p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
11197 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
11198 if (fn(callback)) {
11199 self.listeners.push({
11200 event: event,
11201 // full event string
11202 callback: callback,
11203 // callback to run
11204 type: type,
11205 // the event type (e.g. 'click')
11206 namespace: namespace,
11207 // the event namespace (e.g. ".foo")
11208 qualifier: qualifier,
11209 // a restriction on whether to match this emitter
11210 conf: conf // additional configuration
11211
11212 });
11213 }
11214 }, events, qualifier, callback, conf, confOverrides);
11215 return this;
11216 };
11217
11218 p.one = function (events, qualifier, callback, conf) {
11219 return this.on(events, qualifier, callback, conf, {
11220 one: true
11221 });
11222 };
11223
11224 p.removeListener = p.off = function (events, qualifier, callback, conf) {
11225 var _this = this;
11226
11227 if (this.emitting !== 0) {
11228 this.listeners = copyArray(this.listeners);
11229 }
11230
11231 var listeners = this.listeners;
11232
11233 var _loop = function _loop(i) {
11234 var listener = listeners[i];
11235 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
11236 /*, conf*/
11237 ) {
11238 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
11239 listeners.splice(i, 1);
11240 return false;
11241 }
11242 }, events, qualifier, callback, conf);
11243 };
11244
11245 for (var i = listeners.length - 1; i >= 0; i--) {
11246 _loop(i);
11247 }
11248
11249 return this;
11250 };
11251
11252 p.removeAllListeners = function () {
11253 return this.removeListener('*');
11254 };
11255
11256 p.emit = p.trigger = function (events, extraParams, manualCallback) {
11257 var listeners = this.listeners;
11258 var numListenersBeforeEmit = listeners.length;
11259 this.emitting++;
11260
11261 if (!array(extraParams)) {
11262 extraParams = [extraParams];
11263 }
11264
11265 forEachEventObj(this, function (self, eventObj) {
11266 if (manualCallback != null) {
11267 listeners = [{
11268 event: eventObj.event,
11269 type: eventObj.type,
11270 namespace: eventObj.namespace,
11271 callback: manualCallback
11272 }];
11273 numListenersBeforeEmit = listeners.length;
11274 }
11275
11276 var _loop2 = function _loop2(i) {
11277 var listener = listeners[i];
11278
11279 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
11280 var args = [eventObj];
11281
11282 if (extraParams != null) {
11283 push(args, extraParams);
11284 }
11285
11286 self.beforeEmit(self.context, listener, eventObj);
11287
11288 if (listener.conf && listener.conf.one) {
11289 self.listeners = self.listeners.filter(function (l) {
11290 return l !== listener;
11291 });
11292 }
11293
11294 var context = self.callbackContext(self.context, listener, eventObj);
11295 var ret = listener.callback.apply(context, args);
11296 self.afterEmit(self.context, listener, eventObj);
11297
11298 if (ret === false) {
11299 eventObj.stopPropagation();
11300 eventObj.preventDefault();
11301 }
11302 } // if listener matches
11303
11304 };
11305
11306 for (var i = 0; i < numListenersBeforeEmit; i++) {
11307 _loop2(i);
11308 } // for listener
11309
11310
11311 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
11312 self.parent(self.context).emit(eventObj, extraParams);
11313 }
11314 }, events);
11315 this.emitting--;
11316 return this;
11317 };
11318
11319 var emitterOptions = {
11320 qualifierCompare: function qualifierCompare(selector1, selector2) {
11321 if (selector1 == null || selector2 == null) {
11322 return selector1 == null && selector2 == null;
11323 } else {
11324 return selector1.sameText(selector2);
11325 }
11326 },
11327 eventMatches: function eventMatches(ele, listener, eventObj) {
11328 var selector = listener.qualifier;
11329
11330 if (selector != null) {
11331 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
11332 }
11333
11334 return true;
11335 },
11336 addEventFields: function addEventFields(ele, evt) {
11337 evt.cy = ele.cy();
11338 evt.target = ele;
11339 },
11340 callbackContext: function callbackContext(ele, listener, eventObj) {
11341 return listener.qualifier != null ? eventObj.target : ele;
11342 },
11343 beforeEmit: function beforeEmit(context, listener
11344 /*, eventObj*/
11345 ) {
11346 if (listener.conf && listener.conf.once) {
11347 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
11348 }
11349 },
11350 bubble: function bubble() {
11351 return true;
11352 },
11353 parent: function parent(ele) {
11354 return ele.isChild() ? ele.parent() : ele.cy();
11355 }
11356 };
11357
11358 var argSelector = function argSelector(arg) {
11359 if (string(arg)) {
11360 return new Selector(arg);
11361 } else {
11362 return arg;
11363 }
11364 };
11365
11366 var elesfn$m = {
11367 createEmitter: function createEmitter() {
11368 for (var i = 0; i < this.length; i++) {
11369 var ele = this[i];
11370 var _p = ele._private;
11371
11372 if (!_p.emitter) {
11373 _p.emitter = new Emitter(emitterOptions, ele);
11374 }
11375 }
11376
11377 return this;
11378 },
11379 emitter: function emitter() {
11380 return this._private.emitter;
11381 },
11382 on: function on(events, selector, callback) {
11383 var argSel = argSelector(selector);
11384
11385 for (var i = 0; i < this.length; i++) {
11386 var ele = this[i];
11387 ele.emitter().on(events, argSel, callback);
11388 }
11389
11390 return this;
11391 },
11392 removeListener: function removeListener(events, selector, callback) {
11393 var argSel = argSelector(selector);
11394
11395 for (var i = 0; i < this.length; i++) {
11396 var ele = this[i];
11397 ele.emitter().removeListener(events, argSel, callback);
11398 }
11399
11400 return this;
11401 },
11402 removeAllListeners: function removeAllListeners() {
11403 for (var i = 0; i < this.length; i++) {
11404 var ele = this[i];
11405 ele.emitter().removeAllListeners();
11406 }
11407
11408 return this;
11409 },
11410 one: function one(events, selector, callback) {
11411 var argSel = argSelector(selector);
11412
11413 for (var i = 0; i < this.length; i++) {
11414 var ele = this[i];
11415 ele.emitter().one(events, argSel, callback);
11416 }
11417
11418 return this;
11419 },
11420 once: function once(events, selector, callback) {
11421 var argSel = argSelector(selector);
11422
11423 for (var i = 0; i < this.length; i++) {
11424 var ele = this[i];
11425 ele.emitter().on(events, argSel, callback, {
11426 once: true,
11427 onceCollection: this
11428 });
11429 }
11430 },
11431 emit: function emit(events, extraParams) {
11432 for (var i = 0; i < this.length; i++) {
11433 var ele = this[i];
11434 ele.emitter().emit(events, extraParams);
11435 }
11436
11437 return this;
11438 },
11439 emitAndNotify: function emitAndNotify(event, extraParams) {
11440 // for internal use only
11441 if (this.length === 0) {
11442 return;
11443 } // empty collections don't need to notify anything
11444 // notify renderer
11445
11446
11447 this.cy().notify(event, this);
11448 this.emit(event, extraParams);
11449 return this;
11450 }
11451 };
11452 define$3.eventAliasesOn(elesfn$m);
11453
11454 var elesfn$n = {
11455 nodes: function nodes(selector) {
11456 return this.filter(function (ele) {
11457 return ele.isNode();
11458 }).filter(selector);
11459 },
11460 edges: function edges(selector) {
11461 return this.filter(function (ele) {
11462 return ele.isEdge();
11463 }).filter(selector);
11464 },
11465 // internal helper to get nodes and edges as separate collections with single iteration over elements
11466 byGroup: function byGroup() {
11467 var nodes = this.spawn();
11468 var edges = this.spawn();
11469
11470 for (var i = 0; i < this.length; i++) {
11471 var ele = this[i];
11472
11473 if (ele.isNode()) {
11474 nodes.merge(ele);
11475 } else {
11476 edges.merge(ele);
11477 }
11478 }
11479
11480 return {
11481 nodes: nodes,
11482 edges: edges
11483 };
11484 },
11485 filter: function filter(_filter, thisArg) {
11486 if (_filter === undefined) {
11487 // check this first b/c it's the most common/performant case
11488 return this;
11489 } else if (string(_filter) || elementOrCollection(_filter)) {
11490 return new Selector(_filter).filter(this);
11491 } else if (fn(_filter)) {
11492 var filterEles = this.spawn();
11493 var eles = this;
11494
11495 for (var i = 0; i < eles.length; i++) {
11496 var ele = eles[i];
11497 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
11498
11499 if (include) {
11500 filterEles.merge(ele);
11501 }
11502 }
11503
11504 return filterEles;
11505 }
11506
11507 return this.spawn(); // if not handled by above, give 'em an empty collection
11508 },
11509 not: function not(toRemove) {
11510 if (!toRemove) {
11511 return this;
11512 } else {
11513 if (string(toRemove)) {
11514 toRemove = this.filter(toRemove);
11515 }
11516
11517 var elements = [];
11518 var rMap = toRemove._private.map;
11519
11520 for (var i = 0; i < this.length; i++) {
11521 var element = this[i];
11522 var remove = rMap.has(element.id());
11523
11524 if (!remove) {
11525 elements.push(element);
11526 }
11527 }
11528
11529 return this.spawn(elements);
11530 }
11531 },
11532 absoluteComplement: function absoluteComplement() {
11533 var cy = this.cy();
11534 return cy.mutableElements().not(this);
11535 },
11536 intersect: function intersect(other) {
11537 // if a selector is specified, then filter by it instead
11538 if (string(other)) {
11539 var selector = other;
11540 return this.filter(selector);
11541 }
11542
11543 var elements = [];
11544 var col1 = this;
11545 var col2 = other;
11546 var col1Smaller = this.length < other.length;
11547 var map2 = col1Smaller ? col2._private.map : col1._private.map;
11548 var col = col1Smaller ? col1 : col2;
11549
11550 for (var i = 0; i < col.length; i++) {
11551 var id = col[i]._private.data.id;
11552 var entry = map2.get(id);
11553
11554 if (entry) {
11555 elements.push(entry.ele);
11556 }
11557 }
11558
11559 return this.spawn(elements);
11560 },
11561 xor: function xor(other) {
11562 var cy = this._private.cy;
11563
11564 if (string(other)) {
11565 other = cy.$(other);
11566 }
11567
11568 var elements = [];
11569 var col1 = this;
11570 var col2 = other;
11571
11572 var add = function add(col, other) {
11573 for (var i = 0; i < col.length; i++) {
11574 var ele = col[i];
11575 var id = ele._private.data.id;
11576 var inOther = other.hasElementWithId(id);
11577
11578 if (!inOther) {
11579 elements.push(ele);
11580 }
11581 }
11582 };
11583
11584 add(col1, col2);
11585 add(col2, col1);
11586 return this.spawn(elements);
11587 },
11588 diff: function diff(other) {
11589 var cy = this._private.cy;
11590
11591 if (string(other)) {
11592 other = cy.$(other);
11593 }
11594
11595 var left = [];
11596 var right = [];
11597 var both = [];
11598 var col1 = this;
11599 var col2 = other;
11600
11601 var add = function add(col, other, retEles) {
11602 for (var i = 0; i < col.length; i++) {
11603 var ele = col[i];
11604 var id = ele._private.data.id;
11605 var inOther = other.hasElementWithId(id);
11606
11607 if (inOther) {
11608 both.push(ele);
11609 } else {
11610 retEles.push(ele);
11611 }
11612 }
11613 };
11614
11615 add(col1, col2, left);
11616 add(col2, col1, right);
11617 return {
11618 left: this.spawn(left, {
11619 unique: true
11620 }),
11621 right: this.spawn(right, {
11622 unique: true
11623 }),
11624 both: this.spawn(both, {
11625 unique: true
11626 })
11627 };
11628 },
11629 add: function add(toAdd) {
11630 var cy = this._private.cy;
11631
11632 if (!toAdd) {
11633 return this;
11634 }
11635
11636 if (string(toAdd)) {
11637 var selector = toAdd;
11638 toAdd = cy.mutableElements().filter(selector);
11639 }
11640
11641 var elements = [];
11642
11643 for (var i = 0; i < this.length; i++) {
11644 elements.push(this[i]);
11645 }
11646
11647 var map = this._private.map;
11648
11649 for (var _i = 0; _i < toAdd.length; _i++) {
11650 var add = !map.has(toAdd[_i].id());
11651
11652 if (add) {
11653 elements.push(toAdd[_i]);
11654 }
11655 }
11656
11657 return this.spawn(elements);
11658 },
11659 // in place merge on calling collection
11660 merge: function merge(toAdd) {
11661 var _p = this._private;
11662 var cy = _p.cy;
11663
11664 if (!toAdd) {
11665 return this;
11666 }
11667
11668 if (toAdd && string(toAdd)) {
11669 var selector = toAdd;
11670 toAdd = cy.mutableElements().filter(selector);
11671 }
11672
11673 var map = _p.map;
11674
11675 for (var i = 0; i < toAdd.length; i++) {
11676 var toAddEle = toAdd[i];
11677 var id = toAddEle._private.data.id;
11678 var add = !map.has(id);
11679
11680 if (add) {
11681 var index = this.length++;
11682 this[index] = toAddEle;
11683 map.set(id, {
11684 ele: toAddEle,
11685 index: index
11686 });
11687 } else {
11688 // replace
11689 var _index = map.get(id).index;
11690 this[_index] = toAddEle;
11691 map.set(id, {
11692 ele: toAddEle,
11693 index: _index
11694 });
11695 }
11696 }
11697
11698 return this; // chaining
11699 },
11700 unmergeAt: function unmergeAt(i) {
11701 var ele = this[i];
11702 var id = ele.id();
11703 var _p = this._private;
11704 var map = _p.map; // remove ele
11705
11706 this[i] = undefined;
11707 map["delete"](id);
11708 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
11709
11710 if (this.length > 1 && !unmergedLastEle) {
11711 var lastEleI = this.length - 1;
11712 var lastEle = this[lastEleI];
11713 var lastEleId = lastEle._private.data.id;
11714 this[lastEleI] = undefined;
11715 this[i] = lastEle;
11716 map.set(lastEleId, {
11717 ele: lastEle,
11718 index: i
11719 });
11720 } // the collection is now 1 ele smaller
11721
11722
11723 this.length--;
11724 return this;
11725 },
11726 // remove single ele in place in calling collection
11727 unmergeOne: function unmergeOne(ele) {
11728 ele = ele[0];
11729 var _p = this._private;
11730 var id = ele._private.data.id;
11731 var map = _p.map;
11732 var entry = map.get(id);
11733
11734 if (!entry) {
11735 return this; // no need to remove
11736 }
11737
11738 var i = entry.index;
11739 this.unmergeAt(i);
11740 return this;
11741 },
11742 // remove eles in place on calling collection
11743 unmerge: function unmerge(toRemove) {
11744 var cy = this._private.cy;
11745
11746 if (!toRemove) {
11747 return this;
11748 }
11749
11750 if (toRemove && string(toRemove)) {
11751 var selector = toRemove;
11752 toRemove = cy.mutableElements().filter(selector);
11753 }
11754
11755 for (var i = 0; i < toRemove.length; i++) {
11756 this.unmergeOne(toRemove[i]);
11757 }
11758
11759 return this; // chaining
11760 },
11761 unmergeBy: function unmergeBy(toRmFn) {
11762 for (var i = this.length - 1; i >= 0; i--) {
11763 var ele = this[i];
11764
11765 if (toRmFn(ele)) {
11766 this.unmergeAt(i);
11767 }
11768 }
11769
11770 return this;
11771 },
11772 map: function map(mapFn, thisArg) {
11773 var arr = [];
11774 var eles = this;
11775
11776 for (var i = 0; i < eles.length; i++) {
11777 var ele = eles[i];
11778 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11779 arr.push(ret);
11780 }
11781
11782 return arr;
11783 },
11784 reduce: function reduce(fn, initialValue) {
11785 var val = initialValue;
11786 var eles = this;
11787
11788 for (var i = 0; i < eles.length; i++) {
11789 val = fn(val, eles[i], i, eles);
11790 }
11791
11792 return val;
11793 },
11794 max: function max(valFn, thisArg) {
11795 var max = -Infinity;
11796 var maxEle;
11797 var eles = this;
11798
11799 for (var i = 0; i < eles.length; i++) {
11800 var ele = eles[i];
11801 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11802
11803 if (val > max) {
11804 max = val;
11805 maxEle = ele;
11806 }
11807 }
11808
11809 return {
11810 value: max,
11811 ele: maxEle
11812 };
11813 },
11814 min: function min(valFn, thisArg) {
11815 var min = Infinity;
11816 var minEle;
11817 var eles = this;
11818
11819 for (var i = 0; i < eles.length; i++) {
11820 var ele = eles[i];
11821 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11822
11823 if (val < min) {
11824 min = val;
11825 minEle = ele;
11826 }
11827 }
11828
11829 return {
11830 value: min,
11831 ele: minEle
11832 };
11833 }
11834 }; // aliases
11835
11836 var fn$5 = elesfn$n;
11837 fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11838 fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11839 fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11840 fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11841 fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11842 fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11843
11844 var elesfn$o = {
11845 isNode: function isNode() {
11846 return this.group() === 'nodes';
11847 },
11848 isEdge: function isEdge() {
11849 return this.group() === 'edges';
11850 },
11851 isLoop: function isLoop() {
11852 return this.isEdge() && this.source()[0] === this.target()[0];
11853 },
11854 isSimple: function isSimple() {
11855 return this.isEdge() && this.source()[0] !== this.target()[0];
11856 },
11857 group: function group() {
11858 var ele = this[0];
11859
11860 if (ele) {
11861 return ele._private.group;
11862 }
11863 }
11864 };
11865
11866 /**
11867 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11868 * and z-index (low to high). These styles affect how this applies:
11869 *
11870 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11871 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11872 * root to leaves of the compound graph. The last drawn is `top`.
11873 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11874 * `manual` ignores this convention and draws based on the `z-index` value setting.
11875 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11876 * `z-index` will be drawn on top of an element with a lower `z-index`.
11877 */
11878
11879 var zIndexSort = function zIndexSort(a, b) {
11880 var cy = a.cy();
11881 var hasCompoundNodes = cy.hasCompoundNodes();
11882
11883 function getDepth(ele) {
11884 var style = ele.pstyle('z-compound-depth');
11885
11886 if (style.value === 'auto') {
11887 return hasCompoundNodes ? ele.zDepth() : 0;
11888 } else if (style.value === 'bottom') {
11889 return -1;
11890 } else if (style.value === 'top') {
11891 return MAX_INT;
11892 } // 'orphan'
11893
11894
11895 return 0;
11896 }
11897
11898 var depthDiff = getDepth(a) - getDepth(b);
11899
11900 if (depthDiff !== 0) {
11901 return depthDiff;
11902 }
11903
11904 function getEleDepth(ele) {
11905 var style = ele.pstyle('z-index-compare');
11906
11907 if (style.value === 'auto') {
11908 return ele.isNode() ? 1 : 0;
11909 } // 'manual'
11910
11911
11912 return 0;
11913 }
11914
11915 var eleDiff = getEleDepth(a) - getEleDepth(b);
11916
11917 if (eleDiff !== 0) {
11918 return eleDiff;
11919 }
11920
11921 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11922
11923 if (zDiff !== 0) {
11924 return zDiff;
11925 } // compare indices in the core (order added to graph w/ last on top)
11926
11927
11928 return a.poolIndex() - b.poolIndex();
11929 };
11930
11931 var elesfn$p = {
11932 forEach: function forEach(fn$1, thisArg) {
11933 if (fn(fn$1)) {
11934 var N = this.length;
11935
11936 for (var i = 0; i < N; i++) {
11937 var ele = this[i];
11938 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11939
11940 if (ret === false) {
11941 break;
11942 } // exit each early on return false
11943
11944 }
11945 }
11946
11947 return this;
11948 },
11949 toArray: function toArray() {
11950 var array = [];
11951
11952 for (var i = 0; i < this.length; i++) {
11953 array.push(this[i]);
11954 }
11955
11956 return array;
11957 },
11958 slice: function slice(start, end) {
11959 var array = [];
11960 var thisSize = this.length;
11961
11962 if (end == null) {
11963 end = thisSize;
11964 }
11965
11966 if (start == null) {
11967 start = 0;
11968 }
11969
11970 if (start < 0) {
11971 start = thisSize + start;
11972 }
11973
11974 if (end < 0) {
11975 end = thisSize + end;
11976 }
11977
11978 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11979 array.push(this[i]);
11980 }
11981
11982 return this.spawn(array);
11983 },
11984 size: function size() {
11985 return this.length;
11986 },
11987 eq: function eq(i) {
11988 return this[i] || this.spawn();
11989 },
11990 first: function first() {
11991 return this[0] || this.spawn();
11992 },
11993 last: function last() {
11994 return this[this.length - 1] || this.spawn();
11995 },
11996 empty: function empty() {
11997 return this.length === 0;
11998 },
11999 nonempty: function nonempty() {
12000 return !this.empty();
12001 },
12002 sort: function sort(sortFn) {
12003 if (!fn(sortFn)) {
12004 return this;
12005 }
12006
12007 var sorted = this.toArray().sort(sortFn);
12008 return this.spawn(sorted);
12009 },
12010 sortByZIndex: function sortByZIndex() {
12011 return this.sort(zIndexSort);
12012 },
12013 zDepth: function zDepth() {
12014 var ele = this[0];
12015
12016 if (!ele) {
12017 return undefined;
12018 } // let cy = ele.cy();
12019
12020
12021 var _p = ele._private;
12022 var group = _p.group;
12023
12024 if (group === 'nodes') {
12025 var depth = _p.data.parent ? ele.parents().size() : 0;
12026
12027 if (!ele.isParent()) {
12028 return MAX_INT - 1; // childless nodes always on top
12029 }
12030
12031 return depth;
12032 } else {
12033 var src = _p.source;
12034 var tgt = _p.target;
12035 var srcDepth = src.zDepth();
12036 var tgtDepth = tgt.zDepth();
12037 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
12038 }
12039 }
12040 };
12041 elesfn$p.each = elesfn$p.forEach;
12042
12043 var defineSymbolIterator = function defineSymbolIterator() {
12044 var typeofUndef = "undefined" ;
12045 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
12046
12047 if (isIteratorSupported) {
12048 elesfn$p[Symbol.iterator] = function () {
12049 var _this = this;
12050
12051 // eslint-disable-line no-undef
12052 var entry = {
12053 value: undefined,
12054 done: false
12055 };
12056 var i = 0;
12057 var length = this.length;
12058 return _defineProperty({
12059 next: function next() {
12060 if (i < length) {
12061 entry.value = _this[i++];
12062 } else {
12063 entry.value = undefined;
12064 entry.done = true;
12065 }
12066
12067 return entry;
12068 }
12069 }, Symbol.iterator, function () {
12070 // eslint-disable-line no-undef
12071 return this;
12072 });
12073 };
12074 }
12075 };
12076
12077 defineSymbolIterator();
12078
12079 var getLayoutDimensionOptions = defaults({
12080 nodeDimensionsIncludeLabels: false
12081 });
12082 var elesfn$q = {
12083 // Calculates and returns node dimensions { x, y } based on options given
12084 layoutDimensions: function layoutDimensions(options) {
12085 options = getLayoutDimensionOptions(options);
12086 var dims;
12087
12088 if (!this.takesUpSpace()) {
12089 dims = {
12090 w: 0,
12091 h: 0
12092 };
12093 } else if (options.nodeDimensionsIncludeLabels) {
12094 var bbDim = this.boundingBox();
12095 dims = {
12096 w: bbDim.w,
12097 h: bbDim.h
12098 };
12099 } else {
12100 dims = {
12101 w: this.outerWidth(),
12102 h: this.outerHeight()
12103 };
12104 } // sanitise the dimensions for external layouts (avoid division by zero)
12105
12106
12107 if (dims.w === 0 || dims.h === 0) {
12108 dims.w = dims.h = 1;
12109 }
12110
12111 return dims;
12112 },
12113 // using standard layout options, apply position function (w/ or w/o animation)
12114 layoutPositions: function layoutPositions(layout, options, fn) {
12115 var nodes = this.nodes();
12116 var cy = this.cy();
12117 var layoutEles = options.eles; // nodes & edges
12118
12119 var getMemoizeKey = function getMemoizeKey(node) {
12120 return node.id();
12121 };
12122
12123 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
12124
12125 layout.emit({
12126 type: 'layoutstart',
12127 layout: layout
12128 });
12129 layout.animations = [];
12130
12131 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
12132 var center = {
12133 x: nodesBb.x1 + nodesBb.w / 2,
12134 y: nodesBb.y1 + nodesBb.h / 2
12135 };
12136 var spacingVector = {
12137 // scale from center of bounding box (not necessarily 0,0)
12138 x: (pos.x - center.x) * spacing,
12139 y: (pos.y - center.y) * spacing
12140 };
12141 return {
12142 x: center.x + spacingVector.x,
12143 y: center.y + spacingVector.y
12144 };
12145 };
12146
12147 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
12148
12149 var spacingBb = function spacingBb() {
12150 if (!useSpacingFactor) {
12151 return null;
12152 }
12153
12154 var bb = makeBoundingBox();
12155
12156 for (var i = 0; i < nodes.length; i++) {
12157 var node = nodes[i];
12158 var pos = fnMem(node, i);
12159 expandBoundingBoxByPoint(bb, pos.x, pos.y);
12160 }
12161
12162 return bb;
12163 };
12164
12165 var bb = spacingBb();
12166 var getFinalPos = memoize(function (node, i) {
12167 var newPos = fnMem(node, i);
12168
12169 if (useSpacingFactor) {
12170 var spacing = Math.abs(options.spacingFactor);
12171 newPos = calculateSpacing(spacing, bb, newPos);
12172 }
12173
12174 if (options.transform != null) {
12175 newPos = options.transform(node, newPos);
12176 }
12177
12178 return newPos;
12179 }, getMemoizeKey);
12180
12181 if (options.animate) {
12182 for (var i = 0; i < nodes.length; i++) {
12183 var node = nodes[i];
12184 var newPos = getFinalPos(node, i);
12185 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
12186
12187 if (animateNode) {
12188 var ani = node.animation({
12189 position: newPos,
12190 duration: options.animationDuration,
12191 easing: options.animationEasing
12192 });
12193 layout.animations.push(ani);
12194 } else {
12195 node.position(newPos);
12196 }
12197 }
12198
12199 if (options.fit) {
12200 var fitAni = cy.animation({
12201 fit: {
12202 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
12203 padding: options.padding
12204 },
12205 duration: options.animationDuration,
12206 easing: options.animationEasing
12207 });
12208 layout.animations.push(fitAni);
12209 } else if (options.zoom !== undefined && options.pan !== undefined) {
12210 var zoomPanAni = cy.animation({
12211 zoom: options.zoom,
12212 pan: options.pan,
12213 duration: options.animationDuration,
12214 easing: options.animationEasing
12215 });
12216 layout.animations.push(zoomPanAni);
12217 }
12218
12219 layout.animations.forEach(function (ani) {
12220 return ani.play();
12221 });
12222 layout.one('layoutready', options.ready);
12223 layout.emit({
12224 type: 'layoutready',
12225 layout: layout
12226 });
12227 Promise$1.all(layout.animations.map(function (ani) {
12228 return ani.promise();
12229 })).then(function () {
12230 layout.one('layoutstop', options.stop);
12231 layout.emit({
12232 type: 'layoutstop',
12233 layout: layout
12234 });
12235 });
12236 } else {
12237 nodes.positions(getFinalPos);
12238
12239 if (options.fit) {
12240 cy.fit(options.eles, options.padding);
12241 }
12242
12243 if (options.zoom != null) {
12244 cy.zoom(options.zoom);
12245 }
12246
12247 if (options.pan) {
12248 cy.pan(options.pan);
12249 }
12250
12251 layout.one('layoutready', options.ready);
12252 layout.emit({
12253 type: 'layoutready',
12254 layout: layout
12255 });
12256 layout.one('layoutstop', options.stop);
12257 layout.emit({
12258 type: 'layoutstop',
12259 layout: layout
12260 });
12261 }
12262
12263 return this; // chaining
12264 },
12265 layout: function layout(options) {
12266 var cy = this.cy();
12267 return cy.makeLayout(extend({}, options, {
12268 eles: this
12269 }));
12270 }
12271 }; // aliases:
12272
12273 elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
12274
12275 function styleCache(key, fn, ele) {
12276 var _p = ele._private;
12277 var cache = _p.styleCache = _p.styleCache || [];
12278 var val;
12279
12280 if ((val = cache[key]) != null) {
12281 return val;
12282 } else {
12283 val = cache[key] = fn(ele);
12284 return val;
12285 }
12286 }
12287
12288 function cacheStyleFunction(key, fn) {
12289 key = hashString(key);
12290 return function cachedStyleFunction(ele) {
12291 return styleCache(key, fn, ele);
12292 };
12293 }
12294
12295 function cachePrototypeStyleFunction(key, fn) {
12296 key = hashString(key);
12297
12298 var selfFn = function selfFn(ele) {
12299 return fn.call(ele);
12300 };
12301
12302 return function cachedPrototypeStyleFunction() {
12303 var ele = this[0];
12304
12305 if (ele) {
12306 return styleCache(key, selfFn, ele);
12307 }
12308 };
12309 }
12310
12311 var elesfn$r = {
12312 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
12313 var cy = this.cy();
12314 var renderer = cy.renderer();
12315 var styleEnabled = cy.styleEnabled();
12316
12317 if (renderer && styleEnabled) {
12318 renderer.recalculateRenderedStyle(this, useCache);
12319 }
12320
12321 return this;
12322 },
12323 dirtyStyleCache: function dirtyStyleCache() {
12324 var cy = this.cy();
12325
12326 var dirty = function dirty(ele) {
12327 return ele._private.styleCache = null;
12328 };
12329
12330 if (cy.hasCompoundNodes()) {
12331 var eles;
12332 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12333 eles.merge(eles.connectedEdges());
12334 eles.forEach(dirty);
12335 } else {
12336 this.forEach(function (ele) {
12337 dirty(ele);
12338 ele.connectedEdges().forEach(dirty);
12339 });
12340 }
12341
12342 return this;
12343 },
12344 // fully updates (recalculates) the style for the elements
12345 updateStyle: function updateStyle(notifyRenderer) {
12346 var cy = this._private.cy;
12347
12348 if (!cy.styleEnabled()) {
12349 return this;
12350 }
12351
12352 if (cy.batching()) {
12353 var bEles = cy._private.batchStyleEles;
12354 bEles.merge(this);
12355 return this; // chaining and exit early when batching
12356 }
12357
12358 var hasCompounds = cy.hasCompoundNodes();
12359 var style = cy.style();
12360 var updatedEles = this;
12361 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
12362
12363 if (hasCompounds) {
12364 // then add everything up and down for compound selector checks
12365 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12366 }
12367
12368 var changedEles = style.apply(updatedEles);
12369
12370 if (notifyRenderer) {
12371 changedEles.emitAndNotify('style'); // let renderer know we changed style
12372 } else {
12373 changedEles.emit('style'); // just fire the event
12374 }
12375
12376 return this; // chaining
12377 },
12378 // get the internal parsed style object for the specified property
12379 parsedStyle: function parsedStyle(property) {
12380 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12381 var ele = this[0];
12382 var cy = ele.cy();
12383
12384 if (!cy.styleEnabled()) {
12385 return;
12386 }
12387
12388 if (ele) {
12389 var overriddenStyle = ele._private.style[property];
12390
12391 if (overriddenStyle != null) {
12392 return overriddenStyle;
12393 } else if (includeNonDefault) {
12394 return cy.style().getDefaultProperty(property);
12395 } else {
12396 return null;
12397 }
12398 }
12399 },
12400 numericStyle: function numericStyle(property) {
12401 var ele = this[0];
12402
12403 if (!ele.cy().styleEnabled()) {
12404 return;
12405 }
12406
12407 if (ele) {
12408 var pstyle = ele.pstyle(property);
12409 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
12410 }
12411 },
12412 numericStyleUnits: function numericStyleUnits(property) {
12413 var ele = this[0];
12414
12415 if (!ele.cy().styleEnabled()) {
12416 return;
12417 }
12418
12419 if (ele) {
12420 return ele.pstyle(property).units;
12421 }
12422 },
12423 // get the specified css property as a rendered value (i.e. on-screen value)
12424 // or get the whole rendered style if no property specified (NB doesn't allow setting)
12425 renderedStyle: function renderedStyle(property) {
12426 var cy = this.cy();
12427
12428 if (!cy.styleEnabled()) {
12429 return this;
12430 }
12431
12432 var ele = this[0];
12433
12434 if (ele) {
12435 return cy.style().getRenderedStyle(ele, property);
12436 }
12437 },
12438 // read the calculated css style of the element or override the style (via a bypass)
12439 style: function style(name, value) {
12440 var cy = this.cy();
12441
12442 if (!cy.styleEnabled()) {
12443 return this;
12444 }
12445
12446 var updateTransitions = false;
12447 var style = cy.style();
12448
12449 if (plainObject(name)) {
12450 // then extend the bypass
12451 var props = name;
12452 style.applyBypass(this, props, updateTransitions);
12453 this.emitAndNotify('style'); // let the renderer know we've updated style
12454 } else if (string(name)) {
12455 if (value === undefined) {
12456 // then get the property from the style
12457 var ele = this[0];
12458
12459 if (ele) {
12460 return style.getStylePropertyValue(ele, name);
12461 } else {
12462 // empty collection => can't get any value
12463 return;
12464 }
12465 } else {
12466 // then set the bypass with the property value
12467 style.applyBypass(this, name, value, updateTransitions);
12468 this.emitAndNotify('style'); // let the renderer know we've updated style
12469 }
12470 } else if (name === undefined) {
12471 var _ele = this[0];
12472
12473 if (_ele) {
12474 return style.getRawStyle(_ele);
12475 } else {
12476 // empty collection => can't get any value
12477 return;
12478 }
12479 }
12480
12481 return this; // chaining
12482 },
12483 removeStyle: function removeStyle(names) {
12484 var cy = this.cy();
12485
12486 if (!cy.styleEnabled()) {
12487 return this;
12488 }
12489
12490 var updateTransitions = false;
12491 var style = cy.style();
12492 var eles = this;
12493
12494 if (names === undefined) {
12495 for (var i = 0; i < eles.length; i++) {
12496 var ele = eles[i];
12497 style.removeAllBypasses(ele, updateTransitions);
12498 }
12499 } else {
12500 names = names.split(/\s+/);
12501
12502 for (var _i = 0; _i < eles.length; _i++) {
12503 var _ele2 = eles[_i];
12504 style.removeBypasses(_ele2, names, updateTransitions);
12505 }
12506 }
12507
12508 this.emitAndNotify('style'); // let the renderer know we've updated style
12509
12510 return this; // chaining
12511 },
12512 show: function show() {
12513 this.css('display', 'element');
12514 return this; // chaining
12515 },
12516 hide: function hide() {
12517 this.css('display', 'none');
12518 return this; // chaining
12519 },
12520 effectiveOpacity: function effectiveOpacity() {
12521 var cy = this.cy();
12522
12523 if (!cy.styleEnabled()) {
12524 return 1;
12525 }
12526
12527 var hasCompoundNodes = cy.hasCompoundNodes();
12528 var ele = this[0];
12529
12530 if (ele) {
12531 var _p = ele._private;
12532 var parentOpacity = ele.pstyle('opacity').value;
12533
12534 if (!hasCompoundNodes) {
12535 return parentOpacity;
12536 }
12537
12538 var parents = !_p.data.parent ? null : ele.parents();
12539
12540 if (parents) {
12541 for (var i = 0; i < parents.length; i++) {
12542 var parent = parents[i];
12543 var opacity = parent.pstyle('opacity').value;
12544 parentOpacity = opacity * parentOpacity;
12545 }
12546 }
12547
12548 return parentOpacity;
12549 }
12550 },
12551 transparent: function transparent() {
12552 var cy = this.cy();
12553
12554 if (!cy.styleEnabled()) {
12555 return false;
12556 }
12557
12558 var ele = this[0];
12559 var hasCompoundNodes = ele.cy().hasCompoundNodes();
12560
12561 if (ele) {
12562 if (!hasCompoundNodes) {
12563 return ele.pstyle('opacity').value === 0;
12564 } else {
12565 return ele.effectiveOpacity() === 0;
12566 }
12567 }
12568 },
12569 backgrounding: function backgrounding() {
12570 var cy = this.cy();
12571
12572 if (!cy.styleEnabled()) {
12573 return false;
12574 }
12575
12576 var ele = this[0];
12577 return ele._private.backgrounding ? true : false;
12578 }
12579 };
12580
12581 function checkCompound(ele, parentOk) {
12582 var _p = ele._private;
12583 var parents = _p.data.parent ? ele.parents() : null;
12584
12585 if (parents) {
12586 for (var i = 0; i < parents.length; i++) {
12587 var parent = parents[i];
12588
12589 if (!parentOk(parent)) {
12590 return false;
12591 }
12592 }
12593 }
12594
12595 return true;
12596 }
12597
12598 function defineDerivedStateFunction(specs) {
12599 var ok = specs.ok;
12600 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
12601 var parentOk = specs.parentOk || specs.ok;
12602 return function () {
12603 var cy = this.cy();
12604
12605 if (!cy.styleEnabled()) {
12606 return true;
12607 }
12608
12609 var ele = this[0];
12610 var hasCompoundNodes = cy.hasCompoundNodes();
12611
12612 if (ele) {
12613 var _p = ele._private;
12614
12615 if (!ok(ele)) {
12616 return false;
12617 }
12618
12619 if (ele.isNode()) {
12620 return !hasCompoundNodes || checkCompound(ele, parentOk);
12621 } else {
12622 var src = _p.source;
12623 var tgt = _p.target;
12624 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
12625 }
12626 }
12627 };
12628 }
12629
12630 var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
12631 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
12632 });
12633 elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
12634 ok: eleTakesUpSpace
12635 }));
12636 var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
12637 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
12638 });
12639 var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
12640 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
12641 });
12642 elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
12643 ok: eleInteractive,
12644 parentOk: parentInteractive,
12645 edgeOkViaNode: eleTakesUpSpace
12646 }));
12647
12648 elesfn$r.noninteractive = function () {
12649 var ele = this[0];
12650
12651 if (ele) {
12652 return !ele.interactive();
12653 }
12654 };
12655
12656 var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
12657 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
12658 });
12659 var edgeVisibleViaNode = eleTakesUpSpace;
12660 elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
12661 ok: eleVisible,
12662 edgeOkViaNode: edgeVisibleViaNode
12663 }));
12664
12665 elesfn$r.hidden = function () {
12666 var ele = this[0];
12667
12668 if (ele) {
12669 return !ele.visible();
12670 }
12671 };
12672
12673 elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
12674 if (!this.cy().styleEnabled()) {
12675 return false;
12676 }
12677
12678 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
12679 });
12680 elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
12681 elesfn$r.renderedCss = elesfn$r.renderedStyle;
12682 elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
12683 elesfn$r.pstyle = elesfn$r.parsedStyle;
12684
12685 var elesfn$s = {};
12686
12687 function defineSwitchFunction(params) {
12688 return function () {
12689 var args = arguments;
12690 var changedEles = []; // e.g. cy.nodes().select( data, handler )
12691
12692 if (args.length === 2) {
12693 var data = args[0];
12694 var handler = args[1];
12695 this.on(params.event, data, handler);
12696 } // e.g. cy.nodes().select( handler )
12697 else if (args.length === 1 && fn(args[0])) {
12698 var _handler = args[0];
12699 this.on(params.event, _handler);
12700 } // e.g. cy.nodes().select()
12701 // e.g. (private) cy.nodes().select(['tapselect'])
12702 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12703 var addlEvents = args.length === 1 ? args[0] : null;
12704
12705 for (var i = 0; i < this.length; i++) {
12706 var ele = this[i];
12707 var able = !params.ableField || ele._private[params.ableField];
12708 var changed = ele._private[params.field] != params.value;
12709
12710 if (params.overrideAble) {
12711 var overrideAble = params.overrideAble(ele);
12712
12713 if (overrideAble !== undefined) {
12714 able = overrideAble;
12715
12716 if (!overrideAble) {
12717 return this;
12718 } // to save cycles assume not able for all on override
12719
12720 }
12721 }
12722
12723 if (able) {
12724 ele._private[params.field] = params.value;
12725
12726 if (changed) {
12727 changedEles.push(ele);
12728 }
12729 }
12730 }
12731
12732 var changedColl = this.spawn(changedEles);
12733 changedColl.updateStyle(); // change of state => possible change of style
12734
12735 changedColl.emit(params.event);
12736
12737 if (addlEvents) {
12738 changedColl.emit(addlEvents);
12739 }
12740 }
12741
12742 return this;
12743 };
12744 }
12745
12746 function defineSwitchSet(params) {
12747 elesfn$s[params.field] = function () {
12748 var ele = this[0];
12749
12750 if (ele) {
12751 if (params.overrideField) {
12752 var val = params.overrideField(ele);
12753
12754 if (val !== undefined) {
12755 return val;
12756 }
12757 }
12758
12759 return ele._private[params.field];
12760 }
12761 };
12762
12763 elesfn$s[params.on] = defineSwitchFunction({
12764 event: params.on,
12765 field: params.field,
12766 ableField: params.ableField,
12767 overrideAble: params.overrideAble,
12768 value: true
12769 });
12770 elesfn$s[params.off] = defineSwitchFunction({
12771 event: params.off,
12772 field: params.field,
12773 ableField: params.ableField,
12774 overrideAble: params.overrideAble,
12775 value: false
12776 });
12777 }
12778
12779 defineSwitchSet({
12780 field: 'locked',
12781 overrideField: function overrideField(ele) {
12782 return ele.cy().autolock() ? true : undefined;
12783 },
12784 on: 'lock',
12785 off: 'unlock'
12786 });
12787 defineSwitchSet({
12788 field: 'grabbable',
12789 overrideField: function overrideField(ele) {
12790 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12791 },
12792 on: 'grabify',
12793 off: 'ungrabify'
12794 });
12795 defineSwitchSet({
12796 field: 'selected',
12797 ableField: 'selectable',
12798 overrideAble: function overrideAble(ele) {
12799 return ele.cy().autounselectify() ? false : undefined;
12800 },
12801 on: 'select',
12802 off: 'unselect'
12803 });
12804 defineSwitchSet({
12805 field: 'selectable',
12806 overrideField: function overrideField(ele) {
12807 return ele.cy().autounselectify() ? false : undefined;
12808 },
12809 on: 'selectify',
12810 off: 'unselectify'
12811 });
12812 elesfn$s.deselect = elesfn$s.unselect;
12813
12814 elesfn$s.grabbed = function () {
12815 var ele = this[0];
12816
12817 if (ele) {
12818 return ele._private.grabbed;
12819 }
12820 };
12821
12822 defineSwitchSet({
12823 field: 'active',
12824 on: 'activate',
12825 off: 'unactivate'
12826 });
12827 defineSwitchSet({
12828 field: 'pannable',
12829 on: 'panify',
12830 off: 'unpanify'
12831 });
12832
12833 elesfn$s.inactive = function () {
12834 var ele = this[0];
12835
12836 if (ele) {
12837 return !ele._private.active;
12838 }
12839 };
12840
12841 var elesfn$t = {}; // DAG functions
12842 ////////////////
12843
12844 var defineDagExtremity = function defineDagExtremity(params) {
12845 return function dagExtremityImpl(selector) {
12846 var eles = this;
12847 var ret = [];
12848
12849 for (var i = 0; i < eles.length; i++) {
12850 var ele = eles[i];
12851
12852 if (!ele.isNode()) {
12853 continue;
12854 }
12855
12856 var disqualified = false;
12857 var edges = ele.connectedEdges();
12858
12859 for (var j = 0; j < edges.length; j++) {
12860 var edge = edges[j];
12861 var src = edge.source();
12862 var tgt = edge.target();
12863
12864 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12865 disqualified = true;
12866 break;
12867 }
12868 }
12869
12870 if (!disqualified) {
12871 ret.push(ele);
12872 }
12873 }
12874
12875 return this.spawn(ret, {
12876 unique: true
12877 }).filter(selector);
12878 };
12879 };
12880
12881 var defineDagOneHop = function defineDagOneHop(params) {
12882 return function (selector) {
12883 var eles = this;
12884 var oEles = [];
12885
12886 for (var i = 0; i < eles.length; i++) {
12887 var ele = eles[i];
12888
12889 if (!ele.isNode()) {
12890 continue;
12891 }
12892
12893 var edges = ele.connectedEdges();
12894
12895 for (var j = 0; j < edges.length; j++) {
12896 var edge = edges[j];
12897 var src = edge.source();
12898 var tgt = edge.target();
12899
12900 if (params.outgoing && src === ele) {
12901 oEles.push(edge);
12902 oEles.push(tgt);
12903 } else if (params.incoming && tgt === ele) {
12904 oEles.push(edge);
12905 oEles.push(src);
12906 }
12907 }
12908 }
12909
12910 return this.spawn(oEles, {
12911 unique: true
12912 }).filter(selector);
12913 };
12914 };
12915
12916 var defineDagAllHops = function defineDagAllHops(params) {
12917 return function (selector) {
12918 var eles = this;
12919 var sEles = [];
12920 var sElesIds = {};
12921
12922 for (;;) {
12923 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12924
12925 if (next.length === 0) {
12926 break;
12927 } // done if none left
12928
12929
12930 var newNext = false;
12931
12932 for (var i = 0; i < next.length; i++) {
12933 var n = next[i];
12934 var nid = n.id();
12935
12936 if (!sElesIds[nid]) {
12937 sElesIds[nid] = true;
12938 sEles.push(n);
12939 newNext = true;
12940 }
12941 }
12942
12943 if (!newNext) {
12944 break;
12945 } // done if touched all outgoers already
12946
12947
12948 eles = next;
12949 }
12950
12951 return this.spawn(sEles, {
12952 unique: true
12953 }).filter(selector);
12954 };
12955 };
12956
12957 elesfn$t.clearTraversalCache = function () {
12958 for (var i = 0; i < this.length; i++) {
12959 this[i]._private.traversalCache = null;
12960 }
12961 };
12962
12963 extend(elesfn$t, {
12964 // get the root nodes in the DAG
12965 roots: defineDagExtremity({
12966 noIncomingEdges: true
12967 }),
12968 // get the leaf nodes in the DAG
12969 leaves: defineDagExtremity({
12970 noOutgoingEdges: true
12971 }),
12972 // normally called children in graph theory
12973 // these nodes =edges=> outgoing nodes
12974 outgoers: cache(defineDagOneHop({
12975 outgoing: true
12976 }), 'outgoers'),
12977 // aka DAG descendants
12978 successors: defineDagAllHops({
12979 outgoing: true
12980 }),
12981 // normally called parents in graph theory
12982 // these nodes <=edges= incoming nodes
12983 incomers: cache(defineDagOneHop({
12984 incoming: true
12985 }), 'incomers'),
12986 // aka DAG ancestors
12987 predecessors: defineDagAllHops({
12988 incoming: true
12989 })
12990 }); // Neighbourhood functions
12991 //////////////////////////
12992
12993 extend(elesfn$t, {
12994 neighborhood: cache(function (selector) {
12995 var elements = [];
12996 var nodes = this.nodes();
12997
12998 for (var i = 0; i < nodes.length; i++) {
12999 // for all nodes
13000 var node = nodes[i];
13001 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
13002
13003 for (var j = 0; j < connectedEdges.length; j++) {
13004 var edge = connectedEdges[j];
13005 var src = edge.source();
13006 var tgt = edge.target();
13007 var otherNode = node === src ? tgt : src; // need check in case of loop
13008
13009 if (otherNode.length > 0) {
13010 elements.push(otherNode[0]); // add node 1 hop away
13011 } // add connected edge
13012
13013
13014 elements.push(edge[0]);
13015 }
13016 }
13017
13018 return this.spawn(elements, {
13019 unique: true
13020 }).filter(selector);
13021 }, 'neighborhood'),
13022 closedNeighborhood: function closedNeighborhood(selector) {
13023 return this.neighborhood().add(this).filter(selector);
13024 },
13025 openNeighborhood: function openNeighborhood(selector) {
13026 return this.neighborhood(selector);
13027 }
13028 }); // aliases
13029
13030 elesfn$t.neighbourhood = elesfn$t.neighborhood;
13031 elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
13032 elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
13033 /////////////////
13034
13035 extend(elesfn$t, {
13036 source: cache(function sourceImpl(selector) {
13037 var ele = this[0];
13038 var src;
13039
13040 if (ele) {
13041 src = ele._private.source || ele.cy().collection();
13042 }
13043
13044 return src && selector ? src.filter(selector) : src;
13045 }, 'source'),
13046 target: cache(function targetImpl(selector) {
13047 var ele = this[0];
13048 var tgt;
13049
13050 if (ele) {
13051 tgt = ele._private.target || ele.cy().collection();
13052 }
13053
13054 return tgt && selector ? tgt.filter(selector) : tgt;
13055 }, 'target'),
13056 sources: defineSourceFunction({
13057 attr: 'source'
13058 }),
13059 targets: defineSourceFunction({
13060 attr: 'target'
13061 })
13062 });
13063
13064 function defineSourceFunction(params) {
13065 return function sourceImpl(selector) {
13066 var sources = [];
13067
13068 for (var i = 0; i < this.length; i++) {
13069 var ele = this[i];
13070 var src = ele._private[params.attr];
13071
13072 if (src) {
13073 sources.push(src);
13074 }
13075 }
13076
13077 return this.spawn(sources, {
13078 unique: true
13079 }).filter(selector);
13080 };
13081 }
13082
13083 extend(elesfn$t, {
13084 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
13085 edgesTo: cache(defineEdgesWithFunction({
13086 thisIsSrc: true
13087 }), 'edgesTo')
13088 });
13089
13090 function defineEdgesWithFunction(params) {
13091 return function edgesWithImpl(otherNodes) {
13092 var elements = [];
13093 var cy = this._private.cy;
13094 var p = params || {}; // get elements if a selector is specified
13095
13096 if (string(otherNodes)) {
13097 otherNodes = cy.$(otherNodes);
13098 }
13099
13100 for (var h = 0; h < otherNodes.length; h++) {
13101 var edges = otherNodes[h]._private.edges;
13102
13103 for (var i = 0; i < edges.length; i++) {
13104 var edge = edges[i];
13105 var edgeData = edge._private.data;
13106 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
13107 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
13108 var edgeConnectsThisAndOther = thisToOther || otherToThis;
13109
13110 if (!edgeConnectsThisAndOther) {
13111 continue;
13112 }
13113
13114 if (p.thisIsSrc || p.thisIsTgt) {
13115 if (p.thisIsSrc && !thisToOther) {
13116 continue;
13117 }
13118
13119 if (p.thisIsTgt && !otherToThis) {
13120 continue;
13121 }
13122 }
13123
13124 elements.push(edge);
13125 }
13126 }
13127
13128 return this.spawn(elements, {
13129 unique: true
13130 });
13131 };
13132 }
13133
13134 extend(elesfn$t, {
13135 connectedEdges: cache(function (selector) {
13136 var retEles = [];
13137 var eles = this;
13138
13139 for (var i = 0; i < eles.length; i++) {
13140 var node = eles[i];
13141
13142 if (!node.isNode()) {
13143 continue;
13144 }
13145
13146 var edges = node._private.edges;
13147
13148 for (var j = 0; j < edges.length; j++) {
13149 var edge = edges[j];
13150 retEles.push(edge);
13151 }
13152 }
13153
13154 return this.spawn(retEles, {
13155 unique: true
13156 }).filter(selector);
13157 }, 'connectedEdges'),
13158 connectedNodes: cache(function (selector) {
13159 var retEles = [];
13160 var eles = this;
13161
13162 for (var i = 0; i < eles.length; i++) {
13163 var edge = eles[i];
13164
13165 if (!edge.isEdge()) {
13166 continue;
13167 }
13168
13169 retEles.push(edge.source()[0]);
13170 retEles.push(edge.target()[0]);
13171 }
13172
13173 return this.spawn(retEles, {
13174 unique: true
13175 }).filter(selector);
13176 }, 'connectedNodes'),
13177 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
13178 codirectedEdges: cache(defineParallelEdgesFunction({
13179 codirected: true
13180 }), 'codirectedEdges')
13181 });
13182
13183 function defineParallelEdgesFunction(params) {
13184 var defaults = {
13185 codirected: false
13186 };
13187 params = extend({}, defaults, params);
13188 return function parallelEdgesImpl(selector) {
13189 // micro-optimised for renderer
13190 var elements = [];
13191 var edges = this.edges();
13192 var p = params; // look at all the edges in the collection
13193
13194 for (var i = 0; i < edges.length; i++) {
13195 var edge1 = edges[i];
13196 var edge1_p = edge1._private;
13197 var src1 = edge1_p.source;
13198 var srcid1 = src1._private.data.id;
13199 var tgtid1 = edge1_p.data.target;
13200 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
13201
13202 for (var j = 0; j < srcEdges1.length; j++) {
13203 var edge2 = srcEdges1[j];
13204 var edge2data = edge2._private.data;
13205 var tgtid2 = edge2data.target;
13206 var srcid2 = edge2data.source;
13207 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
13208 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
13209
13210 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
13211 elements.push(edge2);
13212 }
13213 }
13214 }
13215
13216 return this.spawn(elements, {
13217 unique: true
13218 }).filter(selector);
13219 };
13220 } // Misc functions
13221 /////////////////
13222
13223
13224 extend(elesfn$t, {
13225 components: function components(root) {
13226 var self = this;
13227 var cy = self.cy();
13228 var visited = cy.collection();
13229 var unvisited = root == null ? self.nodes() : root.nodes();
13230 var components = [];
13231
13232 if (root != null && unvisited.empty()) {
13233 // root may contain only edges
13234 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
13235 }
13236
13237 var visitInComponent = function visitInComponent(node, component) {
13238 visited.merge(node);
13239 unvisited.unmerge(node);
13240 component.merge(node);
13241 };
13242
13243 if (unvisited.empty()) {
13244 return self.spawn();
13245 }
13246
13247 var _loop = function _loop() {
13248 // each iteration yields a component
13249 var cmpt = cy.collection();
13250 components.push(cmpt);
13251 var root = unvisited[0];
13252 visitInComponent(root, cmpt);
13253 self.bfs({
13254 directed: false,
13255 roots: root,
13256 visit: function visit(v) {
13257 return visitInComponent(v, cmpt);
13258 }
13259 });
13260 cmpt.forEach(function (node) {
13261 node.connectedEdges().forEach(function (e) {
13262 // connectedEdges() usually cached
13263 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
13264 // has() is cheap
13265 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
13266 }
13267 });
13268 });
13269 };
13270
13271 do {
13272 _loop();
13273 } while (unvisited.length > 0);
13274
13275 return components;
13276 },
13277 component: function component() {
13278 var ele = this[0];
13279 return ele.cy().mutableElements().components(ele)[0];
13280 }
13281 });
13282 elesfn$t.componentsOf = elesfn$t.components;
13283
13284 var idFactory = {
13285 generate: function generate(cy, element, tryThisId) {
13286 var id = tryThisId != null ? tryThisId : uuid();
13287
13288 while (cy.hasElementWithId(id)) {
13289 id = uuid();
13290 }
13291
13292 return id;
13293 }
13294 }; // represents a set of nodes, edges, or both together
13295
13296 var Collection = function Collection(cy, elements, options) {
13297 if (cy === undefined || !core(cy)) {
13298 error('A collection must have a reference to the core');
13299 return;
13300 }
13301
13302 var map = new Map$1();
13303 var createdElements = false;
13304
13305 if (!elements) {
13306 elements = [];
13307 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
13308 createdElements = true; // make elements from json and restore all at once later
13309
13310 var eles = [];
13311 var elesIds = new Set$1();
13312
13313 for (var i = 0, l = elements.length; i < l; i++) {
13314 var json = elements[i];
13315
13316 if (json.data == null) {
13317 json.data = {};
13318 }
13319
13320 var _data = json.data; // make sure newly created elements have valid ids
13321
13322 if (_data.id == null) {
13323 _data.id = idFactory.generate(cy, json);
13324 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
13325 continue; // can't create element if prior id already exists
13326 }
13327
13328 var ele = new Element(cy, json, false);
13329 eles.push(ele);
13330 elesIds.add(_data.id);
13331 }
13332
13333 elements = eles;
13334 }
13335
13336 this.length = 0;
13337
13338 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
13339 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
13340
13341 if (element$1 == null) {
13342 continue;
13343 }
13344
13345 var id = element$1._private.data.id;
13346
13347 if (options == null || options.unique && !map.has(id)) {
13348 map.set(id, {
13349 index: this.length,
13350 ele: element$1
13351 });
13352 this[this.length] = element$1;
13353 this.length++;
13354 }
13355 }
13356
13357 this._private = {
13358 cy: cy,
13359 map: map
13360 }; // restore the elements if we created them from json
13361
13362 if (createdElements) {
13363 this.restore();
13364 }
13365 }; // Functions
13366 ////////////////////////////////////////////////////////////////////////////////////////////////////
13367 // keep the prototypes in sync (an element has the same functions as a collection)
13368 // and use elefn and elesfn as shorthands to the prototypes
13369
13370
13371 var elesfn$u = Element.prototype = Collection.prototype;
13372
13373 elesfn$u.instanceString = function () {
13374 return 'collection';
13375 };
13376
13377 elesfn$u.spawn = function (cy, eles, opts) {
13378 if (!core(cy)) {
13379 // cy is optional
13380 opts = eles;
13381 eles = cy;
13382 cy = this.cy();
13383 }
13384
13385 return new Collection(cy, eles, opts);
13386 };
13387
13388 elesfn$u.spawnSelf = function () {
13389 return this.spawn(this);
13390 };
13391
13392 elesfn$u.cy = function () {
13393 return this._private.cy;
13394 };
13395
13396 elesfn$u.renderer = function () {
13397 return this._private.cy.renderer();
13398 };
13399
13400 elesfn$u.element = function () {
13401 return this[0];
13402 };
13403
13404 elesfn$u.collection = function () {
13405 if (collection(this)) {
13406 return this;
13407 } else {
13408 // an element
13409 return new Collection(this._private.cy, [this]);
13410 }
13411 };
13412
13413 elesfn$u.unique = function () {
13414 return new Collection(this._private.cy, this, {
13415 unique: true
13416 });
13417 };
13418
13419 elesfn$u.hasElementWithId = function (id) {
13420 id = '' + id; // id must be string
13421
13422 return this._private.map.has(id);
13423 };
13424
13425 elesfn$u.getElementById = function (id) {
13426 id = '' + id; // id must be string
13427
13428 var cy = this._private.cy;
13429
13430 var entry = this._private.map.get(id);
13431
13432 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
13433 };
13434
13435 elesfn$u.$id = elesfn$u.getElementById;
13436
13437 elesfn$u.poolIndex = function () {
13438 var cy = this._private.cy;
13439 var eles = cy._private.elements;
13440 var id = this[0]._private.data.id;
13441 return eles._private.map.get(id).index;
13442 };
13443
13444 elesfn$u.indexOf = function (ele) {
13445 var id = ele[0]._private.data.id;
13446 return this._private.map.get(id).index;
13447 };
13448
13449 elesfn$u.indexOfId = function (id) {
13450 id = '' + id; // id must be string
13451
13452 return this._private.map.get(id).index;
13453 };
13454
13455 elesfn$u.json = function (obj) {
13456 var ele = this.element();
13457 var cy = this.cy();
13458
13459 if (ele == null && obj) {
13460 return this;
13461 } // can't set to no eles
13462
13463
13464 if (ele == null) {
13465 return undefined;
13466 } // can't get from no eles
13467
13468
13469 var p = ele._private;
13470
13471 if (plainObject(obj)) {
13472 // set
13473 cy.startBatch();
13474
13475 if (obj.data) {
13476 ele.data(obj.data);
13477 var _data2 = p.data;
13478
13479 if (ele.isEdge()) {
13480 // source and target are immutable via data()
13481 var move = false;
13482 var spec = {};
13483 var src = obj.data.source;
13484 var tgt = obj.data.target;
13485
13486 if (src != null && src != _data2.source) {
13487 spec.source = '' + src; // id must be string
13488
13489 move = true;
13490 }
13491
13492 if (tgt != null && tgt != _data2.target) {
13493 spec.target = '' + tgt; // id must be string
13494
13495 move = true;
13496 }
13497
13498 if (move) {
13499 ele = ele.move(spec);
13500 }
13501 } else {
13502 // parent is immutable via data()
13503 var parent = obj.data.parent;
13504
13505 if ((parent != null || _data2.parent != null) && parent != _data2.parent) {
13506 if (parent === undefined) {
13507 // can't set undefined imperatively, so use null
13508 parent = null;
13509 }
13510
13511 if (parent != null) {
13512 parent = '' + parent; // id must be string
13513 }
13514
13515 ele = ele.move({
13516 parent: parent
13517 });
13518 }
13519 }
13520 }
13521
13522 if (obj.position) {
13523 ele.position(obj.position);
13524 } // ignore group -- immutable
13525
13526
13527 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
13528 var obj_k = obj[k];
13529
13530 if (obj_k != null && obj_k !== p[k]) {
13531 if (obj_k) {
13532 ele[trueFnName]();
13533 } else {
13534 ele[falseFnName]();
13535 }
13536 }
13537 };
13538
13539 checkSwitch('removed', 'remove', 'restore');
13540 checkSwitch('selected', 'select', 'unselect');
13541 checkSwitch('selectable', 'selectify', 'unselectify');
13542 checkSwitch('locked', 'lock', 'unlock');
13543 checkSwitch('grabbable', 'grabify', 'ungrabify');
13544 checkSwitch('pannable', 'panify', 'unpanify');
13545
13546 if (obj.classes != null) {
13547 ele.classes(obj.classes);
13548 }
13549
13550 cy.endBatch();
13551 return this;
13552 } else if (obj === undefined) {
13553 // get
13554 var json = {
13555 data: copy(p.data),
13556 position: copy(p.position),
13557 group: p.group,
13558 removed: p.removed,
13559 selected: p.selected,
13560 selectable: p.selectable,
13561 locked: p.locked,
13562 grabbable: p.grabbable,
13563 pannable: p.pannable,
13564 classes: null
13565 };
13566 json.classes = '';
13567 var i = 0;
13568 p.classes.forEach(function (cls) {
13569 return json.classes += i++ === 0 ? cls : ' ' + cls;
13570 });
13571 return json;
13572 }
13573 };
13574
13575 elesfn$u.jsons = function () {
13576 var jsons = [];
13577
13578 for (var i = 0; i < this.length; i++) {
13579 var ele = this[i];
13580 var json = ele.json();
13581 jsons.push(json);
13582 }
13583
13584 return jsons;
13585 };
13586
13587 elesfn$u.clone = function () {
13588 var cy = this.cy();
13589 var elesArr = [];
13590
13591 for (var i = 0; i < this.length; i++) {
13592 var ele = this[i];
13593 var json = ele.json();
13594 var clone = new Element(cy, json, false); // NB no restore
13595
13596 elesArr.push(clone);
13597 }
13598
13599 return new Collection(cy, elesArr);
13600 };
13601
13602 elesfn$u.copy = elesfn$u.clone;
13603
13604 elesfn$u.restore = function () {
13605 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13606 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13607 var self = this;
13608 var cy = self.cy();
13609 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
13610 // restore the nodes first
13611
13612 var nodes = [];
13613 var edges = [];
13614 var elements;
13615
13616 for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
13617 var ele = self[_i2];
13618
13619 if (addToPool && !ele.removed()) {
13620 // don't need to handle this ele
13621 continue;
13622 } // keep nodes first in the array and edges after
13623
13624
13625 if (ele.isNode()) {
13626 // put to front of array if node
13627 nodes.push(ele);
13628 } else {
13629 // put to end of array if edge
13630 edges.push(ele);
13631 }
13632 }
13633
13634 elements = nodes.concat(edges);
13635 var i;
13636
13637 var removeFromElements = function removeFromElements() {
13638 elements.splice(i, 1);
13639 i--;
13640 }; // now, restore each element
13641
13642
13643 for (i = 0; i < elements.length; i++) {
13644 var _ele = elements[i];
13645 var _private = _ele._private;
13646 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
13647
13648 _ele.clearTraversalCache(); // set id and validate
13649
13650
13651 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
13652 _data3.id = idFactory.generate(cy, _ele);
13653 } else if (number(_data3.id)) {
13654 _data3.id = '' + _data3.id; // now it's a string
13655 } else if (emptyString(_data3.id) || !string(_data3.id)) {
13656 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
13657
13658 removeFromElements();
13659 continue;
13660 } else if (cy.hasElementWithId(_data3.id)) {
13661 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
13662
13663 removeFromElements();
13664 continue;
13665 }
13666
13667 var id = _data3.id; // id is finalised, now let's keep a ref
13668
13669 if (_ele.isNode()) {
13670 // extra checks for nodes
13671 var pos = _private.position; // make sure the nodes have a defined position
13672
13673 if (pos.x == null) {
13674 pos.x = 0;
13675 }
13676
13677 if (pos.y == null) {
13678 pos.y = 0;
13679 }
13680 }
13681
13682 if (_ele.isEdge()) {
13683 // extra checks for edges
13684 var edge = _ele;
13685 var fields = ['source', 'target'];
13686 var fieldsLength = fields.length;
13687 var badSourceOrTarget = false;
13688
13689 for (var j = 0; j < fieldsLength; j++) {
13690 var field = fields[j];
13691 var val = _data3[field];
13692
13693 if (number(val)) {
13694 val = _data3[field] = '' + _data3[field]; // now string
13695 }
13696
13697 if (val == null || val === '') {
13698 // can't create if source or target is not defined properly
13699 error('Can not create edge `' + id + '` with unspecified ' + field);
13700 badSourceOrTarget = true;
13701 } else if (!cy.hasElementWithId(val)) {
13702 // can't create edge if one of its nodes doesn't exist
13703 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13704 badSourceOrTarget = true;
13705 }
13706 }
13707
13708 if (badSourceOrTarget) {
13709 removeFromElements();
13710 continue;
13711 } // can't create this
13712
13713
13714 var src = cy.getElementById(_data3.source);
13715 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13716
13717 if (src.same(tgt)) {
13718 src._private.edges.push(edge);
13719 } else {
13720 src._private.edges.push(edge);
13721
13722 tgt._private.edges.push(edge);
13723 }
13724
13725 edge._private.source = src;
13726 edge._private.target = tgt;
13727 } // if is edge
13728 // create mock ids / indexes maps for element so it can be used like collections
13729
13730
13731 _private.map = new Map$1();
13732
13733 _private.map.set(id, {
13734 ele: _ele,
13735 index: 0
13736 });
13737
13738 _private.removed = false;
13739
13740 if (addToPool) {
13741 cy.addToPool(_ele);
13742 }
13743 } // for each element
13744 // do compound node sanity checks
13745
13746
13747 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
13748 // each node
13749 var node = nodes[_i3];
13750 var _data4 = node._private.data;
13751
13752 if (number(_data4.parent)) {
13753 // then automake string
13754 _data4.parent = '' + _data4.parent;
13755 }
13756
13757 var parentId = _data4.parent;
13758 var specifiedParent = parentId != null;
13759
13760 if (specifiedParent) {
13761 var parent = cy.getElementById(parentId);
13762
13763 if (parent.empty()) {
13764 // non-existant parent; just remove it
13765 _data4.parent = undefined;
13766 } else {
13767 var selfAsParent = false;
13768 var ancestor = parent;
13769
13770 while (!ancestor.empty()) {
13771 if (node.same(ancestor)) {
13772 // mark self as parent and remove from data
13773 selfAsParent = true;
13774 _data4.parent = undefined; // remove parent reference
13775 // exit or we loop forever
13776
13777 break;
13778 }
13779
13780 ancestor = ancestor.parent();
13781 }
13782
13783 if (!selfAsParent) {
13784 // connect with children
13785 parent[0]._private.children.push(node);
13786
13787 node._private.parent = parent[0]; // let the core know we have a compound graph
13788
13789 cy_p.hasCompoundNodes = true;
13790 }
13791 } // else
13792
13793 } // if specified parent
13794
13795 } // for each node
13796
13797
13798 if (elements.length > 0) {
13799 var restored = new Collection(cy, elements);
13800
13801 for (var _i4 = 0; _i4 < restored.length; _i4++) {
13802 var _ele2 = restored[_i4];
13803
13804 if (_ele2.isNode()) {
13805 continue;
13806 } // adding an edge invalidates the traversal caches for the parallel edges
13807
13808
13809 _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13810
13811
13812 _ele2.source().clearTraversalCache();
13813
13814 _ele2.target().clearTraversalCache();
13815 }
13816
13817 var toUpdateStyle;
13818
13819 if (cy_p.hasCompoundNodes) {
13820 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13821 } else {
13822 toUpdateStyle = restored;
13823 }
13824
13825 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13826
13827 if (notifyRenderer) {
13828 restored.emitAndNotify('add');
13829 } else if (addToPool) {
13830 restored.emit('add');
13831 }
13832 }
13833
13834 return self; // chainability
13835 };
13836
13837 elesfn$u.removed = function () {
13838 var ele = this[0];
13839 return ele && ele._private.removed;
13840 };
13841
13842 elesfn$u.inside = function () {
13843 var ele = this[0];
13844 return ele && !ele._private.removed;
13845 };
13846
13847 elesfn$u.remove = function () {
13848 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13849 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13850 var self = this;
13851 var elesToRemove = [];
13852 var elesToRemoveIds = {};
13853 var cy = self._private.cy; // add connected edges
13854
13855 function addConnectedEdges(node) {
13856 var edges = node._private.edges;
13857
13858 for (var i = 0; i < edges.length; i++) {
13859 add(edges[i]);
13860 }
13861 } // add descendant nodes
13862
13863
13864 function addChildren(node) {
13865 var children = node._private.children;
13866
13867 for (var i = 0; i < children.length; i++) {
13868 add(children[i]);
13869 }
13870 }
13871
13872 function add(ele) {
13873 var alreadyAdded = elesToRemoveIds[ele.id()];
13874
13875 if (removeFromPool && ele.removed() || alreadyAdded) {
13876 return;
13877 } else {
13878 elesToRemoveIds[ele.id()] = true;
13879 }
13880
13881 if (ele.isNode()) {
13882 elesToRemove.push(ele); // nodes are removed last
13883
13884 addConnectedEdges(ele);
13885 addChildren(ele);
13886 } else {
13887 elesToRemove.unshift(ele); // edges are removed first
13888 }
13889 } // make the list of elements to remove
13890 // (may be removing more than specified due to connected edges etc)
13891
13892
13893 for (var i = 0, l = self.length; i < l; i++) {
13894 var ele = self[i];
13895 add(ele);
13896 }
13897
13898 function removeEdgeRef(node, edge) {
13899 var connectedEdges = node._private.edges;
13900 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13901
13902 node.clearTraversalCache();
13903 }
13904
13905 function removeParallelRef(pllEdge) {
13906 // removing an edge invalidates the traversal caches for the parallel edges
13907 pllEdge.clearTraversalCache();
13908 }
13909
13910 var alteredParents = [];
13911 alteredParents.ids = {};
13912
13913 function removeChildRef(parent, ele) {
13914 ele = ele[0];
13915 parent = parent[0];
13916 var children = parent._private.children;
13917 var pid = parent.id();
13918 removeFromArray(children, ele); // remove parent => child ref
13919
13920 ele._private.parent = null; // remove child => parent ref
13921
13922 if (!alteredParents.ids[pid]) {
13923 alteredParents.ids[pid] = true;
13924 alteredParents.push(parent);
13925 }
13926 }
13927
13928 self.dirtyCompoundBoundsCache();
13929
13930 if (removeFromPool) {
13931 cy.removeFromPool(elesToRemove); // remove from core pool
13932 }
13933
13934 for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
13935 var _ele3 = elesToRemove[_i5];
13936
13937 if (_ele3.isEdge()) {
13938 // remove references to this edge in its connected nodes
13939 var src = _ele3.source()[0];
13940
13941 var tgt = _ele3.target()[0];
13942
13943 removeEdgeRef(src, _ele3);
13944 removeEdgeRef(tgt, _ele3);
13945
13946 var pllEdges = _ele3.parallelEdges();
13947
13948 for (var j = 0; j < pllEdges.length; j++) {
13949 var pllEdge = pllEdges[j];
13950 removeParallelRef(pllEdge);
13951
13952 if (pllEdge.isBundledBezier()) {
13953 pllEdge.dirtyBoundingBoxCache();
13954 }
13955 }
13956 } else {
13957 // remove reference to parent
13958 var parent = _ele3.parent();
13959
13960 if (parent.length !== 0) {
13961 removeChildRef(parent, _ele3);
13962 }
13963 }
13964
13965 if (removeFromPool) {
13966 // mark as removed
13967 _ele3._private.removed = true;
13968 }
13969 } // check to see if we have a compound graph or not
13970
13971
13972 var elesStillInside = cy._private.elements;
13973 cy._private.hasCompoundNodes = false;
13974
13975 for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
13976 var _ele4 = elesStillInside[_i6];
13977
13978 if (_ele4.isParent()) {
13979 cy._private.hasCompoundNodes = true;
13980 break;
13981 }
13982 }
13983
13984 var removedElements = new Collection(this.cy(), elesToRemove);
13985
13986 if (removedElements.size() > 0) {
13987 // must manually notify since trigger won't do this automatically once removed
13988 if (notifyRenderer) {
13989 removedElements.emitAndNotify('remove');
13990 } else if (removeFromPool) {
13991 removedElements.emit('remove');
13992 }
13993 } // the parents who were modified by the removal need their style updated
13994
13995
13996 for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
13997 var _ele5 = alteredParents[_i7];
13998
13999 if (!removeFromPool || !_ele5.removed()) {
14000 _ele5.updateStyle();
14001 }
14002 }
14003
14004 return removedElements;
14005 };
14006
14007 elesfn$u.move = function (struct) {
14008 var cy = this._private.cy;
14009 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
14010 // (our calls to remove/restore do not remove from the graph or make events)
14011
14012 var notifyRenderer = false;
14013 var modifyPool = false;
14014
14015 var toString = function toString(id) {
14016 return id == null ? id : '' + id;
14017 }; // id must be string
14018
14019
14020 if (struct.source !== undefined || struct.target !== undefined) {
14021 var srcId = toString(struct.source);
14022 var tgtId = toString(struct.target);
14023 var srcExists = srcId != null && cy.hasElementWithId(srcId);
14024 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
14025
14026 if (srcExists || tgtExists) {
14027 cy.batch(function () {
14028 // avoid duplicate style updates
14029 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
14030
14031 eles.emitAndNotify('moveout');
14032
14033 for (var i = 0; i < eles.length; i++) {
14034 var ele = eles[i];
14035 var _data5 = ele._private.data;
14036
14037 if (ele.isEdge()) {
14038 if (srcExists) {
14039 _data5.source = srcId;
14040 }
14041
14042 if (tgtExists) {
14043 _data5.target = tgtId;
14044 }
14045 }
14046 }
14047
14048 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
14049 });
14050 eles.emitAndNotify('move');
14051 }
14052 } else if (struct.parent !== undefined) {
14053 // move node to new parent
14054 var parentId = toString(struct.parent);
14055 var parentExists = parentId === null || cy.hasElementWithId(parentId);
14056
14057 if (parentExists) {
14058 var pidToAssign = parentId === null ? undefined : parentId;
14059 cy.batch(function () {
14060 // avoid duplicate style updates
14061 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
14062
14063 updated.emitAndNotify('moveout');
14064
14065 for (var i = 0; i < eles.length; i++) {
14066 var ele = eles[i];
14067 var _data6 = ele._private.data;
14068
14069 if (ele.isNode()) {
14070 _data6.parent = pidToAssign;
14071 }
14072 }
14073
14074 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
14075 });
14076 eles.emitAndNotify('move');
14077 }
14078 }
14079
14080 return this;
14081 };
14082
14083 [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) {
14084 extend(elesfn$u, props);
14085 });
14086
14087 var corefn = {
14088 add: function add(opts) {
14089 var elements;
14090 var cy = this; // add the elements
14091
14092 if (elementOrCollection(opts)) {
14093 var eles = opts;
14094
14095 if (eles._private.cy === cy) {
14096 // same instance => just restore
14097 elements = eles.restore();
14098 } else {
14099 // otherwise, copy from json
14100 var jsons = [];
14101
14102 for (var i = 0; i < eles.length; i++) {
14103 var ele = eles[i];
14104 jsons.push(ele.json());
14105 }
14106
14107 elements = new Collection(cy, jsons);
14108 }
14109 } // specify an array of options
14110 else if (array(opts)) {
14111 var _jsons = opts;
14112 elements = new Collection(cy, _jsons);
14113 } // specify via opts.nodes and opts.edges
14114 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
14115 var elesByGroup = opts;
14116 var _jsons2 = [];
14117 var grs = ['nodes', 'edges'];
14118
14119 for (var _i = 0, il = grs.length; _i < il; _i++) {
14120 var group = grs[_i];
14121 var elesArray = elesByGroup[group];
14122
14123 if (array(elesArray)) {
14124 for (var j = 0, jl = elesArray.length; j < jl; j++) {
14125 var json = extend({
14126 group: group
14127 }, elesArray[j]);
14128
14129 _jsons2.push(json);
14130 }
14131 }
14132 }
14133
14134 elements = new Collection(cy, _jsons2);
14135 } // specify options for one element
14136 else {
14137 var _json = opts;
14138 elements = new Element(cy, _json).collection();
14139 }
14140
14141 return elements;
14142 },
14143 remove: function remove(collection) {
14144 if (elementOrCollection(collection)) ; else if (string(collection)) {
14145 var selector = collection;
14146 collection = this.$(selector);
14147 }
14148
14149 return collection.remove();
14150 }
14151 };
14152
14153 /* global Float32Array */
14154
14155 /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14156 function generateCubicBezier(mX1, mY1, mX2, mY2) {
14157 var NEWTON_ITERATIONS = 4,
14158 NEWTON_MIN_SLOPE = 0.001,
14159 SUBDIVISION_PRECISION = 0.0000001,
14160 SUBDIVISION_MAX_ITERATIONS = 10,
14161 kSplineTableSize = 11,
14162 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
14163 float32ArraySupported = typeof Float32Array !== 'undefined';
14164 /* Must contain four arguments. */
14165
14166 if (arguments.length !== 4) {
14167 return false;
14168 }
14169 /* Arguments must be numbers. */
14170
14171
14172 for (var i = 0; i < 4; ++i) {
14173 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
14174 return false;
14175 }
14176 }
14177 /* X values must be in the [0, 1] range. */
14178
14179
14180 mX1 = Math.min(mX1, 1);
14181 mX2 = Math.min(mX2, 1);
14182 mX1 = Math.max(mX1, 0);
14183 mX2 = Math.max(mX2, 0);
14184 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
14185
14186 function A(aA1, aA2) {
14187 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
14188 }
14189
14190 function B(aA1, aA2) {
14191 return 3.0 * aA2 - 6.0 * aA1;
14192 }
14193
14194 function C(aA1) {
14195 return 3.0 * aA1;
14196 }
14197
14198 function calcBezier(aT, aA1, aA2) {
14199 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
14200 }
14201
14202 function getSlope(aT, aA1, aA2) {
14203 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
14204 }
14205
14206 function newtonRaphsonIterate(aX, aGuessT) {
14207 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
14208 var currentSlope = getSlope(aGuessT, mX1, mX2);
14209
14210 if (currentSlope === 0.0) {
14211 return aGuessT;
14212 }
14213
14214 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
14215 aGuessT -= currentX / currentSlope;
14216 }
14217
14218 return aGuessT;
14219 }
14220
14221 function calcSampleValues() {
14222 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
14223 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
14224 }
14225 }
14226
14227 function binarySubdivide(aX, aA, aB) {
14228 var currentX,
14229 currentT,
14230 i = 0;
14231
14232 do {
14233 currentT = aA + (aB - aA) / 2.0;
14234 currentX = calcBezier(currentT, mX1, mX2) - aX;
14235
14236 if (currentX > 0.0) {
14237 aB = currentT;
14238 } else {
14239 aA = currentT;
14240 }
14241 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
14242
14243 return currentT;
14244 }
14245
14246 function getTForX(aX) {
14247 var intervalStart = 0.0,
14248 currentSample = 1,
14249 lastSample = kSplineTableSize - 1;
14250
14251 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
14252 intervalStart += kSampleStepSize;
14253 }
14254
14255 --currentSample;
14256 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
14257 guessForT = intervalStart + dist * kSampleStepSize,
14258 initialSlope = getSlope(guessForT, mX1, mX2);
14259
14260 if (initialSlope >= NEWTON_MIN_SLOPE) {
14261 return newtonRaphsonIterate(aX, guessForT);
14262 } else if (initialSlope === 0.0) {
14263 return guessForT;
14264 } else {
14265 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
14266 }
14267 }
14268
14269 var _precomputed = false;
14270
14271 function precompute() {
14272 _precomputed = true;
14273
14274 if (mX1 !== mY1 || mX2 !== mY2) {
14275 calcSampleValues();
14276 }
14277 }
14278
14279 var f = function f(aX) {
14280 if (!_precomputed) {
14281 precompute();
14282 }
14283
14284 if (mX1 === mY1 && mX2 === mY2) {
14285 return aX;
14286 }
14287
14288 if (aX === 0) {
14289 return 0;
14290 }
14291
14292 if (aX === 1) {
14293 return 1;
14294 }
14295
14296 return calcBezier(getTForX(aX), mY1, mY2);
14297 };
14298
14299 f.getControlPoints = function () {
14300 return [{
14301 x: mX1,
14302 y: mY1
14303 }, {
14304 x: mX2,
14305 y: mY2
14306 }];
14307 };
14308
14309 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
14310
14311 f.toString = function () {
14312 return str;
14313 };
14314
14315 return f;
14316 }
14317
14318 /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14319
14320 /* 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
14321 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
14322 var generateSpringRK4 = function () {
14323 function springAccelerationForState(state) {
14324 return -state.tension * state.x - state.friction * state.v;
14325 }
14326
14327 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
14328 var state = {
14329 x: initialState.x + derivative.dx * dt,
14330 v: initialState.v + derivative.dv * dt,
14331 tension: initialState.tension,
14332 friction: initialState.friction
14333 };
14334 return {
14335 dx: state.v,
14336 dv: springAccelerationForState(state)
14337 };
14338 }
14339
14340 function springIntegrateState(state, dt) {
14341 var a = {
14342 dx: state.v,
14343 dv: springAccelerationForState(state)
14344 },
14345 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
14346 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
14347 d = springEvaluateStateWithDerivative(state, dt, c),
14348 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
14349 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
14350 state.x = state.x + dxdt * dt;
14351 state.v = state.v + dvdt * dt;
14352 return state;
14353 }
14354
14355 return function springRK4Factory(tension, friction, duration) {
14356 var initState = {
14357 x: -1,
14358 v: 0,
14359 tension: null,
14360 friction: null
14361 },
14362 path = [0],
14363 time_lapsed = 0,
14364 tolerance = 1 / 10000,
14365 DT = 16 / 1000,
14366 have_duration,
14367 dt,
14368 last_state;
14369 tension = parseFloat(tension) || 500;
14370 friction = parseFloat(friction) || 20;
14371 duration = duration || null;
14372 initState.tension = tension;
14373 initState.friction = friction;
14374 have_duration = duration !== null;
14375 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
14376
14377 if (have_duration) {
14378 /* Run the simulation without a duration. */
14379 time_lapsed = springRK4Factory(tension, friction);
14380 /* Compute the adjusted time delta. */
14381
14382 dt = time_lapsed / duration * DT;
14383 } else {
14384 dt = DT;
14385 }
14386
14387 for (;;) {
14388 /* Next/step function .*/
14389 last_state = springIntegrateState(last_state || initState, dt);
14390 /* Store the position. */
14391
14392 path.push(1 + last_state.x);
14393 time_lapsed += 16;
14394 /* If the change threshold is reached, break. */
14395
14396 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
14397 break;
14398 }
14399 }
14400 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
14401 computed path and returns a snapshot of the position according to a given percentComplete. */
14402
14403
14404 return !have_duration ? time_lapsed : function (percentComplete) {
14405 return path[percentComplete * (path.length - 1) | 0];
14406 };
14407 };
14408 }();
14409
14410 var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
14411 var bezier = generateCubicBezier(t1, p1, t2, p2);
14412 return function (start, end, percent) {
14413 return start + (end - start) * bezier(percent);
14414 };
14415 };
14416
14417 var easings = {
14418 'linear': function linear(start, end, percent) {
14419 return start + (end - start) * percent;
14420 },
14421 // default easings
14422 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
14423 'ease-in': cubicBezier(0.42, 0, 1, 1),
14424 'ease-out': cubicBezier(0, 0, 0.58, 1),
14425 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
14426 // sine
14427 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
14428 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
14429 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
14430 // quad
14431 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
14432 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
14433 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
14434 // cubic
14435 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
14436 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
14437 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
14438 // quart
14439 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
14440 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
14441 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
14442 // quint
14443 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
14444 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
14445 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
14446 // expo
14447 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
14448 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
14449 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
14450 // circ
14451 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
14452 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
14453 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
14454 // user param easings...
14455 'spring': function spring(tension, friction, duration) {
14456 if (duration === 0) {
14457 // can't get a spring w/ duration 0
14458 return easings.linear; // duration 0 => jump to end so impl doesn't matter
14459 }
14460
14461 var spring = generateSpringRK4(tension, friction, duration);
14462 return function (start, end, percent) {
14463 return start + (end - start) * spring(percent);
14464 };
14465 },
14466 'cubic-bezier': cubicBezier
14467 };
14468
14469 function getEasedValue(type, start, end, percent, easingFn) {
14470 if (percent === 1) {
14471 return end;
14472 }
14473
14474 if (start === end) {
14475 return end;
14476 }
14477
14478 var val = easingFn(start, end, percent);
14479
14480 if (type == null) {
14481 return val;
14482 }
14483
14484 if (type.roundValue || type.color) {
14485 val = Math.round(val);
14486 }
14487
14488 if (type.min !== undefined) {
14489 val = Math.max(val, type.min);
14490 }
14491
14492 if (type.max !== undefined) {
14493 val = Math.min(val, type.max);
14494 }
14495
14496 return val;
14497 }
14498
14499 function getValue(prop, spec) {
14500 if (prop.pfValue != null || prop.value != null) {
14501 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
14502 return prop.pfValue;
14503 } else {
14504 return prop.value;
14505 }
14506 } else {
14507 return prop;
14508 }
14509 }
14510
14511 function ease(startProp, endProp, percent, easingFn, propSpec) {
14512 var type = propSpec != null ? propSpec.type : null;
14513
14514 if (percent < 0) {
14515 percent = 0;
14516 } else if (percent > 1) {
14517 percent = 1;
14518 }
14519
14520 var start = getValue(startProp, propSpec);
14521 var end = getValue(endProp, propSpec);
14522
14523 if (number(start) && number(end)) {
14524 return getEasedValue(type, start, end, percent, easingFn);
14525 } else if (array(start) && array(end)) {
14526 var easedArr = [];
14527
14528 for (var i = 0; i < end.length; i++) {
14529 var si = start[i];
14530 var ei = end[i];
14531
14532 if (si != null && ei != null) {
14533 var val = getEasedValue(type, si, ei, percent, easingFn);
14534 easedArr.push(val);
14535 } else {
14536 easedArr.push(ei);
14537 }
14538 }
14539
14540 return easedArr;
14541 }
14542
14543 return undefined;
14544 }
14545
14546 function step(self, ani, now, isCore) {
14547 var isEles = !isCore;
14548 var _p = self._private;
14549 var ani_p = ani._private;
14550 var pEasing = ani_p.easing;
14551 var startTime = ani_p.startTime;
14552 var cy = isCore ? self : self.cy();
14553 var style = cy.style();
14554
14555 if (!ani_p.easingImpl) {
14556 if (pEasing == null) {
14557 // use default
14558 ani_p.easingImpl = easings['linear'];
14559 } else {
14560 // then define w/ name
14561 var easingVals;
14562
14563 if (string(pEasing)) {
14564 var easingProp = style.parse('transition-timing-function', pEasing);
14565 easingVals = easingProp.value;
14566 } else {
14567 // then assume preparsed array
14568 easingVals = pEasing;
14569 }
14570
14571 var name, args;
14572
14573 if (string(easingVals)) {
14574 name = easingVals;
14575 args = [];
14576 } else {
14577 name = easingVals[1];
14578 args = easingVals.slice(2).map(function (n) {
14579 return +n;
14580 });
14581 }
14582
14583 if (args.length > 0) {
14584 // create with args
14585 if (name === 'spring') {
14586 args.push(ani_p.duration); // need duration to generate spring
14587 }
14588
14589 ani_p.easingImpl = easings[name].apply(null, args);
14590 } else {
14591 // static impl by name
14592 ani_p.easingImpl = easings[name];
14593 }
14594 }
14595 }
14596
14597 var easing = ani_p.easingImpl;
14598 var percent;
14599
14600 if (ani_p.duration === 0) {
14601 percent = 1;
14602 } else {
14603 percent = (now - startTime) / ani_p.duration;
14604 }
14605
14606 if (ani_p.applying) {
14607 percent = ani_p.progress;
14608 }
14609
14610 if (percent < 0) {
14611 percent = 0;
14612 } else if (percent > 1) {
14613 percent = 1;
14614 }
14615
14616 if (ani_p.delay == null) {
14617 // then update
14618 var startPos = ani_p.startPosition;
14619 var endPos = ani_p.position;
14620
14621 if (endPos && isEles && !self.locked()) {
14622 var newPos = {};
14623
14624 if (valid(startPos.x, endPos.x)) {
14625 newPos.x = ease(startPos.x, endPos.x, percent, easing);
14626 }
14627
14628 if (valid(startPos.y, endPos.y)) {
14629 newPos.y = ease(startPos.y, endPos.y, percent, easing);
14630 }
14631
14632 self.position(newPos);
14633 }
14634
14635 var startPan = ani_p.startPan;
14636 var endPan = ani_p.pan;
14637 var pan = _p.pan;
14638 var animatingPan = endPan != null && isCore;
14639
14640 if (animatingPan) {
14641 if (valid(startPan.x, endPan.x)) {
14642 pan.x = ease(startPan.x, endPan.x, percent, easing);
14643 }
14644
14645 if (valid(startPan.y, endPan.y)) {
14646 pan.y = ease(startPan.y, endPan.y, percent, easing);
14647 }
14648
14649 self.emit('pan');
14650 }
14651
14652 var startZoom = ani_p.startZoom;
14653 var endZoom = ani_p.zoom;
14654 var animatingZoom = endZoom != null && isCore;
14655
14656 if (animatingZoom) {
14657 if (valid(startZoom, endZoom)) {
14658 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
14659 }
14660
14661 self.emit('zoom');
14662 }
14663
14664 if (animatingPan || animatingZoom) {
14665 self.emit('viewport');
14666 }
14667
14668 var props = ani_p.style;
14669
14670 if (props && props.length > 0 && isEles) {
14671 for (var i = 0; i < props.length; i++) {
14672 var prop = props[i];
14673 var _name = prop.name;
14674 var end = prop;
14675 var start = ani_p.startStyle[_name];
14676 var propSpec = style.properties[start.name];
14677 var easedVal = ease(start, end, percent, easing, propSpec);
14678 style.overrideBypass(self, _name, easedVal);
14679 } // for props
14680
14681
14682 self.emit('style');
14683 } // if
14684
14685 }
14686
14687 ani_p.progress = percent;
14688 return percent;
14689 }
14690
14691 function valid(start, end) {
14692 if (start == null || end == null) {
14693 return false;
14694 }
14695
14696 if (number(start) && number(end)) {
14697 return true;
14698 } else if (start && end) {
14699 return true;
14700 }
14701
14702 return false;
14703 }
14704
14705 function startAnimation(self, ani, now, isCore) {
14706 var ani_p = ani._private;
14707 ani_p.started = true;
14708 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14709 }
14710
14711 function stepAll(now, cy) {
14712 var eles = cy._private.aniEles;
14713 var doneEles = [];
14714
14715 function stepOne(ele, isCore) {
14716 var _p = ele._private;
14717 var current = _p.animation.current;
14718 var queue = _p.animation.queue;
14719 var ranAnis = false; // cancel all animations on display:none ele
14720
14721 if (!isCore && ele.pstyle('display').value === 'none') {
14722 // put all current and queue animations in this tick's current list
14723 // and empty the lists for the element
14724 current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
14725
14726 for (var i = 0; i < current.length; i++) {
14727 current[i].stop();
14728 }
14729 } // if nothing currently animating, get something from the queue
14730
14731
14732 if (current.length === 0) {
14733 var next = queue.shift();
14734
14735 if (next) {
14736 current.push(next);
14737 }
14738 }
14739
14740 var callbacks = function callbacks(_callbacks) {
14741 for (var j = _callbacks.length - 1; j >= 0; j--) {
14742 var cb = _callbacks[j];
14743 cb();
14744 }
14745
14746 _callbacks.splice(0, _callbacks.length);
14747 }; // step and remove if done
14748
14749
14750 for (var _i = current.length - 1; _i >= 0; _i--) {
14751 var ani = current[_i];
14752 var ani_p = ani._private;
14753
14754 if (ani_p.stopped) {
14755 current.splice(_i, 1);
14756 ani_p.hooked = false;
14757 ani_p.playing = false;
14758 ani_p.started = false;
14759 callbacks(ani_p.frames);
14760 continue;
14761 }
14762
14763 if (!ani_p.playing && !ani_p.applying) {
14764 continue;
14765 } // an apply() while playing shouldn't do anything
14766
14767
14768 if (ani_p.playing && ani_p.applying) {
14769 ani_p.applying = false;
14770 }
14771
14772 if (!ani_p.started) {
14773 startAnimation(ele, ani, now);
14774 }
14775
14776 step(ele, ani, now, isCore);
14777
14778 if (ani_p.applying) {
14779 ani_p.applying = false;
14780 }
14781
14782 callbacks(ani_p.frames);
14783
14784 if (ani_p.step != null) {
14785 ani_p.step(now);
14786 }
14787
14788 if (ani.completed()) {
14789 current.splice(_i, 1);
14790 ani_p.hooked = false;
14791 ani_p.playing = false;
14792 ani_p.started = false;
14793 callbacks(ani_p.completes);
14794 }
14795
14796 ranAnis = true;
14797 }
14798
14799 if (!isCore && current.length === 0 && queue.length === 0) {
14800 doneEles.push(ele);
14801 }
14802
14803 return ranAnis;
14804 } // stepElement
14805 // handle all eles
14806
14807
14808 var ranEleAni = false;
14809
14810 for (var e = 0; e < eles.length; e++) {
14811 var ele = eles[e];
14812 var handledThisEle = stepOne(ele);
14813 ranEleAni = ranEleAni || handledThisEle;
14814 } // each element
14815
14816
14817 var ranCoreAni = stepOne(cy, true); // notify renderer
14818
14819 if (ranEleAni || ranCoreAni) {
14820 if (eles.length > 0) {
14821 cy.notify('draw', eles);
14822 } else {
14823 cy.notify('draw');
14824 }
14825 } // remove elements from list of currently animating if its queues are empty
14826
14827
14828 eles.unmerge(doneEles);
14829 cy.emit('step');
14830 } // stepAll
14831
14832 var corefn$1 = {
14833 // pull in animation functions
14834 animate: define$3.animate(),
14835 animation: define$3.animation(),
14836 animated: define$3.animated(),
14837 clearQueue: define$3.clearQueue(),
14838 delay: define$3.delay(),
14839 delayAnimation: define$3.delayAnimation(),
14840 stop: define$3.stop(),
14841 addToAnimationPool: function addToAnimationPool(eles) {
14842 var cy = this;
14843
14844 if (!cy.styleEnabled()) {
14845 return;
14846 } // save cycles when no style used
14847
14848
14849 cy._private.aniEles.merge(eles);
14850 },
14851 stopAnimationLoop: function stopAnimationLoop() {
14852 this._private.animationsRunning = false;
14853 },
14854 startAnimationLoop: function startAnimationLoop() {
14855 var cy = this;
14856 cy._private.animationsRunning = true;
14857
14858 if (!cy.styleEnabled()) {
14859 return;
14860 } // save cycles when no style used
14861 // NB the animation loop will exec in headless environments if style enabled
14862 // and explicit cy.destroy() is necessary to stop the loop
14863
14864
14865 function headlessStep() {
14866 if (!cy._private.animationsRunning) {
14867 return;
14868 }
14869
14870 requestAnimationFrame(function animationStep(now) {
14871 stepAll(now, cy);
14872 headlessStep();
14873 });
14874 }
14875
14876 var renderer = cy.renderer();
14877
14878 if (renderer && renderer.beforeRender) {
14879 // let the renderer schedule animations
14880 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14881 stepAll(now, cy);
14882 }, renderer.beforeRenderPriorities.animations);
14883 } else {
14884 // manage the animation loop ourselves
14885 headlessStep(); // first call
14886 }
14887 }
14888 };
14889
14890 var emitterOptions$1 = {
14891 qualifierCompare: function qualifierCompare(selector1, selector2) {
14892 if (selector1 == null || selector2 == null) {
14893 return selector1 == null && selector2 == null;
14894 } else {
14895 return selector1.sameText(selector2);
14896 }
14897 },
14898 eventMatches: function eventMatches(cy, listener, eventObj) {
14899 var selector = listener.qualifier;
14900
14901 if (selector != null) {
14902 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14903 }
14904
14905 return true;
14906 },
14907 addEventFields: function addEventFields(cy, evt) {
14908 evt.cy = cy;
14909 evt.target = cy;
14910 },
14911 callbackContext: function callbackContext(cy, listener, eventObj) {
14912 return listener.qualifier != null ? eventObj.target : cy;
14913 }
14914 };
14915
14916 var argSelector$1 = function argSelector(arg) {
14917 if (string(arg)) {
14918 return new Selector(arg);
14919 } else {
14920 return arg;
14921 }
14922 };
14923
14924 var elesfn$v = {
14925 createEmitter: function createEmitter() {
14926 var _p = this._private;
14927
14928 if (!_p.emitter) {
14929 _p.emitter = new Emitter(emitterOptions$1, this);
14930 }
14931
14932 return this;
14933 },
14934 emitter: function emitter() {
14935 return this._private.emitter;
14936 },
14937 on: function on(events, selector, callback) {
14938 this.emitter().on(events, argSelector$1(selector), callback);
14939 return this;
14940 },
14941 removeListener: function removeListener(events, selector, callback) {
14942 this.emitter().removeListener(events, argSelector$1(selector), callback);
14943 return this;
14944 },
14945 removeAllListeners: function removeAllListeners() {
14946 this.emitter().removeAllListeners();
14947 return this;
14948 },
14949 one: function one(events, selector, callback) {
14950 this.emitter().one(events, argSelector$1(selector), callback);
14951 return this;
14952 },
14953 once: function once(events, selector, callback) {
14954 this.emitter().one(events, argSelector$1(selector), callback);
14955 return this;
14956 },
14957 emit: function emit(events, extraParams) {
14958 this.emitter().emit(events, extraParams);
14959 return this;
14960 },
14961 emitAndNotify: function emitAndNotify(event, eles) {
14962 this.emit(event);
14963 this.notify(event, eles);
14964 return this;
14965 }
14966 };
14967 define$3.eventAliasesOn(elesfn$v);
14968
14969 var corefn$2 = {
14970 png: function png(options) {
14971 var renderer = this._private.renderer;
14972 options = options || {};
14973 return renderer.png(options);
14974 },
14975 jpg: function jpg(options) {
14976 var renderer = this._private.renderer;
14977 options = options || {};
14978 options.bg = options.bg || '#fff';
14979 return renderer.jpg(options);
14980 }
14981 };
14982 corefn$2.jpeg = corefn$2.jpg;
14983
14984 var corefn$3 = {
14985 layout: function layout(options) {
14986 var cy = this;
14987
14988 if (options == null) {
14989 error('Layout options must be specified to make a layout');
14990 return;
14991 }
14992
14993 if (options.name == null) {
14994 error('A `name` must be specified to make a layout');
14995 return;
14996 }
14997
14998 var name = options.name;
14999 var Layout = cy.extension('layout', name);
15000
15001 if (Layout == null) {
15002 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
15003 return;
15004 }
15005
15006 var eles;
15007
15008 if (string(options.eles)) {
15009 eles = cy.$(options.eles);
15010 } else {
15011 eles = options.eles != null ? options.eles : cy.$();
15012 }
15013
15014 var layout = new Layout(extend({}, options, {
15015 cy: cy,
15016 eles: eles
15017 }));
15018 return layout;
15019 }
15020 };
15021 corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
15022
15023 var corefn$4 = {
15024 notify: function notify(eventName, eventEles) {
15025 var _p = this._private;
15026
15027 if (this.batching()) {
15028 _p.batchNotifications = _p.batchNotifications || {};
15029 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
15030
15031 if (eventEles != null) {
15032 eles.merge(eventEles);
15033 }
15034
15035 return; // notifications are disabled during batching
15036 }
15037
15038 if (!_p.notificationsEnabled) {
15039 return;
15040 } // exit on disabled
15041
15042
15043 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
15044
15045 if (this.destroyed() || !renderer) {
15046 return;
15047 }
15048
15049 renderer.notify(eventName, eventEles);
15050 },
15051 notifications: function notifications(bool) {
15052 var p = this._private;
15053
15054 if (bool === undefined) {
15055 return p.notificationsEnabled;
15056 } else {
15057 p.notificationsEnabled = bool ? true : false;
15058 }
15059
15060 return this;
15061 },
15062 noNotifications: function noNotifications(callback) {
15063 this.notifications(false);
15064 callback();
15065 this.notifications(true);
15066 },
15067 batching: function batching() {
15068 return this._private.batchCount > 0;
15069 },
15070 startBatch: function startBatch() {
15071 var _p = this._private;
15072
15073 if (_p.batchCount == null) {
15074 _p.batchCount = 0;
15075 }
15076
15077 if (_p.batchCount === 0) {
15078 _p.batchStyleEles = this.collection();
15079 _p.batchNotifications = {};
15080 }
15081
15082 _p.batchCount++;
15083 return this;
15084 },
15085 endBatch: function endBatch() {
15086 var _p = this._private;
15087
15088 if (_p.batchCount === 0) {
15089 return this;
15090 }
15091
15092 _p.batchCount--;
15093
15094 if (_p.batchCount === 0) {
15095 // update style for dirty eles
15096 _p.batchStyleEles.updateStyle();
15097
15098 var renderer = this.renderer(); // notify the renderer of queued eles and event types
15099
15100 Object.keys(_p.batchNotifications).forEach(function (eventName) {
15101 var eles = _p.batchNotifications[eventName];
15102
15103 if (eles.empty()) {
15104 renderer.notify(eventName);
15105 } else {
15106 renderer.notify(eventName, eles);
15107 }
15108 });
15109 }
15110
15111 return this;
15112 },
15113 batch: function batch(callback) {
15114 this.startBatch();
15115 callback();
15116 this.endBatch();
15117 return this;
15118 },
15119 // for backwards compatibility
15120 batchData: function batchData(map) {
15121 var cy = this;
15122 return this.batch(function () {
15123 var ids = Object.keys(map);
15124
15125 for (var i = 0; i < ids.length; i++) {
15126 var id = ids[i];
15127 var data = map[id];
15128 var ele = cy.getElementById(id);
15129 ele.data(data);
15130 }
15131 });
15132 }
15133 };
15134
15135 var rendererDefaults = defaults({
15136 hideEdgesOnViewport: false,
15137 textureOnViewport: false,
15138 motionBlur: false,
15139 motionBlurOpacity: 0.05,
15140 pixelRatio: undefined,
15141 desktopTapThreshold: 4,
15142 touchTapThreshold: 8,
15143 wheelSensitivity: 1,
15144 debug: false,
15145 showFps: false
15146 });
15147 var corefn$5 = {
15148 renderTo: function renderTo(context, zoom, pan, pxRatio) {
15149 var r = this._private.renderer;
15150 r.renderTo(context, zoom, pan, pxRatio);
15151 return this;
15152 },
15153 renderer: function renderer() {
15154 return this._private.renderer;
15155 },
15156 forceRender: function forceRender() {
15157 this.notify('draw');
15158 return this;
15159 },
15160 resize: function resize() {
15161 this.invalidateSize();
15162 this.emitAndNotify('resize');
15163 return this;
15164 },
15165 initRenderer: function initRenderer(options) {
15166 var cy = this;
15167 var RendererProto = cy.extension('renderer', options.name);
15168
15169 if (RendererProto == null) {
15170 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
15171 return;
15172 }
15173
15174 if (options.wheelSensitivity !== undefined) {
15175 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.");
15176 }
15177
15178 var rOpts = rendererDefaults(options);
15179 rOpts.cy = cy;
15180 cy._private.renderer = new RendererProto(rOpts);
15181 this.notify('init');
15182 },
15183 destroyRenderer: function destroyRenderer() {
15184 var cy = this;
15185 cy.notify('destroy'); // destroy the renderer
15186
15187 var domEle = cy.container();
15188
15189 if (domEle) {
15190 domEle._cyreg = null;
15191
15192 while (domEle.childNodes.length > 0) {
15193 domEle.removeChild(domEle.childNodes[0]);
15194 }
15195 }
15196
15197 cy._private.renderer = null; // to be extra safe, remove the ref
15198
15199 cy.mutableElements().forEach(function (ele) {
15200 var _p = ele._private;
15201 _p.rscratch = {};
15202 _p.rstyle = {};
15203 _p.animation.current = [];
15204 _p.animation.queue = [];
15205 });
15206 },
15207 onRender: function onRender(fn) {
15208 return this.on('render', fn);
15209 },
15210 offRender: function offRender(fn) {
15211 return this.off('render', fn);
15212 }
15213 };
15214 corefn$5.invalidateDimensions = corefn$5.resize;
15215
15216 var corefn$6 = {
15217 // get a collection
15218 // - empty collection on no args
15219 // - collection of elements in the graph on selector arg
15220 // - guarantee a returned collection when elements or collection specified
15221 collection: function collection(eles, opts) {
15222 if (string(eles)) {
15223 return this.$(eles);
15224 } else if (elementOrCollection(eles)) {
15225 return eles.collection();
15226 } else if (array(eles)) {
15227 return new Collection(this, eles, opts);
15228 }
15229
15230 return new Collection(this);
15231 },
15232 nodes: function nodes(selector) {
15233 var nodes = this.$(function (ele) {
15234 return ele.isNode();
15235 });
15236
15237 if (selector) {
15238 return nodes.filter(selector);
15239 }
15240
15241 return nodes;
15242 },
15243 edges: function edges(selector) {
15244 var edges = this.$(function (ele) {
15245 return ele.isEdge();
15246 });
15247
15248 if (selector) {
15249 return edges.filter(selector);
15250 }
15251
15252 return edges;
15253 },
15254 // search the graph like jQuery
15255 $: function $(selector) {
15256 var eles = this._private.elements;
15257
15258 if (selector) {
15259 return eles.filter(selector);
15260 } else {
15261 return eles.spawnSelf();
15262 }
15263 },
15264 mutableElements: function mutableElements() {
15265 return this._private.elements;
15266 }
15267 }; // aliases
15268
15269 corefn$6.elements = corefn$6.filter = corefn$6.$;
15270
15271 var styfn = {}; // keys for style blocks, e.g. ttfftt
15272
15273 var TRUE = 't';
15274 var FALSE = 'f'; // (potentially expensive calculation)
15275 // apply the style to the element based on
15276 // - its bypass
15277 // - what selectors match it
15278
15279 styfn.apply = function (eles) {
15280 var self = this;
15281 var _p = self._private;
15282 var cy = _p.cy;
15283 var updatedEles = cy.collection();
15284
15285 if (_p.newStyle) {
15286 // clear style caches
15287 _p.contextStyles = {};
15288 _p.propDiffs = {};
15289 self.cleanElements(eles, true);
15290 }
15291
15292 for (var ie = 0; ie < eles.length; ie++) {
15293 var ele = eles[ie];
15294 var cxtMeta = self.getContextMeta(ele);
15295
15296 if (cxtMeta.empty) {
15297 continue;
15298 }
15299
15300 var cxtStyle = self.getContextStyle(cxtMeta);
15301 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
15302
15303 if (!_p.newStyle) {
15304 self.updateTransitions(ele, app.diffProps);
15305 }
15306
15307 var hintsDiff = self.updateStyleHints(ele);
15308
15309 if (hintsDiff) {
15310 updatedEles.merge(ele);
15311 }
15312 } // for elements
15313
15314
15315 _p.newStyle = false;
15316 return updatedEles;
15317 };
15318
15319 styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
15320 var self = this;
15321 var cache = self._private.propDiffs = self._private.propDiffs || {};
15322 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
15323 var cachedVal = cache[dualCxtKey];
15324
15325 if (cachedVal) {
15326 return cachedVal;
15327 }
15328
15329 var diffProps = [];
15330 var addedProp = {};
15331
15332 for (var i = 0; i < self.length; i++) {
15333 var cxt = self[i];
15334 var oldHasCxt = oldCxtKey[i] === TRUE;
15335 var newHasCxt = newCxtKey[i] === TRUE;
15336 var cxtHasDiffed = oldHasCxt !== newHasCxt;
15337 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
15338
15339 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
15340 var props = void 0;
15341
15342 if (cxtHasDiffed && cxtHasMappedProps) {
15343 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
15344 } else if (cxtHasDiffed) {
15345 props = cxt.properties; // need to check them all
15346 } else if (cxtHasMappedProps) {
15347 props = cxt.mappedProperties; // only need to check mapped
15348 }
15349
15350 for (var j = 0; j < props.length; j++) {
15351 var prop = props[j];
15352 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
15353 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
15354 // is cached)
15355
15356 var laterCxtOverrides = false;
15357
15358 for (var k = i + 1; k < self.length; k++) {
15359 var laterCxt = self[k];
15360 var hasLaterCxt = newCxtKey[k] === TRUE;
15361
15362 if (!hasLaterCxt) {
15363 continue;
15364 } // can't override unless the context is active
15365
15366
15367 laterCxtOverrides = laterCxt.properties[prop.name] != null;
15368
15369 if (laterCxtOverrides) {
15370 break;
15371 } // exit early as long as one later context overrides
15372
15373 }
15374
15375 if (!addedProp[name] && !laterCxtOverrides) {
15376 addedProp[name] = true;
15377 diffProps.push(name);
15378 }
15379 } // for props
15380
15381 } // if
15382
15383 } // for contexts
15384
15385
15386 cache[dualCxtKey] = diffProps;
15387 return diffProps;
15388 };
15389
15390 styfn.getContextMeta = function (ele) {
15391 var self = this;
15392 var cxtKey = '';
15393 var diffProps;
15394 var prevKey = ele._private.styleCxtKey || '';
15395
15396 if (self._private.newStyle) {
15397 prevKey = ''; // since we need to apply all style if a fresh stylesheet
15398 } // get the cxt key
15399
15400
15401 for (var i = 0; i < self.length; i++) {
15402 var context = self[i];
15403 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
15404
15405 if (contextSelectorMatches) {
15406 cxtKey += TRUE;
15407 } else {
15408 cxtKey += FALSE;
15409 }
15410 } // for context
15411
15412
15413 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
15414 ele._private.styleCxtKey = cxtKey;
15415 return {
15416 key: cxtKey,
15417 diffPropNames: diffProps,
15418 empty: diffProps.length === 0
15419 };
15420 }; // gets a computed ele style object based on matched contexts
15421
15422
15423 styfn.getContextStyle = function (cxtMeta) {
15424 var cxtKey = cxtMeta.key;
15425 var self = this;
15426 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
15427
15428 if (cxtStyles[cxtKey]) {
15429 return cxtStyles[cxtKey];
15430 }
15431
15432 var style = {
15433 _private: {
15434 key: cxtKey
15435 }
15436 };
15437
15438 for (var i = 0; i < self.length; i++) {
15439 var cxt = self[i];
15440 var hasCxt = cxtKey[i] === TRUE;
15441
15442 if (!hasCxt) {
15443 continue;
15444 }
15445
15446 for (var j = 0; j < cxt.properties.length; j++) {
15447 var prop = cxt.properties[j];
15448 style[prop.name] = prop;
15449 }
15450 }
15451
15452 cxtStyles[cxtKey] = style;
15453 return style;
15454 };
15455
15456 styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
15457 var self = this;
15458 var diffProps = cxtMeta.diffPropNames;
15459 var retDiffProps = {};
15460 var types = self.types;
15461
15462 for (var i = 0; i < diffProps.length; i++) {
15463 var diffPropName = diffProps[i];
15464 var cxtProp = cxtStyle[diffPropName];
15465 var eleProp = ele.pstyle(diffPropName);
15466
15467 if (!cxtProp) {
15468 // no context prop means delete
15469 if (!eleProp) {
15470 continue; // no existing prop means nothing needs to be removed
15471 // nb affects initial application on mapped values like control-point-distances
15472 } else if (eleProp.bypass) {
15473 cxtProp = {
15474 name: diffPropName,
15475 deleteBypassed: true
15476 };
15477 } else {
15478 cxtProp = {
15479 name: diffPropName,
15480 "delete": true
15481 };
15482 }
15483 } // save cycles when the context prop doesn't need to be applied
15484
15485
15486 if (eleProp === cxtProp) {
15487 continue;
15488 } // save cycles when a mapped context prop doesn't need to be applied
15489
15490
15491 if (cxtProp.mapped === types.fn // context prop is function mapper
15492 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
15493 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
15494 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
15495 ) {
15496 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
15497 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
15498
15499 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
15500
15501 if (fnValue === mapping.prevFnValue) {
15502 continue;
15503 }
15504 }
15505
15506 var retDiffProp = retDiffProps[diffPropName] = {
15507 prev: eleProp
15508 };
15509 self.applyParsedProperty(ele, cxtProp);
15510 retDiffProp.next = ele.pstyle(diffPropName);
15511
15512 if (retDiffProp.next && retDiffProp.next.bypass) {
15513 retDiffProp.next = retDiffProp.next.bypassed;
15514 }
15515 }
15516
15517 return {
15518 diffProps: retDiffProps
15519 };
15520 };
15521
15522 styfn.updateStyleHints = function (ele) {
15523 var _p = ele._private;
15524 var self = this;
15525 var propNames = self.propertyGroupNames;
15526 var propGrKeys = self.propertyGroupKeys;
15527
15528 var propHash = function propHash(ele, propNames, seedKey) {
15529 return self.getPropertiesHash(ele, propNames, seedKey);
15530 };
15531
15532 var oldStyleKey = _p.styleKey;
15533
15534 if (ele.removed()) {
15535 return false;
15536 }
15537
15538 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
15539 // but lazily -- only use non-default prop values to reduce the number of hashes
15540 //
15541
15542 var overriddenStyles = ele._private.style;
15543 propNames = Object.keys(overriddenStyles);
15544
15545 for (var i = 0; i < propGrKeys.length; i++) {
15546 var grKey = propGrKeys[i];
15547 _p.styleKeys[grKey] = 0;
15548 }
15549
15550 var updateGrKey = function updateGrKey(val, grKey) {
15551 return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
15552 };
15553
15554 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
15555 for (var j = 0; j < strVal.length; j++) {
15556 updateGrKey(strVal.charCodeAt(j), grKey);
15557 }
15558 }; // - hashing works on 32 bit ints b/c we use bitwise ops
15559 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
15560 // - raise up small numbers so more significant digits are seen by hashing
15561 // - make small numbers larger than a normal value to avoid collisions
15562 // - works in practice and it's relatively cheap
15563
15564
15565 var N = 2000000000;
15566
15567 var cleanNum = function cleanNum(val) {
15568 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
15569 };
15570
15571 for (var _i = 0; _i < propNames.length; _i++) {
15572 var name = propNames[_i];
15573 var parsedProp = overriddenStyles[name];
15574
15575 if (parsedProp == null) {
15576 continue;
15577 }
15578
15579 var propInfo = this.properties[name];
15580 var type = propInfo.type;
15581 var _grKey = propInfo.groupKey;
15582 var normalizedNumberVal = void 0;
15583
15584 if (propInfo.hashOverride != null) {
15585 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
15586 } else if (parsedProp.pfValue != null) {
15587 normalizedNumberVal = parsedProp.pfValue;
15588 } // might not be a number if it allows enums
15589
15590
15591 var numberVal = propInfo.enums == null ? parsedProp.value : null;
15592 var haveNormNum = normalizedNumberVal != null;
15593 var haveUnitedNum = numberVal != null;
15594 var haveNum = haveNormNum || haveUnitedNum;
15595 var units = parsedProp.units; // numbers are cheaper to hash than strings
15596 // 1 hash op vs n hash ops (for length n string)
15597
15598 if (type.number && haveNum) {
15599 var v = haveNormNum ? normalizedNumberVal : numberVal;
15600
15601 if (type.multiple) {
15602 for (var _i2 = 0; _i2 < v.length; _i2++) {
15603 updateGrKey(cleanNum(v[_i2]), _grKey);
15604 }
15605 } else {
15606 updateGrKey(cleanNum(v), _grKey);
15607 }
15608
15609 if (!haveNormNum && units != null) {
15610 updateGrKeyWStr(units, _grKey);
15611 }
15612 } else {
15613 updateGrKeyWStr(parsedProp.strValue, _grKey);
15614 }
15615 } // overall style key
15616 //
15617
15618
15619 var hash = 0;
15620
15621 for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
15622 var _grKey2 = propGrKeys[_i3];
15623 var grHash = _p.styleKeys[_grKey2];
15624 hash = hashInt(grHash, hash);
15625 }
15626
15627 _p.styleKey = hash; // label dims
15628 //
15629
15630 var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
15631 _p.labelKey = propHash(ele, ['label'], labelDimsKey);
15632 _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
15633
15634 if (!isNode) {
15635 _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
15636 _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
15637 _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
15638 _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
15639 } // node
15640 //
15641
15642
15643 if (isNode) {
15644 var _p$styleKeys = _p.styleKeys,
15645 nodeBody = _p$styleKeys.nodeBody,
15646 nodeBorder = _p$styleKeys.nodeBorder,
15647 backgroundImage = _p$styleKeys.backgroundImage,
15648 compound = _p$styleKeys.compound,
15649 pie = _p$styleKeys.pie;
15650 _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
15651 _p.hasPie = pie != 0;
15652 }
15653
15654 return oldStyleKey !== _p.styleKey;
15655 };
15656
15657 styfn.clearStyleHints = function (ele) {
15658 var _p = ele._private;
15659 _p.styleKeys = {};
15660 _p.styleKey = null;
15661 _p.labelKey = null;
15662 _p.labelStyleKey = null;
15663 _p.sourceLabelKey = null;
15664 _p.sourceLabelStyleKey = null;
15665 _p.targetLabelKey = null;
15666 _p.targetLabelStyleKey = null;
15667 _p.nodeKey = null;
15668 _p.hasPie = null;
15669 }; // apply a property to the style (for internal use)
15670 // returns whether application was successful
15671 //
15672 // now, this function flattens the property, and here's how:
15673 //
15674 // for parsedProp:{ bypass: true, deleteBypass: true }
15675 // no property is generated, instead the bypass property in the
15676 // element's style is replaced by what's pointed to by the `bypassed`
15677 // field in the bypass property (i.e. restoring the property the
15678 // bypass was overriding)
15679 //
15680 // for parsedProp:{ mapped: truthy }
15681 // the generated flattenedProp:{ mapping: prop }
15682 //
15683 // for parsedProp:{ bypass: true }
15684 // the generated flattenedProp:{ bypassed: parsedProp }
15685
15686
15687 styfn.applyParsedProperty = function (ele, parsedProp) {
15688 var self = this;
15689 var prop = parsedProp;
15690 var style = ele._private.style;
15691 var flatProp;
15692 var types = self.types;
15693 var type = self.properties[prop.name].type;
15694 var propIsBypass = prop.bypass;
15695 var origProp = style[prop.name];
15696 var origPropIsBypass = origProp && origProp.bypass;
15697 var _p = ele._private;
15698 var flatPropMapping = 'mapping';
15699
15700 var getVal = function getVal(p) {
15701 if (p == null) {
15702 return null;
15703 } else if (p.pfValue != null) {
15704 return p.pfValue;
15705 } else {
15706 return p.value;
15707 }
15708 };
15709
15710 var checkTriggers = function checkTriggers() {
15711 var fromVal = getVal(origProp);
15712 var toVal = getVal(prop);
15713 self.checkTriggers(ele, prop.name, fromVal, toVal);
15714 }; // edge sanity checks to prevent the client from making serious mistakes
15715
15716
15717 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15718 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15719 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15720 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15721 }
15722
15723 if (prop["delete"]) {
15724 // delete the property and use the default value on falsey value
15725 style[prop.name] = undefined;
15726 checkTriggers();
15727 return true;
15728 }
15729
15730 if (prop.deleteBypassed) {
15731 // delete the property that the
15732 if (!origProp) {
15733 checkTriggers();
15734 return true; // can't delete if no prop
15735 } else if (origProp.bypass) {
15736 // delete bypassed
15737 origProp.bypassed = undefined;
15738 checkTriggers();
15739 return true;
15740 } else {
15741 return false; // we're unsuccessful deleting the bypassed
15742 }
15743 } // check if we need to delete the current bypass
15744
15745
15746 if (prop.deleteBypass) {
15747 // then this property is just here to indicate we need to delete
15748 if (!origProp) {
15749 checkTriggers();
15750 return true; // property is already not defined
15751 } else if (origProp.bypass) {
15752 // then replace the bypass property with the original
15753 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15754 style[prop.name] = origProp.bypassed;
15755 checkTriggers();
15756 return true;
15757 } else {
15758 return false; // we're unsuccessful deleting the bypass
15759 }
15760 }
15761
15762 var printMappingErr = function printMappingErr() {
15763 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');
15764 }; // put the property in the style objects
15765
15766
15767 switch (prop.mapped) {
15768 // flatten the property if mapped
15769 case types.mapData:
15770 {
15771 // flatten the field (e.g. data.foo.bar)
15772 var fields = prop.field.split('.');
15773 var fieldVal = _p.data;
15774
15775 for (var i = 0; i < fields.length && fieldVal; i++) {
15776 var field = fields[i];
15777 fieldVal = fieldVal[field];
15778 }
15779
15780 if (fieldVal == null) {
15781 printMappingErr();
15782 return false;
15783 }
15784
15785 var percent;
15786
15787 if (!number(fieldVal)) {
15788 // then don't apply and fall back on the existing style
15789 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15790 return false;
15791 } else {
15792 var fieldWidth = prop.fieldMax - prop.fieldMin;
15793
15794 if (fieldWidth === 0) {
15795 // safety check -- not strictly necessary as no props of zero range should be passed here
15796 percent = 0;
15797 } else {
15798 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15799 }
15800 } // make sure to bound percent value
15801
15802
15803 if (percent < 0) {
15804 percent = 0;
15805 } else if (percent > 1) {
15806 percent = 1;
15807 }
15808
15809 if (type.color) {
15810 var r1 = prop.valueMin[0];
15811 var r2 = prop.valueMax[0];
15812 var g1 = prop.valueMin[1];
15813 var g2 = prop.valueMax[1];
15814 var b1 = prop.valueMin[2];
15815 var b2 = prop.valueMax[2];
15816 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15817 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15818 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)];
15819 flatProp = {
15820 // colours are simple, so just create the flat property instead of expensive string parsing
15821 bypass: prop.bypass,
15822 // we're a bypass if the mapping property is a bypass
15823 name: prop.name,
15824 value: clr,
15825 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15826 };
15827 } else if (type.number) {
15828 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15829 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15830 } else {
15831 return false; // can only map to colours and numbers
15832 }
15833
15834 if (!flatProp) {
15835 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15836 printMappingErr();
15837 return false;
15838 }
15839
15840 flatProp.mapping = prop; // keep a reference to the mapping
15841
15842 prop = flatProp; // the flattened (mapped) property is the one we want
15843
15844 break;
15845 }
15846 // direct mapping
15847
15848 case types.data:
15849 {
15850 // flatten the field (e.g. data.foo.bar)
15851 var _fields = prop.field.split('.');
15852
15853 var _fieldVal = _p.data;
15854
15855 for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
15856 var _field = _fields[_i4];
15857 _fieldVal = _fieldVal[_field];
15858 }
15859
15860 if (_fieldVal != null) {
15861 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15862 }
15863
15864 if (!flatProp) {
15865 // if we can't flatten the property, then don't apply and fall back on the existing style
15866 printMappingErr();
15867 return false;
15868 }
15869
15870 flatProp.mapping = prop; // keep a reference to the mapping
15871
15872 prop = flatProp; // the flattened (mapped) property is the one we want
15873
15874 break;
15875 }
15876
15877 case types.fn:
15878 {
15879 var fn = prop.value;
15880 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15881
15882 prop.prevFnValue = fnRetVal;
15883
15884 if (fnRetVal == null) {
15885 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15886 return false;
15887 }
15888
15889 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15890
15891 if (!flatProp) {
15892 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15893 return false;
15894 }
15895
15896 flatProp.mapping = copy(prop); // keep a reference to the mapping
15897
15898 prop = flatProp; // the flattened (mapped) property is the one we want
15899
15900 break;
15901 }
15902
15903 case undefined:
15904 break;
15905 // just set the property
15906
15907 default:
15908 return false;
15909 // not a valid mapping
15910 } // if the property is a bypass property, then link the resultant property to the original one
15911
15912
15913 if (propIsBypass) {
15914 if (origPropIsBypass) {
15915 // then this bypass overrides the existing one
15916 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15917 } else {
15918 // then link the orig prop to the new bypass
15919 prop.bypassed = origProp;
15920 }
15921
15922 style[prop.name] = prop; // and set
15923 } else {
15924 // prop is not bypass
15925 if (origPropIsBypass) {
15926 // then keep the orig prop (since it's a bypass) and link to the new prop
15927 origProp.bypassed = prop;
15928 } else {
15929 // then just replace the old prop with the new one
15930 style[prop.name] = prop;
15931 }
15932 }
15933
15934 checkTriggers();
15935 return true;
15936 };
15937
15938 styfn.cleanElements = function (eles, keepBypasses) {
15939 for (var i = 0; i < eles.length; i++) {
15940 var ele = eles[i];
15941 this.clearStyleHints(ele);
15942 ele.dirtyCompoundBoundsCache();
15943 ele.dirtyBoundingBoxCache();
15944
15945 if (!keepBypasses) {
15946 ele._private.style = {};
15947 } else {
15948 var style = ele._private.style;
15949 var propNames = Object.keys(style);
15950
15951 for (var j = 0; j < propNames.length; j++) {
15952 var propName = propNames[j];
15953 var eleProp = style[propName];
15954
15955 if (eleProp != null) {
15956 if (eleProp.bypass) {
15957 eleProp.bypassed = null;
15958 } else {
15959 style[propName] = null;
15960 }
15961 }
15962 }
15963 }
15964 }
15965 }; // updates the visual style for all elements (useful for manual style modification after init)
15966
15967
15968 styfn.update = function () {
15969 var cy = this._private.cy;
15970 var eles = cy.mutableElements();
15971 eles.updateStyle();
15972 }; // diffProps : { name => { prev, next } }
15973
15974
15975 styfn.updateTransitions = function (ele, diffProps) {
15976 var self = this;
15977 var _p = ele._private;
15978 var props = ele.pstyle('transition-property').value;
15979 var duration = ele.pstyle('transition-duration').pfValue;
15980 var delay = ele.pstyle('transition-delay').pfValue;
15981
15982 if (props.length > 0 && duration > 0) {
15983 var style = {}; // build up the style to animate towards
15984
15985 var anyPrev = false;
15986
15987 for (var i = 0; i < props.length; i++) {
15988 var prop = props[i];
15989 var styProp = ele.pstyle(prop);
15990 var diffProp = diffProps[prop];
15991
15992 if (!diffProp) {
15993 continue;
15994 }
15995
15996 var prevProp = diffProp.prev;
15997 var fromProp = prevProp;
15998 var toProp = diffProp.next != null ? diffProp.next : styProp;
15999 var diff = false;
16000 var initVal = void 0;
16001 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
16002
16003 if (!fromProp) {
16004 continue;
16005 } // consider px values
16006
16007
16008 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
16009 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
16010
16011 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
16012 } else if (number(fromProp.value) && number(toProp.value)) {
16013 diff = toProp.value - fromProp.value; // nonzero is truthy
16014
16015 initVal = fromProp.value + initDt * diff; // consider colour values
16016 } else if (array(fromProp.value) && array(toProp.value)) {
16017 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
16018 initVal = fromProp.strValue;
16019 } // the previous value is good for an animation only if it's different
16020
16021
16022 if (diff) {
16023 style[prop] = toProp.strValue; // to val
16024
16025 this.applyBypass(ele, prop, initVal); // from val
16026
16027 anyPrev = true;
16028 }
16029 } // end if props allow ani
16030 // can't transition if there's nothing previous to transition from
16031
16032
16033 if (!anyPrev) {
16034 return;
16035 }
16036
16037 _p.transitioning = true;
16038 new Promise$1(function (resolve) {
16039 if (delay > 0) {
16040 ele.delayAnimation(delay).play().promise().then(resolve);
16041 } else {
16042 resolve();
16043 }
16044 }).then(function () {
16045 return ele.animation({
16046 style: style,
16047 duration: duration,
16048 easing: ele.pstyle('transition-timing-function').value,
16049 queue: false
16050 }).play().promise();
16051 }).then(function () {
16052 // if( !isBypass ){
16053 self.removeBypasses(ele, props);
16054 ele.emitAndNotify('style'); // }
16055
16056 _p.transitioning = false;
16057 });
16058 } else if (_p.transitioning) {
16059 this.removeBypasses(ele, props);
16060 ele.emitAndNotify('style');
16061 _p.transitioning = false;
16062 }
16063 };
16064
16065 styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
16066 var prop = this.properties[name];
16067 var triggerCheck = getTrigger(prop);
16068
16069 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
16070 onTrigger(prop);
16071 }
16072 };
16073
16074 styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
16075 var _this = this;
16076
16077 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
16078 return prop.triggersZOrder;
16079 }, function () {
16080 _this._private.cy.notify('zorder', ele);
16081 });
16082 };
16083
16084 styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
16085 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
16086 return prop.triggersBounds;
16087 }, function (prop) {
16088 ele.dirtyCompoundBoundsCache();
16089 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
16090 // then dirty the pll edge bb cache as well
16091
16092 if ( // only for beziers -- so performance of other edges isn't affected
16093 (ele.pstyle('curve-style').value === 'bezier' // already a bezier
16094 // was just now changed to or from a bezier:
16095 || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
16096 ele.parallelEdges().forEach(function (pllEdge) {
16097 if (pllEdge.isBundledBezier()) {
16098 pllEdge.dirtyBoundingBoxCache();
16099 }
16100 });
16101 }
16102 });
16103 };
16104
16105 styfn.checkTriggers = function (ele, name, fromValue, toValue) {
16106 ele.dirtyStyleCache();
16107 this.checkZOrderTrigger(ele, name, fromValue, toValue);
16108 this.checkBoundsTrigger(ele, name, fromValue, toValue);
16109 };
16110
16111 var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
16112 // returns true iff application was successful for at least 1 specified property
16113
16114 styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
16115 var self = this;
16116 var props = [];
16117 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
16118
16119 if (name === '*' || name === '**') {
16120 // apply to all property names
16121 if (value !== undefined) {
16122 for (var i = 0; i < self.properties.length; i++) {
16123 var prop = self.properties[i];
16124 var _name = prop.name;
16125 var parsedProp = this.parse(_name, value, true);
16126
16127 if (parsedProp) {
16128 props.push(parsedProp);
16129 }
16130 }
16131 }
16132 } else if (string(name)) {
16133 // then parse the single property
16134 var _parsedProp = this.parse(name, value, true);
16135
16136 if (_parsedProp) {
16137 props.push(_parsedProp);
16138 }
16139 } else if (plainObject(name)) {
16140 // then parse each property
16141 var specifiedProps = name;
16142 updateTransitions = value;
16143 var names = Object.keys(specifiedProps);
16144
16145 for (var _i = 0; _i < names.length; _i++) {
16146 var _name2 = names[_i];
16147 var _value = specifiedProps[_name2];
16148
16149 if (_value === undefined) {
16150 // try camel case name too
16151 _value = specifiedProps[dash2camel(_name2)];
16152 }
16153
16154 if (_value !== undefined) {
16155 var _parsedProp2 = this.parse(_name2, _value, true);
16156
16157 if (_parsedProp2) {
16158 props.push(_parsedProp2);
16159 }
16160 }
16161 }
16162 } else {
16163 // can't do anything without well defined properties
16164 return false;
16165 } // we've failed if there are no valid properties
16166
16167
16168 if (props.length === 0) {
16169 return false;
16170 } // now, apply the bypass properties on the elements
16171
16172
16173 var ret = false; // return true if at least one succesful bypass applied
16174
16175 for (var _i2 = 0; _i2 < eles.length; _i2++) {
16176 // for each ele
16177 var ele = eles[_i2];
16178 var diffProps = {};
16179 var diffProp = void 0;
16180
16181 for (var j = 0; j < props.length; j++) {
16182 // for each prop
16183 var _prop = props[j];
16184
16185 if (updateTransitions) {
16186 var prevProp = ele.pstyle(_prop.name);
16187 diffProp = diffProps[_prop.name] = {
16188 prev: prevProp
16189 };
16190 }
16191
16192 ret = this.applyParsedProperty(ele, _prop) || ret;
16193
16194 if (updateTransitions) {
16195 diffProp.next = ele.pstyle(_prop.name);
16196 }
16197 } // for props
16198
16199
16200 if (ret) {
16201 this.updateStyleHints(ele);
16202 }
16203
16204 if (updateTransitions) {
16205 this.updateTransitions(ele, diffProps, isBypass);
16206 }
16207 } // for eles
16208
16209
16210 return ret;
16211 }; // only useful in specific cases like animation
16212
16213
16214 styfn$1.overrideBypass = function (eles, name, value) {
16215 name = camel2dash(name);
16216
16217 for (var i = 0; i < eles.length; i++) {
16218 var ele = eles[i];
16219 var prop = ele._private.style[name];
16220 var type = this.properties[name].type;
16221 var isColor = type.color;
16222 var isMulti = type.mutiple;
16223 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
16224
16225 if (!prop || !prop.bypass) {
16226 // need a bypass if one doesn't exist
16227 this.applyBypass(ele, name, value);
16228 } else {
16229 prop.value = value;
16230
16231 if (prop.pfValue != null) {
16232 prop.pfValue = value;
16233 }
16234
16235 if (isColor) {
16236 prop.strValue = 'rgb(' + value.join(',') + ')';
16237 } else if (isMulti) {
16238 prop.strValue = value.join(' ');
16239 } else {
16240 prop.strValue = '' + value;
16241 }
16242
16243 this.updateStyleHints(ele);
16244 }
16245
16246 this.checkTriggers(ele, name, oldValue, value);
16247 }
16248 };
16249
16250 styfn$1.removeAllBypasses = function (eles, updateTransitions) {
16251 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
16252 };
16253
16254 styfn$1.removeBypasses = function (eles, props, updateTransitions) {
16255 var isBypass = true;
16256
16257 for (var j = 0; j < eles.length; j++) {
16258 var ele = eles[j];
16259 var diffProps = {};
16260
16261 for (var i = 0; i < props.length; i++) {
16262 var name = props[i];
16263 var prop = this.properties[name];
16264 var prevProp = ele.pstyle(prop.name);
16265
16266 if (!prevProp || !prevProp.bypass) {
16267 // if a bypass doesn't exist for the prop, nothing needs to be removed
16268 continue;
16269 }
16270
16271 var value = ''; // empty => remove bypass
16272
16273 var parsedProp = this.parse(name, value, true);
16274 var diffProp = diffProps[prop.name] = {
16275 prev: prevProp
16276 };
16277 this.applyParsedProperty(ele, parsedProp);
16278 diffProp.next = ele.pstyle(prop.name);
16279 } // for props
16280
16281
16282 this.updateStyleHints(ele);
16283
16284 if (updateTransitions) {
16285 this.updateTransitions(ele, diffProps, isBypass);
16286 }
16287 } // for eles
16288
16289 };
16290
16291 var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
16292
16293 styfn$2.getEmSizeInPixels = function () {
16294 var px = this.containerCss('font-size');
16295
16296 if (px != null) {
16297 return parseFloat(px);
16298 } else {
16299 return 1; // for headless
16300 }
16301 }; // gets css property from the core container
16302
16303
16304 styfn$2.containerCss = function (propName) {
16305 var cy = this._private.cy;
16306 var domElement = cy.container();
16307
16308 if (window$1 && domElement && window$1.getComputedStyle) {
16309 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
16310 }
16311 };
16312
16313 var styfn$3 = {}; // gets the rendered style for an element
16314
16315 styfn$3.getRenderedStyle = function (ele, prop) {
16316 if (prop) {
16317 return this.getStylePropertyValue(ele, prop, true);
16318 } else {
16319 return this.getRawStyle(ele, true);
16320 }
16321 }; // gets the raw style for an element
16322
16323
16324 styfn$3.getRawStyle = function (ele, isRenderedVal) {
16325 var self = this;
16326 ele = ele[0]; // insure it's an element
16327
16328 if (ele) {
16329 var rstyle = {};
16330
16331 for (var i = 0; i < self.properties.length; i++) {
16332 var prop = self.properties[i];
16333 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
16334
16335 if (val != null) {
16336 rstyle[prop.name] = val;
16337 rstyle[dash2camel(prop.name)] = val;
16338 }
16339 }
16340
16341 return rstyle;
16342 }
16343 };
16344
16345 styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
16346 var pstyle = ele.pstyle(property)[subproperty][index];
16347 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
16348 };
16349
16350 styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
16351 var self = this;
16352 ele = ele[0]; // insure it's an element
16353
16354 if (ele) {
16355 var prop = self.properties[propName];
16356
16357 if (prop.alias) {
16358 prop = prop.pointsTo;
16359 }
16360
16361 var type = prop.type;
16362 var styleProp = ele.pstyle(prop.name);
16363
16364 if (styleProp) {
16365 var value = styleProp.value,
16366 units = styleProp.units,
16367 strValue = styleProp.strValue;
16368
16369 if (isRenderedVal && type.number && value != null && number(value)) {
16370 var zoom = ele.cy().zoom();
16371
16372 var getRenderedValue = function getRenderedValue(val) {
16373 return val * zoom;
16374 };
16375
16376 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
16377 return getRenderedValue(val) + units;
16378 };
16379
16380 var isArrayValue = array(value);
16381 var haveUnits = isArrayValue ? units.every(function (u) {
16382 return u != null;
16383 }) : units != null;
16384
16385 if (haveUnits) {
16386 if (isArrayValue) {
16387 return value.map(function (v, i) {
16388 return getValueStringWithUnits(v, units[i]);
16389 }).join(' ');
16390 } else {
16391 return getValueStringWithUnits(value, units);
16392 }
16393 } else {
16394 if (isArrayValue) {
16395 return value.map(function (v) {
16396 return string(v) ? v : '' + getRenderedValue(v);
16397 }).join(' ');
16398 } else {
16399 return '' + getRenderedValue(value);
16400 }
16401 }
16402 } else if (strValue != null) {
16403 return strValue;
16404 }
16405 }
16406
16407 return null;
16408 }
16409 };
16410
16411 styfn$3.getAnimationStartStyle = function (ele, aniProps) {
16412 var rstyle = {};
16413
16414 for (var i = 0; i < aniProps.length; i++) {
16415 var aniProp = aniProps[i];
16416 var name = aniProp.name;
16417 var styleProp = ele.pstyle(name);
16418
16419 if (styleProp !== undefined) {
16420 // then make a prop of it
16421 if (plainObject(styleProp)) {
16422 styleProp = this.parse(name, styleProp.strValue);
16423 } else {
16424 styleProp = this.parse(name, styleProp);
16425 }
16426 }
16427
16428 if (styleProp) {
16429 rstyle[name] = styleProp;
16430 }
16431 }
16432
16433 return rstyle;
16434 };
16435
16436 styfn$3.getPropsList = function (propsObj) {
16437 var self = this;
16438 var rstyle = [];
16439 var style = propsObj;
16440 var props = self.properties;
16441
16442 if (style) {
16443 var names = Object.keys(style);
16444
16445 for (var i = 0; i < names.length; i++) {
16446 var name = names[i];
16447 var val = style[name];
16448 var prop = props[name] || props[camel2dash(name)];
16449 var styleProp = this.parse(prop.name, val);
16450
16451 if (styleProp) {
16452 rstyle.push(styleProp);
16453 }
16454 }
16455 }
16456
16457 return rstyle;
16458 };
16459
16460 styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
16461 var hash = seed;
16462 var name, val, strVal, chVal;
16463 var i, j;
16464
16465 for (i = 0; i < propNames.length; i++) {
16466 name = propNames[i];
16467 val = ele.pstyle(name, false);
16468
16469 if (val == null) {
16470 continue;
16471 } else if (val.pfValue != null) {
16472 hash = hashInt(chVal, hash);
16473 } else {
16474 strVal = val.strValue;
16475
16476 for (j = 0; j < strVal.length; j++) {
16477 chVal = strVal.charCodeAt(j);
16478 hash = hashInt(chVal, hash);
16479 }
16480 }
16481 }
16482
16483 return hash;
16484 };
16485
16486 styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
16487
16488 var styfn$4 = {};
16489
16490 styfn$4.appendFromJson = function (json) {
16491 var style = this;
16492
16493 for (var i = 0; i < json.length; i++) {
16494 var context = json[i];
16495 var selector = context.selector;
16496 var props = context.style || context.css;
16497 var names = Object.keys(props);
16498 style.selector(selector); // apply selector
16499
16500 for (var j = 0; j < names.length; j++) {
16501 var name = names[j];
16502 var value = props[name];
16503 style.css(name, value); // apply property
16504 }
16505 }
16506
16507 return style;
16508 }; // accessible cy.style() function
16509
16510
16511 styfn$4.fromJson = function (json) {
16512 var style = this;
16513 style.resetToDefault();
16514 style.appendFromJson(json);
16515 return style;
16516 }; // get json from cy.style() api
16517
16518
16519 styfn$4.json = function () {
16520 var json = [];
16521
16522 for (var i = this.defaultLength; i < this.length; i++) {
16523 var cxt = this[i];
16524 var selector = cxt.selector;
16525 var props = cxt.properties;
16526 var css = {};
16527
16528 for (var j = 0; j < props.length; j++) {
16529 var prop = props[j];
16530 css[prop.name] = prop.strValue;
16531 }
16532
16533 json.push({
16534 selector: !selector ? 'core' : selector.toString(),
16535 style: css
16536 });
16537 }
16538
16539 return json;
16540 };
16541
16542 var styfn$5 = {};
16543
16544 styfn$5.appendFromString = function (string) {
16545 var self = this;
16546 var style = this;
16547 var remaining = '' + string;
16548 var selAndBlockStr;
16549 var blockRem;
16550 var propAndValStr; // remove comments from the style string
16551
16552 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
16553
16554 function removeSelAndBlockFromRemaining() {
16555 // remove the parsed selector and block from the remaining text to parse
16556 if (remaining.length > selAndBlockStr.length) {
16557 remaining = remaining.substr(selAndBlockStr.length);
16558 } else {
16559 remaining = '';
16560 }
16561 }
16562
16563 function removePropAndValFromRem() {
16564 // remove the parsed property and value from the remaining block text to parse
16565 if (blockRem.length > propAndValStr.length) {
16566 blockRem = blockRem.substr(propAndValStr.length);
16567 } else {
16568 blockRem = '';
16569 }
16570 }
16571
16572 for (;;) {
16573 var nothingLeftToParse = remaining.match(/^\s*$/);
16574
16575 if (nothingLeftToParse) {
16576 break;
16577 }
16578
16579 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
16580
16581 if (!selAndBlock) {
16582 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
16583 break;
16584 }
16585
16586 selAndBlockStr = selAndBlock[0]; // parse the selector
16587
16588 var selectorStr = selAndBlock[1];
16589
16590 if (selectorStr !== 'core') {
16591 var selector = new Selector(selectorStr);
16592
16593 if (selector.invalid) {
16594 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
16595
16596 removeSelAndBlockFromRemaining();
16597 continue;
16598 }
16599 } // parse the block of properties and values
16600
16601
16602 var blockStr = selAndBlock[2];
16603 var invalidBlock = false;
16604 blockRem = blockStr;
16605 var props = [];
16606
16607 for (;;) {
16608 var _nothingLeftToParse = blockRem.match(/^\s*$/);
16609
16610 if (_nothingLeftToParse) {
16611 break;
16612 }
16613
16614 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
16615
16616 if (!propAndVal) {
16617 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
16618 invalidBlock = true;
16619 break;
16620 }
16621
16622 propAndValStr = propAndVal[0];
16623 var propStr = propAndVal[1];
16624 var valStr = propAndVal[2];
16625 var prop = self.properties[propStr];
16626
16627 if (!prop) {
16628 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
16629
16630 removePropAndValFromRem();
16631 continue;
16632 }
16633
16634 var parsedProp = style.parse(propStr, valStr);
16635
16636 if (!parsedProp) {
16637 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
16638
16639 removePropAndValFromRem();
16640 continue;
16641 }
16642
16643 props.push({
16644 name: propStr,
16645 val: valStr
16646 });
16647 removePropAndValFromRem();
16648 }
16649
16650 if (invalidBlock) {
16651 removeSelAndBlockFromRemaining();
16652 break;
16653 } // put the parsed block in the style
16654
16655
16656 style.selector(selectorStr);
16657
16658 for (var i = 0; i < props.length; i++) {
16659 var _prop = props[i];
16660 style.css(_prop.name, _prop.val);
16661 }
16662
16663 removeSelAndBlockFromRemaining();
16664 }
16665
16666 return style;
16667 };
16668
16669 styfn$5.fromString = function (string) {
16670 var style = this;
16671 style.resetToDefault();
16672 style.appendFromString(string);
16673 return style;
16674 };
16675
16676 var styfn$6 = {};
16677
16678 (function () {
16679 var number = number$1;
16680 var rgba = rgbaNoBackRefs;
16681 var hsla = hslaNoBackRefs;
16682 var hex3$1 = hex3;
16683 var hex6$1 = hex6;
16684
16685 var data = function data(prefix) {
16686 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16687 };
16688
16689 var mapData = function mapData(prefix) {
16690 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16691 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16692 };
16693
16694 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
16695
16696 styfn$6.types = {
16697 time: {
16698 number: true,
16699 min: 0,
16700 units: 's|ms',
16701 implicitUnits: 'ms'
16702 },
16703 percent: {
16704 number: true,
16705 min: 0,
16706 max: 100,
16707 units: '%',
16708 implicitUnits: '%'
16709 },
16710 percentages: {
16711 number: true,
16712 min: 0,
16713 max: 100,
16714 units: '%',
16715 implicitUnits: '%',
16716 multiple: true
16717 },
16718 zeroOneNumber: {
16719 number: true,
16720 min: 0,
16721 max: 1,
16722 unitless: true
16723 },
16724 zeroOneNumbers: {
16725 number: true,
16726 min: 0,
16727 max: 1,
16728 unitless: true,
16729 multiple: true
16730 },
16731 nOneOneNumber: {
16732 number: true,
16733 min: -1,
16734 max: 1,
16735 unitless: true
16736 },
16737 nonNegativeInt: {
16738 number: true,
16739 min: 0,
16740 integer: true,
16741 unitless: true
16742 },
16743 position: {
16744 enums: ['parent', 'origin']
16745 },
16746 nodeSize: {
16747 number: true,
16748 min: 0,
16749 enums: ['label']
16750 },
16751 number: {
16752 number: true,
16753 unitless: true
16754 },
16755 numbers: {
16756 number: true,
16757 unitless: true,
16758 multiple: true
16759 },
16760 positiveNumber: {
16761 number: true,
16762 unitless: true,
16763 min: 0,
16764 strictMin: true
16765 },
16766 size: {
16767 number: true,
16768 min: 0
16769 },
16770 bidirectionalSize: {
16771 number: true
16772 },
16773 // allows negative
16774 bidirectionalSizeMaybePercent: {
16775 number: true,
16776 allowPercent: true
16777 },
16778 // allows negative
16779 bidirectionalSizes: {
16780 number: true,
16781 multiple: true
16782 },
16783 // allows negative
16784 sizeMaybePercent: {
16785 number: true,
16786 min: 0,
16787 allowPercent: true
16788 },
16789 axisDirection: {
16790 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16791 },
16792 paddingRelativeTo: {
16793 enums: ['width', 'height', 'average', 'min', 'max']
16794 },
16795 bgWH: {
16796 number: true,
16797 min: 0,
16798 allowPercent: true,
16799 enums: ['auto'],
16800 multiple: true
16801 },
16802 bgPos: {
16803 number: true,
16804 allowPercent: true,
16805 multiple: true
16806 },
16807 bgRelativeTo: {
16808 enums: ['inner', 'include-padding'],
16809 multiple: true
16810 },
16811 bgRepeat: {
16812 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16813 multiple: true
16814 },
16815 bgFit: {
16816 enums: ['none', 'contain', 'cover'],
16817 multiple: true
16818 },
16819 bgCrossOrigin: {
16820 enums: ['anonymous', 'use-credentials'],
16821 multiple: true
16822 },
16823 bgClip: {
16824 enums: ['none', 'node'],
16825 multiple: true
16826 },
16827 color: {
16828 color: true
16829 },
16830 colors: {
16831 color: true,
16832 multiple: true
16833 },
16834 fill: {
16835 enums: ['solid', 'linear-gradient', 'radial-gradient']
16836 },
16837 bool: {
16838 enums: ['yes', 'no']
16839 },
16840 lineStyle: {
16841 enums: ['solid', 'dotted', 'dashed']
16842 },
16843 lineCap: {
16844 enums: ['butt', 'round', 'square']
16845 },
16846 borderStyle: {
16847 enums: ['solid', 'dotted', 'dashed', 'double']
16848 },
16849 curveStyle: {
16850 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16851 },
16852 fontFamily: {
16853 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16854 },
16855 fontStyle: {
16856 enums: ['italic', 'normal', 'oblique']
16857 },
16858 fontWeight: {
16859 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16860 },
16861 textDecoration: {
16862 enums: ['none', 'underline', 'overline', 'line-through']
16863 },
16864 textTransform: {
16865 enums: ['none', 'uppercase', 'lowercase']
16866 },
16867 textWrap: {
16868 enums: ['none', 'wrap', 'ellipsis']
16869 },
16870 textOverflowWrap: {
16871 enums: ['whitespace', 'anywhere']
16872 },
16873 textBackgroundShape: {
16874 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16875 },
16876 nodeShape: {
16877 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']
16878 },
16879 compoundIncludeLabels: {
16880 enums: ['include', 'exclude']
16881 },
16882 arrowShape: {
16883 enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16884 },
16885 arrowFill: {
16886 enums: ['filled', 'hollow']
16887 },
16888 display: {
16889 enums: ['element', 'none']
16890 },
16891 visibility: {
16892 enums: ['hidden', 'visible']
16893 },
16894 zCompoundDepth: {
16895 enums: ['bottom', 'orphan', 'auto', 'top']
16896 },
16897 zIndexCompare: {
16898 enums: ['auto', 'manual']
16899 },
16900 valign: {
16901 enums: ['top', 'center', 'bottom']
16902 },
16903 halign: {
16904 enums: ['left', 'center', 'right']
16905 },
16906 justification: {
16907 enums: ['left', 'center', 'right', 'auto']
16908 },
16909 text: {
16910 string: true
16911 },
16912 data: {
16913 mapping: true,
16914 regex: data('data')
16915 },
16916 layoutData: {
16917 mapping: true,
16918 regex: data('layoutData')
16919 },
16920 scratch: {
16921 mapping: true,
16922 regex: data('scratch')
16923 },
16924 mapData: {
16925 mapping: true,
16926 regex: mapData('mapData')
16927 },
16928 mapLayoutData: {
16929 mapping: true,
16930 regex: mapData('mapLayoutData')
16931 },
16932 mapScratch: {
16933 mapping: true,
16934 regex: mapData('mapScratch')
16935 },
16936 fn: {
16937 mapping: true,
16938 fn: true
16939 },
16940 url: {
16941 regexes: urlRegexes,
16942 singleRegexMatchValue: true
16943 },
16944 urls: {
16945 regexes: urlRegexes,
16946 singleRegexMatchValue: true,
16947 multiple: true
16948 },
16949 propList: {
16950 propList: true
16951 },
16952 angle: {
16953 number: true,
16954 units: 'deg|rad',
16955 implicitUnits: 'rad'
16956 },
16957 textRotation: {
16958 number: true,
16959 units: 'deg|rad',
16960 implicitUnits: 'rad',
16961 enums: ['none', 'autorotate']
16962 },
16963 polygonPointList: {
16964 number: true,
16965 multiple: true,
16966 evenMultiple: true,
16967 min: -1,
16968 max: 1,
16969 unitless: true
16970 },
16971 edgeDistances: {
16972 enums: ['intersection', 'node-position']
16973 },
16974 edgeEndpoint: {
16975 number: true,
16976 multiple: true,
16977 units: '%|px|em|deg|rad',
16978 implicitUnits: 'px',
16979 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16980 singleEnum: true,
16981 validate: function validate(valArr, unitsArr) {
16982 switch (valArr.length) {
16983 case 2:
16984 // can be % or px only
16985 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16986
16987 case 1:
16988 // can be enum, deg, or rad only
16989 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16990
16991 default:
16992 return false;
16993 }
16994 }
16995 },
16996 easing: {
16997 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16998 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']
16999 },
17000 gradientDirection: {
17001 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']
17002 },
17003 boundsExpansion: {
17004 number: true,
17005 multiple: true,
17006 min: 0,
17007 validate: function validate(valArr) {
17008 var length = valArr.length;
17009 return length === 1 || length === 2 || length === 4;
17010 }
17011 }
17012 };
17013 var diff = {
17014 zeroNonZero: function zeroNonZero(val1, val2) {
17015 if ((val1 == null || val2 == null) && val1 !== val2) {
17016 return true; // null cases could represent any value
17017 }
17018
17019 if (val1 == 0 && val2 != 0) {
17020 return true;
17021 } else if (val1 != 0 && val2 == 0) {
17022 return true;
17023 } else {
17024 return false;
17025 }
17026 },
17027 any: function any(val1, val2) {
17028 return val1 != val2;
17029 }
17030 }; // define visual style properties
17031 //
17032 // - n.b. adding a new group of props may require updates to updateStyleHints()
17033 // - adding new props to an existing group gets handled automatically
17034
17035 var t = styfn$6.types;
17036 var mainLabel = [{
17037 name: 'label',
17038 type: t.text,
17039 triggersBounds: diff.any
17040 }, {
17041 name: 'text-rotation',
17042 type: t.textRotation,
17043 triggersBounds: diff.any
17044 }, {
17045 name: 'text-margin-x',
17046 type: t.bidirectionalSize,
17047 triggersBounds: diff.any
17048 }, {
17049 name: 'text-margin-y',
17050 type: t.bidirectionalSize,
17051 triggersBounds: diff.any
17052 }];
17053 var sourceLabel = [{
17054 name: 'source-label',
17055 type: t.text,
17056 triggersBounds: diff.any
17057 }, {
17058 name: 'source-text-rotation',
17059 type: t.textRotation,
17060 triggersBounds: diff.any
17061 }, {
17062 name: 'source-text-margin-x',
17063 type: t.bidirectionalSize,
17064 triggersBounds: diff.any
17065 }, {
17066 name: 'source-text-margin-y',
17067 type: t.bidirectionalSize,
17068 triggersBounds: diff.any
17069 }, {
17070 name: 'source-text-offset',
17071 type: t.size,
17072 triggersBounds: diff.any
17073 }];
17074 var targetLabel = [{
17075 name: 'target-label',
17076 type: t.text,
17077 triggersBounds: diff.any
17078 }, {
17079 name: 'target-text-rotation',
17080 type: t.textRotation,
17081 triggersBounds: diff.any
17082 }, {
17083 name: 'target-text-margin-x',
17084 type: t.bidirectionalSize,
17085 triggersBounds: diff.any
17086 }, {
17087 name: 'target-text-margin-y',
17088 type: t.bidirectionalSize,
17089 triggersBounds: diff.any
17090 }, {
17091 name: 'target-text-offset',
17092 type: t.size,
17093 triggersBounds: diff.any
17094 }];
17095 var labelDimensions = [{
17096 name: 'font-family',
17097 type: t.fontFamily,
17098 triggersBounds: diff.any
17099 }, {
17100 name: 'font-style',
17101 type: t.fontStyle,
17102 triggersBounds: diff.any
17103 }, {
17104 name: 'font-weight',
17105 type: t.fontWeight,
17106 triggersBounds: diff.any
17107 }, {
17108 name: 'font-size',
17109 type: t.size,
17110 triggersBounds: diff.any
17111 }, {
17112 name: 'text-transform',
17113 type: t.textTransform,
17114 triggersBounds: diff.any
17115 }, {
17116 name: 'text-wrap',
17117 type: t.textWrap,
17118 triggersBounds: diff.any
17119 }, {
17120 name: 'text-overflow-wrap',
17121 type: t.textOverflowWrap,
17122 triggersBounds: diff.any
17123 }, {
17124 name: 'text-max-width',
17125 type: t.size,
17126 triggersBounds: diff.any
17127 }, {
17128 name: 'text-outline-width',
17129 type: t.size,
17130 triggersBounds: diff.any
17131 }, {
17132 name: 'line-height',
17133 type: t.positiveNumber,
17134 triggersBounds: diff.any
17135 }];
17136 var commonLabel = [{
17137 name: 'text-valign',
17138 type: t.valign,
17139 triggersBounds: diff.any
17140 }, {
17141 name: 'text-halign',
17142 type: t.halign,
17143 triggersBounds: diff.any
17144 }, {
17145 name: 'color',
17146 type: t.color
17147 }, {
17148 name: 'text-outline-color',
17149 type: t.color
17150 }, {
17151 name: 'text-outline-opacity',
17152 type: t.zeroOneNumber
17153 }, {
17154 name: 'text-background-color',
17155 type: t.color
17156 }, {
17157 name: 'text-background-opacity',
17158 type: t.zeroOneNumber
17159 }, {
17160 name: 'text-background-padding',
17161 type: t.size,
17162 triggersBounds: diff.any
17163 }, {
17164 name: 'text-border-opacity',
17165 type: t.zeroOneNumber
17166 }, {
17167 name: 'text-border-color',
17168 type: t.color
17169 }, {
17170 name: 'text-border-width',
17171 type: t.size,
17172 triggersBounds: diff.any
17173 }, {
17174 name: 'text-border-style',
17175 type: t.borderStyle,
17176 triggersBounds: diff.any
17177 }, {
17178 name: 'text-background-shape',
17179 type: t.textBackgroundShape,
17180 triggersBounds: diff.any
17181 }, {
17182 name: 'text-justification',
17183 type: t.justification
17184 }];
17185 var behavior = [{
17186 name: 'events',
17187 type: t.bool
17188 }, {
17189 name: 'text-events',
17190 type: t.bool
17191 }];
17192 var visibility = [{
17193 name: 'display',
17194 type: t.display,
17195 triggersZOrder: diff.any,
17196 triggersBounds: diff.any,
17197 triggersBoundsOfParallelBeziers: true
17198 }, {
17199 name: 'visibility',
17200 type: t.visibility,
17201 triggersZOrder: diff.any
17202 }, {
17203 name: 'opacity',
17204 type: t.zeroOneNumber,
17205 triggersZOrder: diff.zeroNonZero
17206 }, {
17207 name: 'text-opacity',
17208 type: t.zeroOneNumber
17209 }, {
17210 name: 'min-zoomed-font-size',
17211 type: t.size
17212 }, {
17213 name: 'z-compound-depth',
17214 type: t.zCompoundDepth,
17215 triggersZOrder: diff.any
17216 }, {
17217 name: 'z-index-compare',
17218 type: t.zIndexCompare,
17219 triggersZOrder: diff.any
17220 }, {
17221 name: 'z-index',
17222 type: t.nonNegativeInt,
17223 triggersZOrder: diff.any
17224 }];
17225 var overlay = [{
17226 name: 'overlay-padding',
17227 type: t.size,
17228 triggersBounds: diff.any
17229 }, {
17230 name: 'overlay-color',
17231 type: t.color
17232 }, {
17233 name: 'overlay-opacity',
17234 type: t.zeroOneNumber,
17235 triggersBounds: diff.zeroNonZero
17236 }];
17237 var transition = [{
17238 name: 'transition-property',
17239 type: t.propList
17240 }, {
17241 name: 'transition-duration',
17242 type: t.time
17243 }, {
17244 name: 'transition-delay',
17245 type: t.time
17246 }, {
17247 name: 'transition-timing-function',
17248 type: t.easing
17249 }];
17250
17251 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
17252 if (parsedProp.value === 'label') {
17253 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
17254 } else {
17255 return parsedProp.pfValue;
17256 }
17257 };
17258
17259 var nodeBody = [{
17260 name: 'height',
17261 type: t.nodeSize,
17262 triggersBounds: diff.any,
17263 hashOverride: nodeSizeHashOverride
17264 }, {
17265 name: 'width',
17266 type: t.nodeSize,
17267 triggersBounds: diff.any,
17268 hashOverride: nodeSizeHashOverride
17269 }, {
17270 name: 'shape',
17271 type: t.nodeShape,
17272 triggersBounds: diff.any
17273 }, {
17274 name: 'shape-polygon-points',
17275 type: t.polygonPointList,
17276 triggersBounds: diff.any
17277 }, {
17278 name: 'background-color',
17279 type: t.color
17280 }, {
17281 name: 'background-fill',
17282 type: t.fill
17283 }, {
17284 name: 'background-opacity',
17285 type: t.zeroOneNumber
17286 }, {
17287 name: 'background-blacken',
17288 type: t.nOneOneNumber
17289 }, {
17290 name: 'background-gradient-stop-colors',
17291 type: t.colors
17292 }, {
17293 name: 'background-gradient-stop-positions',
17294 type: t.percentages
17295 }, {
17296 name: 'background-gradient-direction',
17297 type: t.gradientDirection
17298 }, {
17299 name: 'padding',
17300 type: t.sizeMaybePercent,
17301 triggersBounds: diff.any
17302 }, {
17303 name: 'padding-relative-to',
17304 type: t.paddingRelativeTo,
17305 triggersBounds: diff.any
17306 }, {
17307 name: 'bounds-expansion',
17308 type: t.boundsExpansion,
17309 triggersBounds: diff.any
17310 }];
17311 var nodeBorder = [{
17312 name: 'border-color',
17313 type: t.color
17314 }, {
17315 name: 'border-opacity',
17316 type: t.zeroOneNumber
17317 }, {
17318 name: 'border-width',
17319 type: t.size,
17320 triggersBounds: diff.any
17321 }, {
17322 name: 'border-style',
17323 type: t.borderStyle
17324 }];
17325 var backgroundImage = [{
17326 name: 'background-image',
17327 type: t.urls
17328 }, {
17329 name: 'background-image-crossorigin',
17330 type: t.bgCrossOrigin
17331 }, {
17332 name: 'background-image-opacity',
17333 type: t.zeroOneNumbers
17334 }, {
17335 name: 'background-position-x',
17336 type: t.bgPos
17337 }, {
17338 name: 'background-position-y',
17339 type: t.bgPos
17340 }, {
17341 name: 'background-width-relative-to',
17342 type: t.bgRelativeTo
17343 }, {
17344 name: 'background-height-relative-to',
17345 type: t.bgRelativeTo
17346 }, {
17347 name: 'background-repeat',
17348 type: t.bgRepeat
17349 }, {
17350 name: 'background-fit',
17351 type: t.bgFit
17352 }, {
17353 name: 'background-clip',
17354 type: t.bgClip
17355 }, {
17356 name: 'background-width',
17357 type: t.bgWH
17358 }, {
17359 name: 'background-height',
17360 type: t.bgWH
17361 }, {
17362 name: 'background-offset-x',
17363 type: t.bgPos
17364 }, {
17365 name: 'background-offset-y',
17366 type: t.bgPos
17367 }];
17368 var compound = [{
17369 name: 'position',
17370 type: t.position,
17371 triggersBounds: diff.any
17372 }, {
17373 name: 'compound-sizing-wrt-labels',
17374 type: t.compoundIncludeLabels,
17375 triggersBounds: diff.any
17376 }, {
17377 name: 'min-width',
17378 type: t.size,
17379 triggersBounds: diff.any
17380 }, {
17381 name: 'min-width-bias-left',
17382 type: t.sizeMaybePercent,
17383 triggersBounds: diff.any
17384 }, {
17385 name: 'min-width-bias-right',
17386 type: t.sizeMaybePercent,
17387 triggersBounds: diff.any
17388 }, {
17389 name: 'min-height',
17390 type: t.size,
17391 triggersBounds: diff.any
17392 }, {
17393 name: 'min-height-bias-top',
17394 type: t.sizeMaybePercent,
17395 triggersBounds: diff.any
17396 }, {
17397 name: 'min-height-bias-bottom',
17398 type: t.sizeMaybePercent,
17399 triggersBounds: diff.any
17400 }];
17401 var edgeLine = [{
17402 name: 'line-style',
17403 type: t.lineStyle
17404 }, {
17405 name: 'line-color',
17406 type: t.color
17407 }, {
17408 name: 'line-fill',
17409 type: t.fill
17410 }, {
17411 name: 'line-cap',
17412 type: t.lineCap
17413 }, {
17414 name: 'line-dash-pattern',
17415 type: t.numbers
17416 }, {
17417 name: 'line-dash-offset',
17418 type: t.number
17419 }, {
17420 name: 'line-gradient-stop-colors',
17421 type: t.colors
17422 }, {
17423 name: 'line-gradient-stop-positions',
17424 type: t.percentages
17425 }, {
17426 name: 'curve-style',
17427 type: t.curveStyle,
17428 triggersBounds: diff.any,
17429 triggersBoundsOfParallelBeziers: true
17430 }, {
17431 name: 'haystack-radius',
17432 type: t.zeroOneNumber,
17433 triggersBounds: diff.any
17434 }, {
17435 name: 'source-endpoint',
17436 type: t.edgeEndpoint,
17437 triggersBounds: diff.any
17438 }, {
17439 name: 'target-endpoint',
17440 type: t.edgeEndpoint,
17441 triggersBounds: diff.any
17442 }, {
17443 name: 'control-point-step-size',
17444 type: t.size,
17445 triggersBounds: diff.any
17446 }, {
17447 name: 'control-point-distances',
17448 type: t.bidirectionalSizes,
17449 triggersBounds: diff.any
17450 }, {
17451 name: 'control-point-weights',
17452 type: t.numbers,
17453 triggersBounds: diff.any
17454 }, {
17455 name: 'segment-distances',
17456 type: t.bidirectionalSizes,
17457 triggersBounds: diff.any
17458 }, {
17459 name: 'segment-weights',
17460 type: t.numbers,
17461 triggersBounds: diff.any
17462 }, {
17463 name: 'taxi-turn',
17464 type: t.bidirectionalSizeMaybePercent,
17465 triggersBounds: diff.any
17466 }, {
17467 name: 'taxi-turn-min-distance',
17468 type: t.size,
17469 triggersBounds: diff.any
17470 }, {
17471 name: 'taxi-direction',
17472 type: t.axisDirection,
17473 triggersBounds: diff.any
17474 }, {
17475 name: 'edge-distances',
17476 type: t.edgeDistances,
17477 triggersBounds: diff.any
17478 }, {
17479 name: 'arrow-scale',
17480 type: t.positiveNumber,
17481 triggersBounds: diff.any
17482 }, {
17483 name: 'loop-direction',
17484 type: t.angle,
17485 triggersBounds: diff.any
17486 }, {
17487 name: 'loop-sweep',
17488 type: t.angle,
17489 triggersBounds: diff.any
17490 }, {
17491 name: 'source-distance-from-node',
17492 type: t.size,
17493 triggersBounds: diff.any
17494 }, {
17495 name: 'target-distance-from-node',
17496 type: t.size,
17497 triggersBounds: diff.any
17498 }];
17499 var ghost = [{
17500 name: 'ghost',
17501 type: t.bool,
17502 triggersBounds: diff.any
17503 }, {
17504 name: 'ghost-offset-x',
17505 type: t.bidirectionalSize,
17506 triggersBounds: diff.any
17507 }, {
17508 name: 'ghost-offset-y',
17509 type: t.bidirectionalSize,
17510 triggersBounds: diff.any
17511 }, {
17512 name: 'ghost-opacity',
17513 type: t.zeroOneNumber
17514 }];
17515 var core = [{
17516 name: 'selection-box-color',
17517 type: t.color
17518 }, {
17519 name: 'selection-box-opacity',
17520 type: t.zeroOneNumber
17521 }, {
17522 name: 'selection-box-border-color',
17523 type: t.color
17524 }, {
17525 name: 'selection-box-border-width',
17526 type: t.size
17527 }, {
17528 name: 'active-bg-color',
17529 type: t.color
17530 }, {
17531 name: 'active-bg-opacity',
17532 type: t.zeroOneNumber
17533 }, {
17534 name: 'active-bg-size',
17535 type: t.size
17536 }, {
17537 name: 'outside-texture-bg-color',
17538 type: t.color
17539 }, {
17540 name: 'outside-texture-bg-opacity',
17541 type: t.zeroOneNumber
17542 }]; // pie backgrounds for nodes
17543
17544 var pie = [];
17545 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
17546
17547 pie.push({
17548 name: 'pie-size',
17549 type: t.sizeMaybePercent
17550 });
17551
17552 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17553 pie.push({
17554 name: 'pie-' + i + '-background-color',
17555 type: t.color
17556 });
17557 pie.push({
17558 name: 'pie-' + i + '-background-size',
17559 type: t.percent
17560 });
17561 pie.push({
17562 name: 'pie-' + i + '-background-opacity',
17563 type: t.zeroOneNumber
17564 });
17565 } // edge arrows
17566
17567
17568 var edgeArrow = [];
17569 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
17570 [{
17571 name: 'arrow-shape',
17572 type: t.arrowShape,
17573 triggersBounds: diff.any
17574 }, {
17575 name: 'arrow-color',
17576 type: t.color
17577 }, {
17578 name: 'arrow-fill',
17579 type: t.arrowFill
17580 }].forEach(function (prop) {
17581 arrowPrefixes.forEach(function (prefix) {
17582 var name = prefix + '-' + prop.name;
17583 var type = prop.type,
17584 triggersBounds = prop.triggersBounds;
17585 edgeArrow.push({
17586 name: name,
17587 type: type,
17588 triggersBounds: triggersBounds
17589 });
17590 });
17591 }, {});
17592 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
17593 var propGroups = styfn$6.propertyGroups = {
17594 // common to all eles
17595 behavior: behavior,
17596 transition: transition,
17597 visibility: visibility,
17598 overlay: overlay,
17599 ghost: ghost,
17600 // labels
17601 commonLabel: commonLabel,
17602 labelDimensions: labelDimensions,
17603 mainLabel: mainLabel,
17604 sourceLabel: sourceLabel,
17605 targetLabel: targetLabel,
17606 // node props
17607 nodeBody: nodeBody,
17608 nodeBorder: nodeBorder,
17609 backgroundImage: backgroundImage,
17610 pie: pie,
17611 compound: compound,
17612 // edge props
17613 edgeLine: edgeLine,
17614 edgeArrow: edgeArrow,
17615 core: core
17616 };
17617 var propGroupNames = styfn$6.propertyGroupNames = {};
17618 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
17619 propGroupKeys.forEach(function (key) {
17620 propGroupNames[key] = propGroups[key].map(function (prop) {
17621 return prop.name;
17622 });
17623 propGroups[key].forEach(function (prop) {
17624 return prop.groupKey = key;
17625 });
17626 }); // define aliases
17627
17628 var aliases = styfn$6.aliases = [{
17629 name: 'content',
17630 pointsTo: 'label'
17631 }, {
17632 name: 'control-point-distance',
17633 pointsTo: 'control-point-distances'
17634 }, {
17635 name: 'control-point-weight',
17636 pointsTo: 'control-point-weights'
17637 }, {
17638 name: 'edge-text-rotation',
17639 pointsTo: 'text-rotation'
17640 }, {
17641 name: 'padding-left',
17642 pointsTo: 'padding'
17643 }, {
17644 name: 'padding-right',
17645 pointsTo: 'padding'
17646 }, {
17647 name: 'padding-top',
17648 pointsTo: 'padding'
17649 }, {
17650 name: 'padding-bottom',
17651 pointsTo: 'padding'
17652 }]; // list of property names
17653
17654 styfn$6.propertyNames = props.map(function (p) {
17655 return p.name;
17656 }); // allow access of properties by name ( e.g. style.properties.height )
17657
17658 for (var _i = 0; _i < props.length; _i++) {
17659 var prop = props[_i];
17660 props[prop.name] = prop; // allow lookup by name
17661 } // map aliases
17662
17663
17664 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17665 var alias = aliases[_i2];
17666 var pointsToProp = props[alias.pointsTo];
17667 var aliasProp = {
17668 name: alias.name,
17669 alias: true,
17670 pointsTo: pointsToProp
17671 }; // add alias prop for parsing
17672
17673 props.push(aliasProp);
17674 props[alias.name] = aliasProp; // allow lookup by name
17675 }
17676 })();
17677
17678 styfn$6.getDefaultProperty = function (name) {
17679 return this.getDefaultProperties()[name];
17680 };
17681
17682 styfn$6.getDefaultProperties = function () {
17683 var _p = this._private;
17684
17685 if (_p.defaultProperties != null) {
17686 return _p.defaultProperties;
17687 }
17688
17689 var rawProps = extend({
17690 // core props
17691 'selection-box-color': '#ddd',
17692 'selection-box-opacity': 0.65,
17693 'selection-box-border-color': '#aaa',
17694 'selection-box-border-width': 1,
17695 'active-bg-color': 'black',
17696 'active-bg-opacity': 0.15,
17697 'active-bg-size': 30,
17698 'outside-texture-bg-color': '#000',
17699 'outside-texture-bg-opacity': 0.125,
17700 // common node/edge props
17701 'events': 'yes',
17702 'text-events': 'no',
17703 'text-valign': 'top',
17704 'text-halign': 'center',
17705 'text-justification': 'auto',
17706 'line-height': 1,
17707 'color': '#000',
17708 'text-outline-color': '#000',
17709 'text-outline-width': 0,
17710 'text-outline-opacity': 1,
17711 'text-opacity': 1,
17712 'text-decoration': 'none',
17713 'text-transform': 'none',
17714 'text-wrap': 'none',
17715 'text-overflow-wrap': 'whitespace',
17716 'text-max-width': 9999,
17717 'text-background-color': '#000',
17718 'text-background-opacity': 0,
17719 'text-background-shape': 'rectangle',
17720 'text-background-padding': 0,
17721 'text-border-opacity': 0,
17722 'text-border-width': 0,
17723 'text-border-style': 'solid',
17724 'text-border-color': '#000',
17725 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17726 'font-style': 'normal',
17727 'font-weight': 'normal',
17728 'font-size': 16,
17729 'min-zoomed-font-size': 0,
17730 'text-rotation': 'none',
17731 'source-text-rotation': 'none',
17732 'target-text-rotation': 'none',
17733 'visibility': 'visible',
17734 'display': 'element',
17735 'opacity': 1,
17736 'z-compound-depth': 'auto',
17737 'z-index-compare': 'auto',
17738 'z-index': 0,
17739 'label': '',
17740 'text-margin-x': 0,
17741 'text-margin-y': 0,
17742 'source-label': '',
17743 'source-text-offset': 0,
17744 'source-text-margin-x': 0,
17745 'source-text-margin-y': 0,
17746 'target-label': '',
17747 'target-text-offset': 0,
17748 'target-text-margin-x': 0,
17749 'target-text-margin-y': 0,
17750 'overlay-opacity': 0,
17751 'overlay-color': '#000',
17752 'overlay-padding': 10,
17753 'transition-property': 'none',
17754 'transition-duration': 0,
17755 'transition-delay': 0,
17756 'transition-timing-function': 'linear',
17757 // node props
17758 'background-blacken': 0,
17759 'background-color': '#999',
17760 'background-fill': 'solid',
17761 'background-opacity': 1,
17762 'background-image': 'none',
17763 'background-image-crossorigin': 'anonymous',
17764 'background-image-opacity': 1,
17765 'background-position-x': '50%',
17766 'background-position-y': '50%',
17767 'background-offset-x': 0,
17768 'background-offset-y': 0,
17769 'background-width-relative-to': 'include-padding',
17770 'background-height-relative-to': 'include-padding',
17771 'background-repeat': 'no-repeat',
17772 'background-fit': 'none',
17773 'background-clip': 'node',
17774 'background-width': 'auto',
17775 'background-height': 'auto',
17776 'border-color': '#000',
17777 'border-opacity': 1,
17778 'border-width': 0,
17779 'border-style': 'solid',
17780 'height': 30,
17781 'width': 30,
17782 'shape': 'ellipse',
17783 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17784 'bounds-expansion': 0,
17785 // node gradient
17786 'background-gradient-direction': 'to-bottom',
17787 'background-gradient-stop-colors': '#999',
17788 'background-gradient-stop-positions': '0%',
17789 // ghost props
17790 'ghost': 'no',
17791 'ghost-offset-y': 0,
17792 'ghost-offset-x': 0,
17793 'ghost-opacity': 0,
17794 // compound props
17795 'padding': 0,
17796 'padding-relative-to': 'width',
17797 'position': 'origin',
17798 'compound-sizing-wrt-labels': 'include',
17799 'min-width': 0,
17800 'min-width-bias-left': 0,
17801 'min-width-bias-right': 0,
17802 'min-height': 0,
17803 'min-height-bias-top': 0,
17804 'min-height-bias-bottom': 0
17805 }, {
17806 // node pie bg
17807 'pie-size': '100%'
17808 }, [{
17809 name: 'pie-{{i}}-background-color',
17810 value: 'black'
17811 }, {
17812 name: 'pie-{{i}}-background-size',
17813 value: '0%'
17814 }, {
17815 name: 'pie-{{i}}-background-opacity',
17816 value: 1
17817 }].reduce(function (css, prop) {
17818 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17819 var name = prop.name.replace('{{i}}', i);
17820 var val = prop.value;
17821 css[name] = val;
17822 }
17823
17824 return css;
17825 }, {}), {
17826 // edge props
17827 'line-style': 'solid',
17828 'line-color': '#999',
17829 'line-fill': 'solid',
17830 'line-cap': 'butt',
17831 'line-gradient-stop-colors': '#999',
17832 'line-gradient-stop-positions': '0%',
17833 'control-point-step-size': 40,
17834 'control-point-weights': 0.5,
17835 'segment-weights': 0.5,
17836 'segment-distances': 20,
17837 'taxi-turn': '50%',
17838 'taxi-turn-min-distance': 10,
17839 'taxi-direction': 'auto',
17840 'edge-distances': 'intersection',
17841 'curve-style': 'haystack',
17842 'haystack-radius': 0,
17843 'arrow-scale': 1,
17844 'loop-direction': '-45deg',
17845 'loop-sweep': '-90deg',
17846 'source-distance-from-node': 0,
17847 'target-distance-from-node': 0,
17848 'source-endpoint': 'outside-to-node',
17849 'target-endpoint': 'outside-to-node',
17850 'line-dash-pattern': [6, 3],
17851 'line-dash-offset': 0
17852 }, [{
17853 name: 'arrow-shape',
17854 value: 'none'
17855 }, {
17856 name: 'arrow-color',
17857 value: '#999'
17858 }, {
17859 name: 'arrow-fill',
17860 value: 'filled'
17861 }].reduce(function (css, prop) {
17862 styfn$6.arrowPrefixes.forEach(function (prefix) {
17863 var name = prefix + '-' + prop.name;
17864 var val = prop.value;
17865 css[name] = val;
17866 });
17867 return css;
17868 }, {}));
17869 var parsedProps = {};
17870
17871 for (var i = 0; i < this.properties.length; i++) {
17872 var prop = this.properties[i];
17873
17874 if (prop.pointsTo) {
17875 continue;
17876 }
17877
17878 var name = prop.name;
17879 var val = rawProps[name];
17880 var parsedProp = this.parse(name, val);
17881 parsedProps[name] = parsedProp;
17882 }
17883
17884 _p.defaultProperties = parsedProps;
17885 return _p.defaultProperties;
17886 };
17887
17888 styfn$6.addDefaultStylesheet = function () {
17889 this.selector(':parent').css({
17890 'shape': 'rectangle',
17891 'padding': 10,
17892 'background-color': '#eee',
17893 'border-color': '#ccc',
17894 'border-width': 1
17895 }).selector('edge').css({
17896 'width': 3
17897 }).selector(':loop').css({
17898 'curve-style': 'bezier'
17899 }).selector('edge:compound').css({
17900 'curve-style': 'bezier',
17901 'source-endpoint': 'outside-to-line',
17902 'target-endpoint': 'outside-to-line'
17903 }).selector(':selected').css({
17904 'background-color': '#0169D9',
17905 'line-color': '#0169D9',
17906 'source-arrow-color': '#0169D9',
17907 'target-arrow-color': '#0169D9',
17908 'mid-source-arrow-color': '#0169D9',
17909 'mid-target-arrow-color': '#0169D9'
17910 }).selector(':parent:selected').css({
17911 'background-color': '#CCE1F9',
17912 'border-color': '#aec8e5'
17913 }).selector(':active').css({
17914 'overlay-color': 'black',
17915 'overlay-padding': 10,
17916 'overlay-opacity': 0.25
17917 });
17918 this.defaultLength = this.length;
17919 };
17920
17921 var styfn$7 = {}; // a caching layer for property parsing
17922
17923 styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17924 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17925
17926 if (fn(value)) {
17927 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17928 }
17929
17930 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17931 var bypassKey = propIsBypass ? 't' : 'f';
17932 var valueKey = '' + value;
17933 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17934 var propCache = self.propCache = self.propCache || [];
17935 var ret;
17936
17937 if (!(ret = propCache[argHash])) {
17938 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17939 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17940 // - mappings can't be shared b/c mappings are per-element
17941
17942
17943 if (propIsBypass || propIsFlat === 'mapping') {
17944 // need a copy since props are mutated later in their lifecycles
17945 ret = copy(ret);
17946
17947 if (ret) {
17948 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17949 }
17950 }
17951
17952 return ret;
17953 };
17954
17955 styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17956 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17957
17958 if (!prop && value != null) {
17959 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17960 }
17961
17962 return prop;
17963 }; // parse a property; return null on invalid; return parsed property otherwise
17964 // fields :
17965 // - name : the name of the property
17966 // - value : the parsed, native-typed value of the property
17967 // - strValue : a string value that represents the property value in valid css
17968 // - bypass : true iff the property is a bypass property
17969
17970
17971 styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17972 var self = this;
17973 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17974
17975 var property = self.properties[name];
17976 var passedValue = value;
17977 var types = self.types;
17978
17979 if (!property) {
17980 return null;
17981 } // return null on property of unknown name
17982
17983
17984 if (value === undefined) {
17985 return null;
17986 } // can't assign undefined
17987 // the property may be an alias
17988
17989
17990 if (property.alias) {
17991 property = property.pointsTo;
17992 name = property.name;
17993 }
17994
17995 var valueIsString = string(value);
17996
17997 if (valueIsString) {
17998 // trim the value to make parsing easier
17999 value = value.trim();
18000 }
18001
18002 var type = property.type;
18003
18004 if (!type) {
18005 return null;
18006 } // no type, no luck
18007 // check if bypass is null or empty string (i.e. indication to delete bypass property)
18008
18009
18010 if (propIsBypass && (value === '' || value === null)) {
18011 return {
18012 name: name,
18013 value: value,
18014 bypass: true,
18015 deleteBypass: true
18016 };
18017 } // check if value is a function used as a mapper
18018
18019
18020 if (fn(value)) {
18021 return {
18022 name: name,
18023 value: value,
18024 strValue: 'fn',
18025 mapped: types.fn,
18026 bypass: propIsBypass
18027 };
18028 } // check if value is mapped
18029
18030
18031 var data, mapData;
18032
18033 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))) {
18034 if (propIsBypass) {
18035 return false;
18036 } // mappers not allowed in bypass
18037
18038
18039 var mapped = types.data;
18040 return {
18041 name: name,
18042 value: data,
18043 strValue: '' + value,
18044 mapped: mapped,
18045 field: data[1],
18046 bypass: propIsBypass
18047 };
18048 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
18049 if (propIsBypass) {
18050 return false;
18051 } // mappers not allowed in bypass
18052
18053
18054 if (type.multiple) {
18055 return false;
18056 } // impossible to map to num
18057
18058
18059 var _mapped = types.mapData; // we can map only if the type is a colour or a number
18060
18061 if (!(type.color || type.number)) {
18062 return false;
18063 }
18064
18065 var valueMin = this.parse(name, mapData[4]); // parse to validate
18066
18067 if (!valueMin || valueMin.mapped) {
18068 return false;
18069 } // can't be invalid or mapped
18070
18071
18072 var valueMax = this.parse(name, mapData[5]); // parse to validate
18073
18074 if (!valueMax || valueMax.mapped) {
18075 return false;
18076 } // can't be invalid or mapped
18077 // check if valueMin and valueMax are the same
18078
18079
18080 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
18081 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
18082 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
18083 } else if (type.color) {
18084 var c1 = valueMin.value;
18085 var c2 = valueMax.value;
18086 var same = c1[0] === c2[0] // red
18087 && c1[1] === c2[1] // green
18088 && c1[2] === c2[2] // blue
18089 && ( // optional alpha
18090 c1[3] === c2[3] // same alpha outright
18091 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
18092 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
18093 );
18094
18095 if (same) {
18096 return false;
18097 } // can't make a mapper without a range
18098
18099 }
18100
18101 return {
18102 name: name,
18103 value: mapData,
18104 strValue: '' + value,
18105 mapped: _mapped,
18106 field: mapData[1],
18107 fieldMin: parseFloat(mapData[2]),
18108 // min & max are numeric
18109 fieldMax: parseFloat(mapData[3]),
18110 valueMin: valueMin.value,
18111 valueMax: valueMax.value,
18112 bypass: propIsBypass
18113 };
18114 }
18115
18116 if (type.multiple && propIsFlat !== 'multiple') {
18117 var vals;
18118
18119 if (valueIsString) {
18120 vals = value.split(/\s+/);
18121 } else if (array(value)) {
18122 vals = value;
18123 } else {
18124 vals = [value];
18125 }
18126
18127 if (type.evenMultiple && vals.length % 2 !== 0) {
18128 return null;
18129 }
18130
18131 var valArr = [];
18132 var unitsArr = [];
18133 var pfValArr = [];
18134 var strVal = '';
18135 var hasEnum = false;
18136
18137 for (var i = 0; i < vals.length; i++) {
18138 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
18139 hasEnum = hasEnum || string(p.value);
18140 valArr.push(p.value);
18141 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
18142 unitsArr.push(p.units);
18143 strVal += (i > 0 ? ' ' : '') + p.strValue;
18144 }
18145
18146 if (type.validate && !type.validate(valArr, unitsArr)) {
18147 return null;
18148 }
18149
18150 if (type.singleEnum && hasEnum) {
18151 if (valArr.length === 1 && string(valArr[0])) {
18152 return {
18153 name: name,
18154 value: valArr[0],
18155 strValue: valArr[0],
18156 bypass: propIsBypass
18157 };
18158 } else {
18159 return null;
18160 }
18161 }
18162
18163 return {
18164 name: name,
18165 value: valArr,
18166 pfValue: pfValArr,
18167 strValue: strVal,
18168 bypass: propIsBypass,
18169 units: unitsArr
18170 };
18171 } // several types also allow enums
18172
18173
18174 var checkEnums = function checkEnums() {
18175 for (var _i = 0; _i < type.enums.length; _i++) {
18176 var en = type.enums[_i];
18177
18178 if (en === value) {
18179 return {
18180 name: name,
18181 value: value,
18182 strValue: '' + value,
18183 bypass: propIsBypass
18184 };
18185 }
18186 }
18187
18188 return null;
18189 }; // check the type and return the appropriate object
18190
18191
18192 if (type.number) {
18193 var units;
18194 var implicitUnits = 'px'; // not set => px
18195
18196 if (type.units) {
18197 // use specified units if set
18198 units = type.units;
18199 }
18200
18201 if (type.implicitUnits) {
18202 implicitUnits = type.implicitUnits;
18203 }
18204
18205 if (!type.unitless) {
18206 if (valueIsString) {
18207 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
18208
18209 if (units) {
18210 unitsRegex = units;
18211 } // only allow explicit units if so set
18212
18213
18214 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
18215
18216 if (match) {
18217 value = match[1];
18218 units = match[2] || implicitUnits;
18219 }
18220 } else if (!units || type.implicitUnits) {
18221 units = implicitUnits; // implicitly px if unspecified
18222 }
18223 }
18224
18225 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
18226
18227 if (isNaN(value) && type.enums === undefined) {
18228 return null;
18229 } // check if this number type also accepts special keywords in place of numbers
18230 // (i.e. `left`, `auto`, etc)
18231
18232
18233 if (isNaN(value) && type.enums !== undefined) {
18234 value = passedValue;
18235 return checkEnums();
18236 } // check if value must be an integer
18237
18238
18239 if (type.integer && !integer(value)) {
18240 return null;
18241 } // check value is within range
18242
18243
18244 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
18245 return null;
18246 }
18247
18248 var ret = {
18249 name: name,
18250 value: value,
18251 strValue: '' + value + (units ? units : ''),
18252 units: units,
18253 bypass: propIsBypass
18254 }; // normalise value in pixels
18255
18256 if (type.unitless || units !== 'px' && units !== 'em') {
18257 ret.pfValue = value;
18258 } else {
18259 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
18260 } // normalise value in ms
18261
18262
18263 if (units === 'ms' || units === 's') {
18264 ret.pfValue = units === 'ms' ? value : 1000 * value;
18265 } // normalise value in rad
18266
18267
18268 if (units === 'deg' || units === 'rad') {
18269 ret.pfValue = units === 'rad' ? value : deg2rad(value);
18270 } // normalize value in %
18271
18272
18273 if (units === '%') {
18274 ret.pfValue = value / 100;
18275 }
18276
18277 return ret;
18278 } else if (type.propList) {
18279 var props = [];
18280 var propsStr = '' + value;
18281
18282 if (propsStr === 'none') ; else {
18283 // go over each prop
18284 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
18285
18286 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
18287 var propName = propsSplit[_i2].trim();
18288
18289 if (self.properties[propName]) {
18290 props.push(propName);
18291 } else {
18292 warn('`' + propName + '` is not a valid property name');
18293 }
18294 }
18295
18296 if (props.length === 0) {
18297 return null;
18298 }
18299 }
18300
18301 return {
18302 name: name,
18303 value: props,
18304 strValue: props.length === 0 ? 'none' : props.join(' '),
18305 bypass: propIsBypass
18306 };
18307 } else if (type.color) {
18308 var tuple = color2tuple(value);
18309
18310 if (!tuple) {
18311 return null;
18312 }
18313
18314 return {
18315 name: name,
18316 value: tuple,
18317 pfValue: tuple,
18318 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
18319 // n.b. no spaces b/c of multiple support
18320 bypass: propIsBypass
18321 };
18322 } else if (type.regex || type.regexes) {
18323 // first check enums
18324 if (type.enums) {
18325 var enumProp = checkEnums();
18326
18327 if (enumProp) {
18328 return enumProp;
18329 }
18330 }
18331
18332 var regexes = type.regexes ? type.regexes : [type.regex];
18333
18334 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
18335 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
18336
18337 var m = regex.exec(value);
18338
18339 if (m) {
18340 // regex matches
18341 return {
18342 name: name,
18343 value: type.singleRegexMatchValue ? m[1] : m,
18344 strValue: '' + value,
18345 bypass: propIsBypass
18346 };
18347 }
18348 }
18349
18350 return null; // didn't match any
18351 } else if (type.string) {
18352 // just return
18353 return {
18354 name: name,
18355 value: '' + value,
18356 strValue: '' + value,
18357 bypass: propIsBypass
18358 };
18359 } else if (type.enums) {
18360 // check enums last because it's a combo type in others
18361 return checkEnums();
18362 } else {
18363 return null; // not a type we can handle
18364 }
18365 };
18366
18367 var Style = function Style(cy) {
18368 if (!(this instanceof Style)) {
18369 return new Style(cy);
18370 }
18371
18372 if (!core(cy)) {
18373 error('A style must have a core reference');
18374 return;
18375 }
18376
18377 this._private = {
18378 cy: cy,
18379 coreStyle: {}
18380 };
18381 this.length = 0;
18382 this.resetToDefault();
18383 };
18384
18385 var styfn$8 = Style.prototype;
18386
18387 styfn$8.instanceString = function () {
18388 return 'style';
18389 }; // remove all contexts
18390
18391
18392 styfn$8.clear = function () {
18393 for (var i = 0; i < this.length; i++) {
18394 this[i] = undefined;
18395 }
18396
18397 this.length = 0;
18398 var _p = this._private;
18399 _p.newStyle = true;
18400 return this; // chaining
18401 };
18402
18403 styfn$8.resetToDefault = function () {
18404 this.clear();
18405 this.addDefaultStylesheet();
18406 return this;
18407 }; // builds a style object for the 'core' selector
18408
18409
18410 styfn$8.core = function (propName) {
18411 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
18412 }; // create a new context from the specified selector string and switch to that context
18413
18414
18415 styfn$8.selector = function (selectorStr) {
18416 // 'core' is a special case and does not need a selector
18417 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
18418 var i = this.length++; // new context means new index
18419
18420 this[i] = {
18421 selector: selector,
18422 properties: [],
18423 mappedProperties: [],
18424 index: i
18425 };
18426 return this; // chaining
18427 }; // add one or many css rules to the current context
18428
18429
18430 styfn$8.css = function () {
18431 var self = this;
18432 var args = arguments;
18433
18434 if (args.length === 1) {
18435 var map = args[0];
18436
18437 for (var i = 0; i < self.properties.length; i++) {
18438 var prop = self.properties[i];
18439 var mapVal = map[prop.name];
18440
18441 if (mapVal === undefined) {
18442 mapVal = map[dash2camel(prop.name)];
18443 }
18444
18445 if (mapVal !== undefined) {
18446 this.cssRule(prop.name, mapVal);
18447 }
18448 }
18449 } else if (args.length === 2) {
18450 this.cssRule(args[0], args[1]);
18451 } // do nothing if args are invalid
18452
18453
18454 return this; // chaining
18455 };
18456
18457 styfn$8.style = styfn$8.css; // add a single css rule to the current context
18458
18459 styfn$8.cssRule = function (name, value) {
18460 // name-value pair
18461 var property = this.parse(name, value); // add property to current context if valid
18462
18463 if (property) {
18464 var i = this.length - 1;
18465 this[i].properties.push(property);
18466 this[i].properties[property.name] = property; // allow access by name as well
18467
18468 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
18469 this._private.hasPie = true;
18470 }
18471
18472 if (property.mapped) {
18473 this[i].mappedProperties.push(property);
18474 } // add to core style if necessary
18475
18476
18477 var currentSelectorIsCore = !this[i].selector;
18478
18479 if (currentSelectorIsCore) {
18480 this._private.coreStyle[property.name] = property;
18481 }
18482 }
18483
18484 return this; // chaining
18485 };
18486
18487 styfn$8.append = function (style) {
18488 if (stylesheet(style)) {
18489 style.appendToStyle(this);
18490 } else if (array(style)) {
18491 this.appendFromJson(style);
18492 } else if (string(style)) {
18493 this.appendFromString(style);
18494 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
18495
18496
18497 return this;
18498 }; // static function
18499
18500
18501 Style.fromJson = function (cy, json) {
18502 var style = new Style(cy);
18503 style.fromJson(json);
18504 return style;
18505 };
18506
18507 Style.fromString = function (cy, string) {
18508 return new Style(cy).fromString(string);
18509 };
18510
18511 [styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
18512 extend(styfn$8, props);
18513 });
18514 Style.types = styfn$8.types;
18515 Style.properties = styfn$8.properties;
18516 Style.propertyGroups = styfn$8.propertyGroups;
18517 Style.propertyGroupNames = styfn$8.propertyGroupNames;
18518 Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
18519
18520 var corefn$7 = {
18521 style: function style(newStyle) {
18522 if (newStyle) {
18523 var s = this.setStyle(newStyle);
18524 s.update();
18525 }
18526
18527 return this._private.style;
18528 },
18529 setStyle: function setStyle(style) {
18530 var _p = this._private;
18531
18532 if (stylesheet(style)) {
18533 _p.style = style.generateStyle(this);
18534 } else if (array(style)) {
18535 _p.style = Style.fromJson(this, style);
18536 } else if (string(style)) {
18537 _p.style = Style.fromString(this, style);
18538 } else {
18539 _p.style = Style(this);
18540 }
18541
18542 return _p.style;
18543 }
18544 };
18545
18546 var defaultSelectionType = 'single';
18547 var corefn$8 = {
18548 autolock: function autolock(bool) {
18549 if (bool !== undefined) {
18550 this._private.autolock = bool ? true : false;
18551 } else {
18552 return this._private.autolock;
18553 }
18554
18555 return this; // chaining
18556 },
18557 autoungrabify: function autoungrabify(bool) {
18558 if (bool !== undefined) {
18559 this._private.autoungrabify = bool ? true : false;
18560 } else {
18561 return this._private.autoungrabify;
18562 }
18563
18564 return this; // chaining
18565 },
18566 autounselectify: function autounselectify(bool) {
18567 if (bool !== undefined) {
18568 this._private.autounselectify = bool ? true : false;
18569 } else {
18570 return this._private.autounselectify;
18571 }
18572
18573 return this; // chaining
18574 },
18575 selectionType: function selectionType(selType) {
18576 var _p = this._private;
18577
18578 if (_p.selectionType == null) {
18579 _p.selectionType = defaultSelectionType;
18580 }
18581
18582 if (selType !== undefined) {
18583 if (selType === 'additive' || selType === 'single') {
18584 _p.selectionType = selType;
18585 }
18586 } else {
18587 return _p.selectionType;
18588 }
18589
18590 return this;
18591 },
18592 panningEnabled: function panningEnabled(bool) {
18593 if (bool !== undefined) {
18594 this._private.panningEnabled = bool ? true : false;
18595 } else {
18596 return this._private.panningEnabled;
18597 }
18598
18599 return this; // chaining
18600 },
18601 userPanningEnabled: function userPanningEnabled(bool) {
18602 if (bool !== undefined) {
18603 this._private.userPanningEnabled = bool ? true : false;
18604 } else {
18605 return this._private.userPanningEnabled;
18606 }
18607
18608 return this; // chaining
18609 },
18610 zoomingEnabled: function zoomingEnabled(bool) {
18611 if (bool !== undefined) {
18612 this._private.zoomingEnabled = bool ? true : false;
18613 } else {
18614 return this._private.zoomingEnabled;
18615 }
18616
18617 return this; // chaining
18618 },
18619 userZoomingEnabled: function userZoomingEnabled(bool) {
18620 if (bool !== undefined) {
18621 this._private.userZoomingEnabled = bool ? true : false;
18622 } else {
18623 return this._private.userZoomingEnabled;
18624 }
18625
18626 return this; // chaining
18627 },
18628 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18629 if (bool !== undefined) {
18630 this._private.boxSelectionEnabled = bool ? true : false;
18631 } else {
18632 return this._private.boxSelectionEnabled;
18633 }
18634
18635 return this; // chaining
18636 },
18637 pan: function pan() {
18638 var args = arguments;
18639 var pan = this._private.pan;
18640 var dim, val, dims, x, y;
18641
18642 switch (args.length) {
18643 case 0:
18644 // .pan()
18645 return pan;
18646
18647 case 1:
18648 if (string(args[0])) {
18649 // .pan('x')
18650 dim = args[0];
18651 return pan[dim];
18652 } else if (plainObject(args[0])) {
18653 // .pan({ x: 0, y: 100 })
18654 if (!this._private.panningEnabled) {
18655 return this;
18656 }
18657
18658 dims = args[0];
18659 x = dims.x;
18660 y = dims.y;
18661
18662 if (number(x)) {
18663 pan.x = x;
18664 }
18665
18666 if (number(y)) {
18667 pan.y = y;
18668 }
18669
18670 this.emit('pan viewport');
18671 }
18672
18673 break;
18674
18675 case 2:
18676 // .pan('x', 100)
18677 if (!this._private.panningEnabled) {
18678 return this;
18679 }
18680
18681 dim = args[0];
18682 val = args[1];
18683
18684 if ((dim === 'x' || dim === 'y') && number(val)) {
18685 pan[dim] = val;
18686 }
18687
18688 this.emit('pan viewport');
18689 break;
18690 // invalid
18691 }
18692
18693 this.notify('viewport');
18694 return this; // chaining
18695 },
18696 panBy: function panBy(arg0, arg1) {
18697 var args = arguments;
18698 var pan = this._private.pan;
18699 var dim, val, dims, x, y;
18700
18701 if (!this._private.panningEnabled) {
18702 return this;
18703 }
18704
18705 switch (args.length) {
18706 case 1:
18707 if (plainObject(arg0)) {
18708 // .panBy({ x: 0, y: 100 })
18709 dims = args[0];
18710 x = dims.x;
18711 y = dims.y;
18712
18713 if (number(x)) {
18714 pan.x += x;
18715 }
18716
18717 if (number(y)) {
18718 pan.y += y;
18719 }
18720
18721 this.emit('pan viewport');
18722 }
18723
18724 break;
18725
18726 case 2:
18727 // .panBy('x', 100)
18728 dim = arg0;
18729 val = arg1;
18730
18731 if ((dim === 'x' || dim === 'y') && number(val)) {
18732 pan[dim] += val;
18733 }
18734
18735 this.emit('pan viewport');
18736 break;
18737 // invalid
18738 }
18739
18740 this.notify('viewport');
18741 return this; // chaining
18742 },
18743 fit: function fit(elements, padding) {
18744 var viewportState = this.getFitViewport(elements, padding);
18745
18746 if (viewportState) {
18747 var _p = this._private;
18748 _p.zoom = viewportState.zoom;
18749 _p.pan = viewportState.pan;
18750 this.emit('pan zoom viewport');
18751 this.notify('viewport');
18752 }
18753
18754 return this; // chaining
18755 },
18756 getFitViewport: function getFitViewport(elements, padding) {
18757 if (number(elements) && padding === undefined) {
18758 // elements is optional
18759 padding = elements;
18760 elements = undefined;
18761 }
18762
18763 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18764 return;
18765 }
18766
18767 var bb;
18768
18769 if (string(elements)) {
18770 var sel = elements;
18771 elements = this.$(sel);
18772 } else if (boundingBox(elements)) {
18773 // assume bb
18774 var bbe = elements;
18775 bb = {
18776 x1: bbe.x1,
18777 y1: bbe.y1,
18778 x2: bbe.x2,
18779 y2: bbe.y2
18780 };
18781 bb.w = bb.x2 - bb.x1;
18782 bb.h = bb.y2 - bb.y1;
18783 } else if (!elementOrCollection(elements)) {
18784 elements = this.mutableElements();
18785 }
18786
18787 if (elementOrCollection(elements) && elements.empty()) {
18788 return;
18789 } // can't fit to nothing
18790
18791
18792 bb = bb || elements.boundingBox();
18793 var w = this.width();
18794 var h = this.height();
18795 var zoom;
18796 padding = number(padding) ? padding : 0;
18797
18798 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18799 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18800
18801 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18802 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18803 var pan = {
18804 // now pan to middle
18805 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18806 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18807 };
18808 return {
18809 zoom: zoom,
18810 pan: pan
18811 };
18812 }
18813
18814 return;
18815 },
18816 zoomRange: function zoomRange(min, max) {
18817 var _p = this._private;
18818
18819 if (max == null) {
18820 var opts = min;
18821 min = opts.min;
18822 max = opts.max;
18823 }
18824
18825 if (number(min) && number(max) && min <= max) {
18826 _p.minZoom = min;
18827 _p.maxZoom = max;
18828 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18829 _p.minZoom = min;
18830 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18831 _p.maxZoom = max;
18832 }
18833
18834 return this;
18835 },
18836 minZoom: function minZoom(zoom) {
18837 if (zoom === undefined) {
18838 return this._private.minZoom;
18839 } else {
18840 return this.zoomRange({
18841 min: zoom
18842 });
18843 }
18844 },
18845 maxZoom: function maxZoom(zoom) {
18846 if (zoom === undefined) {
18847 return this._private.maxZoom;
18848 } else {
18849 return this.zoomRange({
18850 max: zoom
18851 });
18852 }
18853 },
18854 getZoomedViewport: function getZoomedViewport(params) {
18855 var _p = this._private;
18856 var currentPan = _p.pan;
18857 var currentZoom = _p.zoom;
18858 var pos; // in rendered px
18859
18860 var zoom;
18861 var bail = false;
18862
18863 if (!_p.zoomingEnabled) {
18864 // zooming disabled
18865 bail = true;
18866 }
18867
18868 if (number(params)) {
18869 // then set the zoom
18870 zoom = params;
18871 } else if (plainObject(params)) {
18872 // then zoom about a point
18873 zoom = params.level;
18874
18875 if (params.position != null) {
18876 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18877 } else if (params.renderedPosition != null) {
18878 pos = params.renderedPosition;
18879 }
18880
18881 if (pos != null && !_p.panningEnabled) {
18882 // panning disabled
18883 bail = true;
18884 }
18885 } // crop zoom
18886
18887
18888 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18889 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18890
18891 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18892 return null;
18893 }
18894
18895 if (pos != null) {
18896 // set zoom about position
18897 var pan1 = currentPan;
18898 var zoom1 = currentZoom;
18899 var zoom2 = zoom;
18900 var pan2 = {
18901 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18902 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18903 };
18904 return {
18905 zoomed: true,
18906 panned: true,
18907 zoom: zoom2,
18908 pan: pan2
18909 };
18910 } else {
18911 // just set the zoom
18912 return {
18913 zoomed: true,
18914 panned: false,
18915 zoom: zoom,
18916 pan: currentPan
18917 };
18918 }
18919 },
18920 zoom: function zoom(params) {
18921 if (params === undefined) {
18922 // get
18923 return this._private.zoom;
18924 } else {
18925 // set
18926 var vp = this.getZoomedViewport(params);
18927 var _p = this._private;
18928
18929 if (vp == null || !vp.zoomed) {
18930 return this;
18931 }
18932
18933 _p.zoom = vp.zoom;
18934
18935 if (vp.panned) {
18936 _p.pan.x = vp.pan.x;
18937 _p.pan.y = vp.pan.y;
18938 }
18939
18940 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18941 this.notify('viewport');
18942 return this; // chaining
18943 }
18944 },
18945 viewport: function viewport(opts) {
18946 var _p = this._private;
18947 var zoomDefd = true;
18948 var panDefd = true;
18949 var events = []; // to trigger
18950
18951 var zoomFailed = false;
18952 var panFailed = false;
18953
18954 if (!opts) {
18955 return this;
18956 }
18957
18958 if (!number(opts.zoom)) {
18959 zoomDefd = false;
18960 }
18961
18962 if (!plainObject(opts.pan)) {
18963 panDefd = false;
18964 }
18965
18966 if (!zoomDefd && !panDefd) {
18967 return this;
18968 }
18969
18970 if (zoomDefd) {
18971 var z = opts.zoom;
18972
18973 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18974 zoomFailed = true;
18975 } else {
18976 _p.zoom = z;
18977 events.push('zoom');
18978 }
18979 }
18980
18981 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18982 var p = opts.pan;
18983
18984 if (number(p.x)) {
18985 _p.pan.x = p.x;
18986 panFailed = false;
18987 }
18988
18989 if (number(p.y)) {
18990 _p.pan.y = p.y;
18991 panFailed = false;
18992 }
18993
18994 if (!panFailed) {
18995 events.push('pan');
18996 }
18997 }
18998
18999 if (events.length > 0) {
19000 events.push('viewport');
19001 this.emit(events.join(' '));
19002 this.notify('viewport');
19003 }
19004
19005 return this; // chaining
19006 },
19007 center: function center(elements) {
19008 var pan = this.getCenterPan(elements);
19009
19010 if (pan) {
19011 this._private.pan = pan;
19012 this.emit('pan viewport');
19013 this.notify('viewport');
19014 }
19015
19016 return this; // chaining
19017 },
19018 getCenterPan: function getCenterPan(elements, zoom) {
19019 if (!this._private.panningEnabled) {
19020 return;
19021 }
19022
19023 if (string(elements)) {
19024 var selector = elements;
19025 elements = this.mutableElements().filter(selector);
19026 } else if (!elementOrCollection(elements)) {
19027 elements = this.mutableElements();
19028 }
19029
19030 if (elements.length === 0) {
19031 return;
19032 } // can't centre pan to nothing
19033
19034
19035 var bb = elements.boundingBox();
19036 var w = this.width();
19037 var h = this.height();
19038 zoom = zoom === undefined ? this._private.zoom : zoom;
19039 var pan = {
19040 // middle
19041 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
19042 y: (h - zoom * (bb.y1 + bb.y2)) / 2
19043 };
19044 return pan;
19045 },
19046 reset: function reset() {
19047 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
19048 return this;
19049 }
19050
19051 this.viewport({
19052 pan: {
19053 x: 0,
19054 y: 0
19055 },
19056 zoom: 1
19057 });
19058 return this; // chaining
19059 },
19060 invalidateSize: function invalidateSize() {
19061 this._private.sizeCache = null;
19062 },
19063 size: function size() {
19064 var _p = this._private;
19065 var container = _p.container;
19066 return _p.sizeCache = _p.sizeCache || (container ? function () {
19067 var style = window$1.getComputedStyle(container);
19068
19069 var val = function val(name) {
19070 return parseFloat(style.getPropertyValue(name));
19071 };
19072
19073 return {
19074 width: container.clientWidth - val('padding-left') - val('padding-right'),
19075 height: container.clientHeight - val('padding-top') - val('padding-bottom')
19076 };
19077 }() : {
19078 // fallback if no container (not 0 b/c can be used for dividing etc)
19079 width: 1,
19080 height: 1
19081 });
19082 },
19083 width: function width() {
19084 return this.size().width;
19085 },
19086 height: function height() {
19087 return this.size().height;
19088 },
19089 extent: function extent() {
19090 var pan = this._private.pan;
19091 var zoom = this._private.zoom;
19092 var rb = this.renderedExtent();
19093 var b = {
19094 x1: (rb.x1 - pan.x) / zoom,
19095 x2: (rb.x2 - pan.x) / zoom,
19096 y1: (rb.y1 - pan.y) / zoom,
19097 y2: (rb.y2 - pan.y) / zoom
19098 };
19099 b.w = b.x2 - b.x1;
19100 b.h = b.y2 - b.y1;
19101 return b;
19102 },
19103 renderedExtent: function renderedExtent() {
19104 var width = this.width();
19105 var height = this.height();
19106 return {
19107 x1: 0,
19108 y1: 0,
19109 x2: width,
19110 y2: height,
19111 w: width,
19112 h: height
19113 };
19114 }
19115 }; // aliases
19116
19117 corefn$8.centre = corefn$8.center; // backwards compatibility
19118
19119 corefn$8.autolockNodes = corefn$8.autolock;
19120 corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
19121
19122 var fn$6 = {
19123 data: define$3.data({
19124 field: 'data',
19125 bindingEvent: 'data',
19126 allowBinding: true,
19127 allowSetting: true,
19128 settingEvent: 'data',
19129 settingTriggersEvent: true,
19130 triggerFnName: 'trigger',
19131 allowGetting: true
19132 }),
19133 removeData: define$3.removeData({
19134 field: 'data',
19135 event: 'data',
19136 triggerFnName: 'trigger',
19137 triggerEvent: true
19138 }),
19139 scratch: define$3.data({
19140 field: 'scratch',
19141 bindingEvent: 'scratch',
19142 allowBinding: true,
19143 allowSetting: true,
19144 settingEvent: 'scratch',
19145 settingTriggersEvent: true,
19146 triggerFnName: 'trigger',
19147 allowGetting: true
19148 }),
19149 removeScratch: define$3.removeData({
19150 field: 'scratch',
19151 event: 'scratch',
19152 triggerFnName: 'trigger',
19153 triggerEvent: true
19154 })
19155 }; // aliases
19156
19157 fn$6.attr = fn$6.data;
19158 fn$6.removeAttr = fn$6.removeData;
19159
19160 var Core = function Core(opts) {
19161 var cy = this;
19162 opts = extend({}, opts);
19163 var container = opts.container; // allow for passing a wrapped jquery object
19164 // e.g. cytoscape({ container: $('#cy') })
19165
19166 if (container && !htmlElement(container) && htmlElement(container[0])) {
19167 container = container[0];
19168 }
19169
19170 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
19171
19172 reg = reg || {};
19173
19174 if (reg && reg.cy) {
19175 reg.cy.destroy();
19176 reg = {}; // old instance => replace reg completely
19177 }
19178
19179 var readies = reg.readies = reg.readies || [];
19180
19181 if (container) {
19182 container._cyreg = reg;
19183 } // make sure container assoc'd reg points to this cy
19184
19185
19186 reg.cy = cy;
19187 var head = window$1 !== undefined && container !== undefined && !opts.headless;
19188 var options = opts;
19189 options.layout = extend({
19190 name: head ? 'grid' : 'null'
19191 }, options.layout);
19192 options.renderer = extend({
19193 name: head ? 'canvas' : 'null'
19194 }, options.renderer);
19195
19196 var defVal = function defVal(def, val, altVal) {
19197 if (val !== undefined) {
19198 return val;
19199 } else if (altVal !== undefined) {
19200 return altVal;
19201 } else {
19202 return def;
19203 }
19204 };
19205
19206 var _p = this._private = {
19207 container: container,
19208 // html dom ele container
19209 ready: false,
19210 // whether ready has been triggered
19211 options: options,
19212 // cached options
19213 elements: new Collection(this),
19214 // elements in the graph
19215 listeners: [],
19216 // list of listeners
19217 aniEles: new Collection(this),
19218 // elements being animated
19219 data: {},
19220 // data for the core
19221 scratch: {},
19222 // scratch object for core
19223 layout: null,
19224 renderer: null,
19225 destroyed: false,
19226 // whether destroy was called
19227 notificationsEnabled: true,
19228 // whether notifications are sent to the renderer
19229 minZoom: 1e-50,
19230 maxZoom: 1e50,
19231 zoomingEnabled: defVal(true, options.zoomingEnabled),
19232 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
19233 panningEnabled: defVal(true, options.panningEnabled),
19234 userPanningEnabled: defVal(true, options.userPanningEnabled),
19235 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
19236 autolock: defVal(false, options.autolock, options.autolockNodes),
19237 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
19238 autounselectify: defVal(false, options.autounselectify),
19239 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
19240 zoom: number(options.zoom) ? options.zoom : 1,
19241 pan: {
19242 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
19243 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
19244 },
19245 animation: {
19246 // object for currently-running animations
19247 current: [],
19248 queue: []
19249 },
19250 hasCompoundNodes: false
19251 };
19252
19253 this.createEmitter(); // set selection type
19254
19255 this.selectionType(options.selectionType); // init zoom bounds
19256
19257 this.zoomRange({
19258 min: options.minZoom,
19259 max: options.maxZoom
19260 });
19261
19262 var loadExtData = function loadExtData(extData, next) {
19263 var anyIsPromise = extData.some(promise);
19264
19265 if (anyIsPromise) {
19266 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
19267 } else {
19268 next(extData); // exec synchronously for convenience
19269 }
19270 }; // start with the default stylesheet so we have something before loading an external stylesheet
19271
19272
19273 if (_p.styleEnabled) {
19274 cy.setStyle([]);
19275 } // create the renderer
19276
19277
19278 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
19279
19280 cy.initRenderer(rendererOptions);
19281
19282 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
19283 cy.notifications(false); // remove old elements
19284
19285 var oldEles = cy.mutableElements();
19286
19287 if (oldEles.length > 0) {
19288 oldEles.remove();
19289 }
19290
19291 if (elements != null) {
19292 if (plainObject(elements) || array(elements)) {
19293 cy.add(elements);
19294 }
19295 }
19296
19297 cy.one('layoutready', function (e) {
19298 cy.notifications(true);
19299 cy.emit(e); // we missed this event by turning notifications off, so pass it on
19300
19301 cy.one('load', onload);
19302 cy.emitAndNotify('load');
19303 }).one('layoutstop', function () {
19304 cy.one('done', ondone);
19305 cy.emit('done');
19306 });
19307 var layoutOpts = extend({}, cy._private.options.layout);
19308 layoutOpts.eles = cy.elements();
19309 cy.layout(layoutOpts).run();
19310 };
19311
19312 loadExtData([options.style, options.elements], function (thens) {
19313 var initStyle = thens[0];
19314 var initEles = thens[1]; // init style
19315
19316 if (_p.styleEnabled) {
19317 cy.style().append(initStyle);
19318 } // initial load
19319
19320
19321 setElesAndLayout(initEles, function () {
19322 // onready
19323 cy.startAnimationLoop();
19324 _p.ready = true; // if a ready callback is specified as an option, the bind it
19325
19326 if (fn(options.ready)) {
19327 cy.on('ready', options.ready);
19328 } // bind all the ready handlers registered before creating this instance
19329
19330
19331 for (var i = 0; i < readies.length; i++) {
19332 var fn$1 = readies[i];
19333 cy.on('ready', fn$1);
19334 }
19335
19336 if (reg) {
19337 reg.readies = [];
19338 } // 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
19339
19340
19341 cy.emit('ready');
19342 }, options.done);
19343 });
19344 };
19345
19346 var corefn$9 = Core.prototype; // short alias
19347
19348 extend(corefn$9, {
19349 instanceString: function instanceString() {
19350 return 'core';
19351 },
19352 isReady: function isReady() {
19353 return this._private.ready;
19354 },
19355 destroyed: function destroyed() {
19356 return this._private.destroyed;
19357 },
19358 ready: function ready(fn) {
19359 if (this.isReady()) {
19360 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
19361 } else {
19362 this.on('ready', fn);
19363 }
19364
19365 return this;
19366 },
19367 destroy: function destroy() {
19368 var cy = this;
19369 if (cy.destroyed()) return;
19370 cy.stopAnimationLoop();
19371 cy.destroyRenderer();
19372 this.emit('destroy');
19373 cy._private.destroyed = true;
19374 return cy;
19375 },
19376 hasElementWithId: function hasElementWithId(id) {
19377 return this._private.elements.hasElementWithId(id);
19378 },
19379 getElementById: function getElementById(id) {
19380 return this._private.elements.getElementById(id);
19381 },
19382 hasCompoundNodes: function hasCompoundNodes() {
19383 return this._private.hasCompoundNodes;
19384 },
19385 headless: function headless() {
19386 return this._private.renderer.isHeadless();
19387 },
19388 styleEnabled: function styleEnabled() {
19389 return this._private.styleEnabled;
19390 },
19391 addToPool: function addToPool(eles) {
19392 this._private.elements.merge(eles);
19393
19394 return this; // chaining
19395 },
19396 removeFromPool: function removeFromPool(eles) {
19397 this._private.elements.unmerge(eles);
19398
19399 return this;
19400 },
19401 container: function container() {
19402 return this._private.container || null;
19403 },
19404 mount: function mount(container) {
19405 if (container == null) {
19406 return;
19407 }
19408
19409 var cy = this;
19410 var _p = cy._private;
19411 var options = _p.options;
19412
19413 if (!htmlElement(container) && htmlElement(container[0])) {
19414 container = container[0];
19415 }
19416
19417 cy.stopAnimationLoop();
19418 cy.destroyRenderer();
19419 _p.container = container;
19420 _p.styleEnabled = true;
19421 cy.invalidateSize();
19422 cy.initRenderer(extend({}, options, options.renderer, {
19423 // allow custom renderer name to be re-used, otherwise use canvas
19424 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
19425 }));
19426 cy.startAnimationLoop();
19427 cy.style(options.style);
19428 cy.emit('mount');
19429 return cy;
19430 },
19431 unmount: function unmount() {
19432 var cy = this;
19433 cy.stopAnimationLoop();
19434 cy.destroyRenderer();
19435 cy.initRenderer({
19436 name: 'null'
19437 });
19438 cy.emit('unmount');
19439 return cy;
19440 },
19441 options: function options() {
19442 return copy(this._private.options);
19443 },
19444 json: function json(obj) {
19445 var cy = this;
19446 var _p = cy._private;
19447 var eles = cy.mutableElements();
19448
19449 var getFreshRef = function getFreshRef(ele) {
19450 return cy.getElementById(ele.id());
19451 };
19452
19453 if (plainObject(obj)) {
19454 // set
19455 cy.startBatch();
19456
19457 if (obj.elements) {
19458 var idInJson = {};
19459
19460 var updateEles = function updateEles(jsons, gr) {
19461 var toAdd = [];
19462 var toMod = [];
19463
19464 for (var i = 0; i < jsons.length; i++) {
19465 var json = jsons[i];
19466 var id = '' + json.data.id; // id must be string
19467
19468 var ele = cy.getElementById(id);
19469 idInJson[id] = true;
19470
19471 if (ele.length !== 0) {
19472 // existing element should be updated
19473 toMod.push({
19474 ele: ele,
19475 json: json
19476 });
19477 } else {
19478 // otherwise should be added
19479 if (gr) {
19480 json.group = gr;
19481 toAdd.push(json);
19482 } else {
19483 toAdd.push(json);
19484 }
19485 }
19486 }
19487
19488 cy.add(toAdd);
19489
19490 for (var _i = 0; _i < toMod.length; _i++) {
19491 var _toMod$_i = toMod[_i],
19492 _ele = _toMod$_i.ele,
19493 _json = _toMod$_i.json;
19494
19495 _ele.json(_json);
19496 }
19497 };
19498
19499 if (array(obj.elements)) {
19500 // elements: []
19501 updateEles(obj.elements);
19502 } else {
19503 // elements: { nodes: [], edges: [] }
19504 var grs = ['nodes', 'edges'];
19505
19506 for (var i = 0; i < grs.length; i++) {
19507 var gr = grs[i];
19508 var elements = obj.elements[gr];
19509
19510 if (array(elements)) {
19511 updateEles(elements, gr);
19512 }
19513 }
19514 }
19515
19516 var parentsToRemove = cy.collection();
19517 eles.filter(function (ele) {
19518 return !idInJson[ele.id()];
19519 }).forEach(function (ele) {
19520 if (ele.isParent()) {
19521 parentsToRemove.merge(ele);
19522 } else {
19523 ele.remove();
19524 }
19525 }); // so that children are not removed w/parent
19526
19527 parentsToRemove.forEach(function (ele) {
19528 return ele.children().move({
19529 parent: null
19530 });
19531 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
19532
19533 parentsToRemove.forEach(function (ele) {
19534 return getFreshRef(ele).remove();
19535 });
19536 }
19537
19538 if (obj.style) {
19539 cy.style(obj.style);
19540 }
19541
19542 if (obj.zoom != null && obj.zoom !== _p.zoom) {
19543 cy.zoom(obj.zoom);
19544 }
19545
19546 if (obj.pan) {
19547 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
19548 cy.pan(obj.pan);
19549 }
19550 }
19551
19552 if (obj.data) {
19553 cy.data(obj.data);
19554 }
19555
19556 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
19557
19558 for (var _i2 = 0; _i2 < fields.length; _i2++) {
19559 var f = fields[_i2];
19560
19561 if (obj[f] != null) {
19562 cy[f](obj[f]);
19563 }
19564 }
19565
19566 cy.endBatch();
19567 return this; // chaining
19568 } else {
19569 // get
19570 var flat = !!obj;
19571 var json = {};
19572
19573 if (flat) {
19574 json.elements = this.elements().map(function (ele) {
19575 return ele.json();
19576 });
19577 } else {
19578 json.elements = {};
19579 eles.forEach(function (ele) {
19580 var group = ele.group();
19581
19582 if (!json.elements[group]) {
19583 json.elements[group] = [];
19584 }
19585
19586 json.elements[group].push(ele.json());
19587 });
19588 }
19589
19590 if (this._private.styleEnabled) {
19591 json.style = cy.style().json();
19592 }
19593
19594 json.data = copy(cy.data());
19595 var options = _p.options;
19596 json.zoomingEnabled = _p.zoomingEnabled;
19597 json.userZoomingEnabled = _p.userZoomingEnabled;
19598 json.zoom = _p.zoom;
19599 json.minZoom = _p.minZoom;
19600 json.maxZoom = _p.maxZoom;
19601 json.panningEnabled = _p.panningEnabled;
19602 json.userPanningEnabled = _p.userPanningEnabled;
19603 json.pan = copy(_p.pan);
19604 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19605 json.renderer = copy(options.renderer);
19606 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19607 json.textureOnViewport = options.textureOnViewport;
19608 json.wheelSensitivity = options.wheelSensitivity;
19609 json.motionBlur = options.motionBlur;
19610 return json;
19611 }
19612 }
19613 });
19614 corefn$9.$id = corefn$9.getElementById;
19615 [corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
19616 extend(corefn$9, props);
19617 });
19618
19619 /* eslint-disable no-unused-vars */
19620
19621 var defaults$9 = {
19622 fit: true,
19623 // whether to fit the viewport to the graph
19624 directed: false,
19625 // whether the tree is directed downwards (or edges can point in any direction if false)
19626 padding: 30,
19627 // padding on fit
19628 circle: false,
19629 // put depths in concentric circles if true, put depths top down if false
19630 grid: false,
19631 // whether to create an even grid into which the DAG is placed (circle:false only)
19632 spacingFactor: 1.75,
19633 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19634 boundingBox: undefined,
19635 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19636 avoidOverlap: true,
19637 // prevents node overlap, may overflow boundingBox if not enough space
19638 nodeDimensionsIncludeLabels: false,
19639 // Excludes the label when calculating node bounding boxes for the layout algorithm
19640 roots: undefined,
19641 // the roots of the trees
19642 maximal: false,
19643 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19644 animate: false,
19645 // whether to transition the node positions
19646 animationDuration: 500,
19647 // duration of animation in ms if enabled
19648 animationEasing: undefined,
19649 // easing of animation if enabled,
19650 animateFilter: function animateFilter(node, i) {
19651 return true;
19652 },
19653 // 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
19654 ready: undefined,
19655 // callback on layoutready
19656 stop: undefined,
19657 // callback on layoutstop
19658 transform: function transform(node, position) {
19659 return position;
19660 } // transform a given node position. Useful for changing flow direction in discrete layouts
19661
19662 };
19663 /* eslint-enable */
19664
19665 var getInfo = function getInfo(ele) {
19666 return ele.scratch('breadthfirst');
19667 };
19668
19669 var setInfo = function setInfo(ele, obj) {
19670 return ele.scratch('breadthfirst', obj);
19671 };
19672
19673 function BreadthFirstLayout(options) {
19674 this.options = extend({}, defaults$9, options);
19675 }
19676
19677 BreadthFirstLayout.prototype.run = function () {
19678 var params = this.options;
19679 var options = params;
19680 var cy = params.cy;
19681 var eles = options.eles;
19682 var nodes = eles.nodes().filter(function (n) {
19683 return !n.isParent();
19684 });
19685 var graph = eles;
19686 var directed = options.directed;
19687 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19688
19689 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19690 x1: 0,
19691 y1: 0,
19692 w: cy.width(),
19693 h: cy.height()
19694 });
19695 var roots;
19696
19697 if (elementOrCollection(options.roots)) {
19698 roots = options.roots;
19699 } else if (array(options.roots)) {
19700 var rootsArray = [];
19701
19702 for (var i = 0; i < options.roots.length; i++) {
19703 var id = options.roots[i];
19704 var ele = cy.getElementById(id);
19705 rootsArray.push(ele);
19706 }
19707
19708 roots = cy.collection(rootsArray);
19709 } else if (string(options.roots)) {
19710 roots = cy.$(options.roots);
19711 } else {
19712 if (directed) {
19713 roots = nodes.roots();
19714 } else {
19715 var components = eles.components();
19716 roots = cy.collection();
19717
19718 var _loop = function _loop(_i) {
19719 var comp = components[_i];
19720 var maxDegree = comp.maxDegree(false);
19721 var compRoots = comp.filter(function (ele) {
19722 return ele.degree(false) === maxDegree;
19723 });
19724 roots = roots.add(compRoots);
19725 };
19726
19727 for (var _i = 0; _i < components.length; _i++) {
19728 _loop(_i);
19729 }
19730 }
19731 }
19732
19733 var depths = [];
19734 var foundByBfs = {};
19735
19736 var addToDepth = function addToDepth(ele, d) {
19737 if (depths[d] == null) {
19738 depths[d] = [];
19739 }
19740
19741 var i = depths[d].length;
19742 depths[d].push(ele);
19743 setInfo(ele, {
19744 index: i,
19745 depth: d
19746 });
19747 };
19748
19749 var changeDepth = function changeDepth(ele, newDepth) {
19750 var _getInfo = getInfo(ele),
19751 depth = _getInfo.depth,
19752 index = _getInfo.index;
19753
19754 depths[depth][index] = null;
19755 addToDepth(ele, newDepth);
19756 }; // find the depths of the nodes
19757
19758
19759 graph.bfs({
19760 roots: roots,
19761 directed: options.directed,
19762 visit: function visit(node, edge, pNode, i, depth) {
19763 var ele = node[0];
19764 var id = ele.id();
19765 addToDepth(ele, depth);
19766 foundByBfs[id] = true;
19767 }
19768 }); // check for nodes not found by bfs
19769
19770 var orphanNodes = [];
19771
19772 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19773 var _ele = nodes[_i2];
19774
19775 if (foundByBfs[_ele.id()]) {
19776 continue;
19777 } else {
19778 orphanNodes.push(_ele);
19779 }
19780 } // assign the nodes a depth and index
19781
19782
19783 var assignDepthsAt = function assignDepthsAt(i) {
19784 var eles = depths[i];
19785
19786 for (var j = 0; j < eles.length; j++) {
19787 var _ele2 = eles[j];
19788
19789 if (_ele2 == null) {
19790 eles.splice(j, 1);
19791 j--;
19792 continue;
19793 }
19794
19795 setInfo(_ele2, {
19796 depth: i,
19797 index: j
19798 });
19799 }
19800 };
19801
19802 var assignDepths = function assignDepths() {
19803 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19804 assignDepthsAt(_i3);
19805 }
19806 };
19807
19808 var adjustMaximally = function adjustMaximally(ele, shifted) {
19809 var eInfo = getInfo(ele);
19810 var incomers = ele.incomers().filter(function (el) {
19811 return el.isNode() && eles.has(el);
19812 });
19813 var maxDepth = -1;
19814 var id = ele.id();
19815
19816 for (var k = 0; k < incomers.length; k++) {
19817 var incmr = incomers[k];
19818 var iInfo = getInfo(incmr);
19819 maxDepth = Math.max(maxDepth, iInfo.depth);
19820 }
19821
19822 if (eInfo.depth <= maxDepth) {
19823 if (shifted[id]) {
19824 return null;
19825 }
19826
19827 changeDepth(ele, maxDepth + 1);
19828 shifted[id] = true;
19829 return true;
19830 }
19831
19832 return false;
19833 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19834
19835
19836 if (directed && maximal) {
19837 var Q = [];
19838 var shifted = {};
19839
19840 var enqueue = function enqueue(n) {
19841 return Q.push(n);
19842 };
19843
19844 var dequeue = function dequeue() {
19845 return Q.shift();
19846 };
19847
19848 nodes.forEach(function (n) {
19849 return Q.push(n);
19850 });
19851
19852 while (Q.length > 0) {
19853 var _ele3 = dequeue();
19854
19855 var didShift = adjustMaximally(_ele3, shifted);
19856
19857 if (didShift) {
19858 _ele3.outgoers().filter(function (el) {
19859 return el.isNode() && eles.has(el);
19860 }).forEach(enqueue);
19861 } else if (didShift === null) {
19862 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19863 break; // exit on failure
19864 }
19865 }
19866 }
19867
19868 assignDepths(); // clear holes
19869 // find min distance we need to leave between nodes
19870
19871 var minDistance = 0;
19872
19873 if (options.avoidOverlap) {
19874 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19875 var n = nodes[_i4];
19876 var nbb = n.layoutDimensions(options);
19877 var w = nbb.w;
19878 var h = nbb.h;
19879 minDistance = Math.max(minDistance, w, h);
19880 }
19881 } // get the weighted percent for an element based on its connectivity to other levels
19882
19883
19884 var cachedWeightedPercent = {};
19885
19886 var getWeightedPercent = function getWeightedPercent(ele) {
19887 if (cachedWeightedPercent[ele.id()]) {
19888 return cachedWeightedPercent[ele.id()];
19889 }
19890
19891 var eleDepth = getInfo(ele).depth;
19892 var neighbors = ele.neighborhood();
19893 var percent = 0;
19894 var samples = 0;
19895
19896 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19897 var neighbor = neighbors[_i5];
19898
19899 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19900 continue;
19901 }
19902
19903 var bf = getInfo(neighbor);
19904 var index = bf.index;
19905 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19906
19907 if (index == null || depth == null) {
19908 continue;
19909 }
19910
19911 var nDepth = depths[depth].length;
19912
19913 if (depth < eleDepth) {
19914 // only get influenced by elements above
19915 percent += index / nDepth;
19916 samples++;
19917 }
19918 }
19919
19920 samples = Math.max(1, samples);
19921 percent = percent / samples;
19922
19923 if (samples === 0) {
19924 // put lone nodes at the start
19925 percent = 0;
19926 }
19927
19928 cachedWeightedPercent[ele.id()] = percent;
19929 return percent;
19930 }; // rearrange the indices in each depth level based on connectivity
19931
19932
19933 var sortFn = function sortFn(a, b) {
19934 var apct = getWeightedPercent(a);
19935 var bpct = getWeightedPercent(b);
19936 var diff = apct - bpct;
19937
19938 if (diff === 0) {
19939 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19940 } else {
19941 return diff;
19942 }
19943 }; // sort each level to make connected nodes closer
19944
19945
19946 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19947 depths[_i6].sort(sortFn);
19948
19949 assignDepthsAt(_i6);
19950 } // assign orphan nodes to a new top-level depth
19951
19952
19953 var orphanDepth = [];
19954
19955 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19956 orphanDepth.push(orphanNodes[_i7]);
19957 }
19958
19959 depths.unshift(orphanDepth);
19960 assignDepths();
19961 var biggestDepthSize = 0;
19962
19963 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19964 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19965 }
19966
19967 var center = {
19968 x: bb.x1 + bb.w / 2,
19969 y: bb.x1 + bb.h / 2
19970 };
19971 var maxDepthSize = depths.reduce(function (max, eles) {
19972 return Math.max(max, eles.length);
19973 }, 0);
19974
19975 var getPosition = function getPosition(ele) {
19976 var _getInfo2 = getInfo(ele),
19977 depth = _getInfo2.depth,
19978 index = _getInfo2.index;
19979
19980 var depthSize = depths[depth].length;
19981 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19982 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19983 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19984 radiusStepSize = Math.max(radiusStepSize, minDistance);
19985
19986 if (!options.circle) {
19987 var epos = {
19988 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19989 y: (depth + 1) * distanceY
19990 };
19991 return epos;
19992 } else {
19993 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19994 var theta = 2 * Math.PI / depths[depth].length * index;
19995
19996 if (depth === 0 && depths[0].length === 1) {
19997 radius = 1;
19998 }
19999
20000 return {
20001 x: center.x + radius * Math.cos(theta),
20002 y: center.y + radius * Math.sin(theta)
20003 };
20004 }
20005 };
20006
20007 nodes.layoutPositions(this, options, getPosition);
20008 return this; // chaining
20009 };
20010
20011 var defaults$a = {
20012 fit: true,
20013 // whether to fit the viewport to the graph
20014 padding: 30,
20015 // the padding on fit
20016 boundingBox: undefined,
20017 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20018 avoidOverlap: true,
20019 // prevents node overlap, may overflow boundingBox and radius if not enough space
20020 nodeDimensionsIncludeLabels: false,
20021 // Excludes the label when calculating node bounding boxes for the layout algorithm
20022 spacingFactor: undefined,
20023 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20024 radius: undefined,
20025 // the radius of the circle
20026 startAngle: 3 / 2 * Math.PI,
20027 // where nodes start in radians
20028 sweep: undefined,
20029 // how many radians should be between the first and last node (defaults to full circle)
20030 clockwise: true,
20031 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
20032 sort: undefined,
20033 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20034 animate: false,
20035 // whether to transition the node positions
20036 animationDuration: 500,
20037 // duration of animation in ms if enabled
20038 animationEasing: undefined,
20039 // easing of animation if enabled
20040 animateFilter: function animateFilter(node, i) {
20041 return true;
20042 },
20043 // 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
20044 ready: undefined,
20045 // callback on layoutready
20046 stop: undefined,
20047 // callback on layoutstop
20048 transform: function transform(node, position) {
20049 return position;
20050 } // transform a given node position. Useful for changing flow direction in discrete layouts
20051
20052 };
20053
20054 function CircleLayout(options) {
20055 this.options = extend({}, defaults$a, options);
20056 }
20057
20058 CircleLayout.prototype.run = function () {
20059 var params = this.options;
20060 var options = params;
20061 var cy = params.cy;
20062 var eles = options.eles;
20063 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
20064 var nodes = eles.nodes().not(':parent');
20065
20066 if (options.sort) {
20067 nodes = nodes.sort(options.sort);
20068 }
20069
20070 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20071 x1: 0,
20072 y1: 0,
20073 w: cy.width(),
20074 h: cy.height()
20075 });
20076 var center = {
20077 x: bb.x1 + bb.w / 2,
20078 y: bb.y1 + bb.h / 2
20079 };
20080 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
20081 var dTheta = sweep / Math.max(1, nodes.length - 1);
20082 var r;
20083 var minDistance = 0;
20084
20085 for (var i = 0; i < nodes.length; i++) {
20086 var n = nodes[i];
20087 var nbb = n.layoutDimensions(options);
20088 var w = nbb.w;
20089 var h = nbb.h;
20090 minDistance = Math.max(minDistance, w, h);
20091 }
20092
20093 if (number(options.radius)) {
20094 r = options.radius;
20095 } else if (nodes.length <= 1) {
20096 r = 0;
20097 } else {
20098 r = Math.min(bb.h, bb.w) / 2 - minDistance;
20099 } // calculate the radius
20100
20101
20102 if (nodes.length > 1 && options.avoidOverlap) {
20103 // but only if more than one node (can't overlap)
20104 minDistance *= 1.75; // just to have some nice spacing
20105
20106 var dcos = Math.cos(dTheta) - Math.cos(0);
20107 var dsin = Math.sin(dTheta) - Math.sin(0);
20108 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
20109
20110 r = Math.max(rMin, r);
20111 }
20112
20113 var getPos = function getPos(ele, i) {
20114 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
20115 var rx = r * Math.cos(theta);
20116 var ry = r * Math.sin(theta);
20117 var pos = {
20118 x: center.x + rx,
20119 y: center.y + ry
20120 };
20121 return pos;
20122 };
20123
20124 nodes.layoutPositions(this, options, getPos);
20125 return this; // chaining
20126 };
20127
20128 var defaults$b = {
20129 fit: true,
20130 // whether to fit the viewport to the graph
20131 padding: 30,
20132 // the padding on fit
20133 startAngle: 3 / 2 * Math.PI,
20134 // where nodes start in radians
20135 sweep: undefined,
20136 // how many radians should be between the first and last node (defaults to full circle)
20137 clockwise: true,
20138 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
20139 equidistant: false,
20140 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
20141 minNodeSpacing: 10,
20142 // min spacing between outside of nodes (used for radius adjustment)
20143 boundingBox: undefined,
20144 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20145 avoidOverlap: true,
20146 // prevents node overlap, may overflow boundingBox if not enough space
20147 nodeDimensionsIncludeLabels: false,
20148 // Excludes the label when calculating node bounding boxes for the layout algorithm
20149 height: undefined,
20150 // height of layout area (overrides container height)
20151 width: undefined,
20152 // width of layout area (overrides container width)
20153 spacingFactor: undefined,
20154 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20155 concentric: function concentric(node) {
20156 // returns numeric value for each node, placing higher nodes in levels towards the centre
20157 return node.degree();
20158 },
20159 levelWidth: function levelWidth(nodes) {
20160 // the letiation of concentric values in each level
20161 return nodes.maxDegree() / 4;
20162 },
20163 animate: false,
20164 // whether to transition the node positions
20165 animationDuration: 500,
20166 // duration of animation in ms if enabled
20167 animationEasing: undefined,
20168 // easing of animation if enabled
20169 animateFilter: function animateFilter(node, i) {
20170 return true;
20171 },
20172 // 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
20173 ready: undefined,
20174 // callback on layoutready
20175 stop: undefined,
20176 // callback on layoutstop
20177 transform: function transform(node, position) {
20178 return position;
20179 } // transform a given node position. Useful for changing flow direction in discrete layouts
20180
20181 };
20182
20183 function ConcentricLayout(options) {
20184 this.options = extend({}, defaults$b, options);
20185 }
20186
20187 ConcentricLayout.prototype.run = function () {
20188 var params = this.options;
20189 var options = params;
20190 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
20191 var cy = params.cy;
20192 var eles = options.eles;
20193 var nodes = eles.nodes().not(':parent');
20194 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20195 x1: 0,
20196 y1: 0,
20197 w: cy.width(),
20198 h: cy.height()
20199 });
20200 var center = {
20201 x: bb.x1 + bb.w / 2,
20202 y: bb.y1 + bb.h / 2
20203 };
20204 var nodeValues = []; // { node, value }
20205
20206 var maxNodeSize = 0;
20207
20208 for (var i = 0; i < nodes.length; i++) {
20209 var node = nodes[i];
20210 var value = void 0; // calculate the node value
20211
20212 value = options.concentric(node);
20213 nodeValues.push({
20214 value: value,
20215 node: node
20216 }); // for style mapping
20217
20218 node._private.scratch.concentric = value;
20219 } // in case we used the `concentric` in style
20220
20221
20222 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
20223
20224 for (var _i = 0; _i < nodes.length; _i++) {
20225 var _node = nodes[_i];
20226
20227 var nbb = _node.layoutDimensions(options);
20228
20229 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
20230 } // sort node values in descreasing order
20231
20232
20233 nodeValues.sort(function (a, b) {
20234 return b.value - a.value;
20235 });
20236 var levelWidth = options.levelWidth(nodes); // put the values into levels
20237
20238 var levels = [[]];
20239 var currentLevel = levels[0];
20240
20241 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
20242 var val = nodeValues[_i2];
20243
20244 if (currentLevel.length > 0) {
20245 var diff = Math.abs(currentLevel[0].value - val.value);
20246
20247 if (diff >= levelWidth) {
20248 currentLevel = [];
20249 levels.push(currentLevel);
20250 }
20251 }
20252
20253 currentLevel.push(val);
20254 } // create positions from levels
20255
20256
20257 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
20258
20259 if (!options.avoidOverlap) {
20260 // then strictly constrain to bb
20261 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
20262 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
20263 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
20264 minDist = Math.min(minDist, rStep);
20265 } // find the metrics for each level
20266
20267
20268 var r = 0;
20269
20270 for (var _i3 = 0; _i3 < levels.length; _i3++) {
20271 var level = levels[_i3];
20272 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
20273 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
20274
20275 if (level.length > 1 && options.avoidOverlap) {
20276 // but only if more than one node (can't overlap)
20277 var dcos = Math.cos(dTheta) - Math.cos(0);
20278 var dsin = Math.sin(dTheta) - Math.sin(0);
20279 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
20280
20281 r = Math.max(rMin, r);
20282 }
20283
20284 level.r = r;
20285 r += minDist;
20286 }
20287
20288 if (options.equidistant) {
20289 var rDeltaMax = 0;
20290 var _r = 0;
20291
20292 for (var _i4 = 0; _i4 < levels.length; _i4++) {
20293 var _level = levels[_i4];
20294 var rDelta = _level.r - _r;
20295 rDeltaMax = Math.max(rDeltaMax, rDelta);
20296 }
20297
20298 _r = 0;
20299
20300 for (var _i5 = 0; _i5 < levels.length; _i5++) {
20301 var _level2 = levels[_i5];
20302
20303 if (_i5 === 0) {
20304 _r = _level2.r;
20305 }
20306
20307 _level2.r = _r;
20308 _r += rDeltaMax;
20309 }
20310 } // calculate the node positions
20311
20312
20313 var pos = {}; // id => position
20314
20315 for (var _i6 = 0; _i6 < levels.length; _i6++) {
20316 var _level3 = levels[_i6];
20317 var _dTheta = _level3.dTheta;
20318 var _r2 = _level3.r;
20319
20320 for (var j = 0; j < _level3.length; j++) {
20321 var _val = _level3[j];
20322 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
20323 var p = {
20324 x: center.x + _r2 * Math.cos(theta),
20325 y: center.y + _r2 * Math.sin(theta)
20326 };
20327 pos[_val.node.id()] = p;
20328 }
20329 } // position the nodes
20330
20331
20332 nodes.layoutPositions(this, options, function (ele) {
20333 var id = ele.id();
20334 return pos[id];
20335 });
20336 return this; // chaining
20337 };
20338
20339 /*
20340 The CoSE layout was written by Gerardo Huck.
20341 https://www.linkedin.com/in/gerardohuck/
20342
20343 Based on the following article:
20344 http://dl.acm.org/citation.cfm?id=1498047
20345
20346 Modifications tracked on Github.
20347 */
20348 var DEBUG;
20349 /**
20350 * @brief : default layout options
20351 */
20352
20353 var defaults$c = {
20354 // Called on `layoutready`
20355 ready: function ready() {},
20356 // Called on `layoutstop`
20357 stop: function stop() {},
20358 // Whether to animate while running the layout
20359 // true : Animate continuously as the layout is running
20360 // false : Just show the end result
20361 // 'end' : Animate with the end result, from the initial positions to the end positions
20362 animate: true,
20363 // Easing of the animation for animate:'end'
20364 animationEasing: undefined,
20365 // The duration of the animation for animate:'end'
20366 animationDuration: undefined,
20367 // A function that determines whether the node should be animated
20368 // All nodes animated by default on animate enabled
20369 // Non-animated nodes are positioned immediately when the layout starts
20370 animateFilter: function animateFilter(node, i) {
20371 return true;
20372 },
20373 // The layout animates only after this many milliseconds for animate:true
20374 // (prevents flashing on fast runs)
20375 animationThreshold: 250,
20376 // Number of iterations between consecutive screen positions update
20377 refresh: 20,
20378 // Whether to fit the network view after when done
20379 fit: true,
20380 // Padding on fit
20381 padding: 30,
20382 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20383 boundingBox: undefined,
20384 // Excludes the label when calculating node bounding boxes for the layout algorithm
20385 nodeDimensionsIncludeLabels: false,
20386 // Randomize the initial positions of the nodes (true) or use existing positions (false)
20387 randomize: false,
20388 // Extra spacing between components in non-compound graphs
20389 componentSpacing: 40,
20390 // Node repulsion (non overlapping) multiplier
20391 nodeRepulsion: function nodeRepulsion(node) {
20392 return 2048;
20393 },
20394 // Node repulsion (overlapping) multiplier
20395 nodeOverlap: 4,
20396 // Ideal edge (non nested) length
20397 idealEdgeLength: function idealEdgeLength(edge) {
20398 return 32;
20399 },
20400 // Divisor to compute edge forces
20401 edgeElasticity: function edgeElasticity(edge) {
20402 return 32;
20403 },
20404 // Nesting factor (multiplier) to compute ideal edge length for nested edges
20405 nestingFactor: 1.2,
20406 // Gravity force (constant)
20407 gravity: 1,
20408 // Maximum number of iterations to perform
20409 numIter: 1000,
20410 // Initial temperature (maximum node displacement)
20411 initialTemp: 1000,
20412 // Cooling factor (how the temperature is reduced between consecutive iterations
20413 coolingFactor: 0.99,
20414 // Lower temperature threshold (below this point the layout will end)
20415 minTemp: 1.0
20416 };
20417 /**
20418 * @brief : constructor
20419 * @arg options : object containing layout options
20420 */
20421
20422 function CoseLayout(options) {
20423 this.options = extend({}, defaults$c, options);
20424 this.options.layout = this;
20425 }
20426 /**
20427 * @brief : runs the layout
20428 */
20429
20430
20431 CoseLayout.prototype.run = function () {
20432 var options = this.options;
20433 var cy = options.cy;
20434 var layout = this;
20435 layout.stopped = false;
20436
20437 if (options.animate === true || options.animate === false) {
20438 layout.emit({
20439 type: 'layoutstart',
20440 layout: layout
20441 });
20442 } // Set DEBUG - Global variable
20443
20444
20445 if (true === options.debug) {
20446 DEBUG = true;
20447 } else {
20448 DEBUG = false;
20449 } // Initialize layout info
20450
20451
20452 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
20453
20454 if (DEBUG) {
20455 printLayoutInfo(layoutInfo);
20456 } // If required, randomize node positions
20457
20458
20459 if (options.randomize) {
20460 randomizePositions(layoutInfo);
20461 }
20462
20463 var startTime = performanceNow();
20464
20465 var refresh = function refresh() {
20466 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
20467
20468 if (true === options.fit) {
20469 cy.fit(options.padding);
20470 }
20471 };
20472
20473 var mainLoop = function mainLoop(i) {
20474 if (layout.stopped || i >= options.numIter) {
20475 // logDebug("Layout manually stopped. Stopping computation in step " + i);
20476 return false;
20477 } // Do one step in the phisical simulation
20478
20479
20480 step$1(layoutInfo, options); // Update temperature
20481
20482 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
20483
20484 if (layoutInfo.temperature < options.minTemp) {
20485 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
20486 return false;
20487 }
20488
20489 return true;
20490 };
20491
20492 var done = function done() {
20493 if (options.animate === true || options.animate === false) {
20494 refresh(); // Layout has finished
20495
20496 layout.one('layoutstop', options.stop);
20497 layout.emit({
20498 type: 'layoutstop',
20499 layout: layout
20500 });
20501 } else {
20502 var nodes = options.eles.nodes();
20503 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20504 nodes.layoutPositions(layout, options, getScaledPos);
20505 }
20506 };
20507
20508 var i = 0;
20509 var loopRet = true;
20510
20511 if (options.animate === true) {
20512 var frame = function frame() {
20513 var f = 0;
20514
20515 while (loopRet && f < options.refresh) {
20516 loopRet = mainLoop(i);
20517 i++;
20518 f++;
20519 }
20520
20521 if (!loopRet) {
20522 // it's done
20523 separateComponents(layoutInfo, options);
20524 done();
20525 } else {
20526 var now = performanceNow();
20527
20528 if (now - startTime >= options.animationThreshold) {
20529 refresh();
20530 }
20531
20532 requestAnimationFrame(frame);
20533 }
20534 };
20535
20536 frame();
20537 } else {
20538 while (loopRet) {
20539 loopRet = mainLoop(i);
20540 i++;
20541 }
20542
20543 separateComponents(layoutInfo, options);
20544 done();
20545 }
20546
20547 return this; // chaining
20548 };
20549 /**
20550 * @brief : called on continuous layouts to stop them before they finish
20551 */
20552
20553
20554 CoseLayout.prototype.stop = function () {
20555 this.stopped = true;
20556
20557 if (this.thread) {
20558 this.thread.stop();
20559 }
20560
20561 this.emit('layoutstop');
20562 return this; // chaining
20563 };
20564
20565 CoseLayout.prototype.destroy = function () {
20566 if (this.thread) {
20567 this.thread.stop();
20568 }
20569
20570 return this; // chaining
20571 };
20572 /**
20573 * @brief : Creates an object which is contains all the data
20574 * used in the layout process
20575 * @arg cy : cytoscape.js object
20576 * @return : layoutInfo object initialized
20577 */
20578
20579
20580 var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20581 // Shortcut
20582 var edges = options.eles.edges();
20583 var nodes = options.eles.nodes();
20584 var layoutInfo = {
20585 isCompound: cy.hasCompoundNodes(),
20586 layoutNodes: [],
20587 idToIndex: {},
20588 nodeSize: nodes.size(),
20589 graphSet: [],
20590 indexToGraph: [],
20591 layoutEdges: [],
20592 edgeSize: edges.size(),
20593 temperature: options.initialTemp,
20594 clientWidth: cy.width(),
20595 clientHeight: cy.width(),
20596 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
20597 x1: 0,
20598 y1: 0,
20599 w: cy.width(),
20600 h: cy.height()
20601 })
20602 };
20603 var components = options.eles.components();
20604 var id2cmptId = {};
20605
20606 for (var i = 0; i < components.length; i++) {
20607 var component = components[i];
20608
20609 for (var j = 0; j < component.length; j++) {
20610 var node = component[j];
20611 id2cmptId[node.id()] = i;
20612 }
20613 } // Iterate over all nodes, creating layout nodes
20614
20615
20616 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20617 var n = nodes[i];
20618 var nbb = n.layoutDimensions(options);
20619 var tempNode = {};
20620 tempNode.isLocked = n.locked();
20621 tempNode.id = n.data('id');
20622 tempNode.parentId = n.data('parent');
20623 tempNode.cmptId = id2cmptId[n.id()];
20624 tempNode.children = [];
20625 tempNode.positionX = n.position('x');
20626 tempNode.positionY = n.position('y');
20627 tempNode.offsetX = 0;
20628 tempNode.offsetY = 0;
20629 tempNode.height = nbb.w;
20630 tempNode.width = nbb.h;
20631 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20632 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20633 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20634 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20635 tempNode.padLeft = parseFloat(n.style('padding'));
20636 tempNode.padRight = parseFloat(n.style('padding'));
20637 tempNode.padTop = parseFloat(n.style('padding'));
20638 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20639
20640 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20641
20642 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20643
20644 layoutInfo.idToIndex[tempNode.id] = i;
20645 } // Inline implementation of a queue, used for traversing the graph in BFS order
20646
20647
20648 var queue = [];
20649 var start = 0; // Points to the start the queue
20650
20651 var end = -1; // Points to the end of the queue
20652
20653 var tempGraph = []; // Second pass to add child information and
20654 // initialize queue for hierarchical traversal
20655
20656 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20657 var n = layoutInfo.layoutNodes[i];
20658 var p_id = n.parentId; // Check if node n has a parent node
20659
20660 if (null != p_id) {
20661 // Add node Id to parent's list of children
20662 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20663 } else {
20664 // If a node doesn't have a parent, then it's in the root graph
20665 queue[++end] = n.id;
20666 tempGraph.push(n.id);
20667 }
20668 } // Add root graph to graphSet
20669
20670
20671 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20672
20673 while (start <= end) {
20674 // Get the node to visit and remove it from queue
20675 var node_id = queue[start++];
20676 var node_ix = layoutInfo.idToIndex[node_id];
20677 var node = layoutInfo.layoutNodes[node_ix];
20678 var children = node.children;
20679
20680 if (children.length > 0) {
20681 // Add children nodes as a new graph to graph set
20682 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20683
20684 for (var i = 0; i < children.length; i++) {
20685 queue[++end] = children[i];
20686 }
20687 }
20688 } // Create indexToGraph map
20689
20690
20691 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20692 var graph = layoutInfo.graphSet[i];
20693
20694 for (var j = 0; j < graph.length; j++) {
20695 var index = layoutInfo.idToIndex[graph[j]];
20696 layoutInfo.indexToGraph[index] = i;
20697 }
20698 } // Iterate over all edges, creating Layout Edges
20699
20700
20701 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20702 var e = edges[i];
20703 var tempEdge = {};
20704 tempEdge.id = e.data('id');
20705 tempEdge.sourceId = e.data('source');
20706 tempEdge.targetId = e.data('target'); // Compute ideal length
20707
20708 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20709 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20710
20711 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20712 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20713 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20714 var targetGraph = layoutInfo.indexToGraph[targetIx];
20715
20716 if (sourceGraph != targetGraph) {
20717 // Find lowest common graph ancestor
20718 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20719
20720 var lcaGraph = layoutInfo.graphSet[lca];
20721 var depth = 0; // Source depth
20722
20723 var tempNode = layoutInfo.layoutNodes[sourceIx];
20724
20725 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20726 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20727 depth++;
20728 } // Target depth
20729
20730
20731 tempNode = layoutInfo.layoutNodes[targetIx];
20732
20733 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20734 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20735 depth++;
20736 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20737 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20738 // ". Depth: " + depth);
20739 // Update idealLength
20740
20741
20742 idealLength *= depth * options.nestingFactor;
20743 }
20744
20745 tempEdge.idealLength = idealLength;
20746 tempEdge.elasticity = elasticity;
20747 layoutInfo.layoutEdges.push(tempEdge);
20748 } // Finally, return layoutInfo object
20749
20750
20751 return layoutInfo;
20752 };
20753 /**
20754 * @brief : This function finds the index of the lowest common
20755 * graph ancestor between 2 nodes in the subtree
20756 * (from the graph hierarchy induced tree) whose
20757 * root is graphIx
20758 *
20759 * @arg node1: node1's ID
20760 * @arg node2: node2's ID
20761 * @arg layoutInfo: layoutInfo object
20762 *
20763 */
20764
20765
20766 var findLCA = function findLCA(node1, node2, layoutInfo) {
20767 // Find their common ancester, starting from the root graph
20768 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20769
20770 if (2 > res.count) {
20771 // If aux function couldn't find the common ancester,
20772 // then it is the root graph
20773 return 0;
20774 } else {
20775 return res.graph;
20776 }
20777 };
20778 /**
20779 * @brief : Auxiliary function used for LCA computation
20780 *
20781 * @arg node1 : node1's ID
20782 * @arg node2 : node2's ID
20783 * @arg graphIx : subgraph index
20784 * @arg layoutInfo : layoutInfo object
20785 *
20786 * @return : object of the form {count: X, graph: Y}, where:
20787 * X is the number of ancesters (max: 2) found in
20788 * graphIx (and it's subgraphs),
20789 * Y is the graph index of the lowest graph containing
20790 * all X nodes
20791 */
20792
20793
20794 var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20795 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20796
20797 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20798 return {
20799 count: 2,
20800 graph: graphIx
20801 };
20802 } // Make recursive calls for all subgraphs
20803
20804
20805 var c = 0;
20806
20807 for (var i = 0; i < graph.length; i++) {
20808 var nodeId = graph[i];
20809 var nodeIx = layoutInfo.idToIndex[nodeId];
20810 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20811
20812 if (0 === children.length) {
20813 continue;
20814 }
20815
20816 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20817 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20818
20819 if (0 === result.count) {
20820 // Neither node1 nor node2 are present in this subgraph
20821 continue;
20822 } else if (1 === result.count) {
20823 // One of (node1, node2) is present in this subgraph
20824 c++;
20825
20826 if (2 === c) {
20827 // We've already found both nodes, no need to keep searching
20828 break;
20829 }
20830 } else {
20831 // Both nodes are present in this subgraph
20832 return result;
20833 }
20834 }
20835
20836 return {
20837 count: c,
20838 graph: graphIx
20839 };
20840 };
20841 /**
20842 * @brief: printsLayoutInfo into js console
20843 * Only used for debbuging
20844 */
20845
20846
20847 if (false) {
20848 var printLayoutInfo;
20849 }
20850 /**
20851 * @brief : Randomizes the position of all nodes
20852 */
20853
20854
20855 var randomizePositions = function randomizePositions(layoutInfo, cy) {
20856 var width = layoutInfo.clientWidth;
20857 var height = layoutInfo.clientHeight;
20858
20859 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20860 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20861
20862 if (0 === n.children.length && !n.isLocked) {
20863 n.positionX = Math.random() * width;
20864 n.positionY = Math.random() * height;
20865 }
20866 }
20867 };
20868
20869 var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20870 var bb = layoutInfo.boundingBox;
20871 var coseBB = {
20872 x1: Infinity,
20873 x2: -Infinity,
20874 y1: Infinity,
20875 y2: -Infinity
20876 };
20877
20878 if (options.boundingBox) {
20879 nodes.forEach(function (node) {
20880 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20881 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20882 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20883 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20884 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20885 });
20886 coseBB.w = coseBB.x2 - coseBB.x1;
20887 coseBB.h = coseBB.y2 - coseBB.y1;
20888 }
20889
20890 return function (ele, i) {
20891 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20892
20893 if (options.boundingBox) {
20894 // then add extra bounding box constraint
20895 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20896 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20897 return {
20898 x: bb.x1 + pctX * bb.w,
20899 y: bb.y1 + pctY * bb.h
20900 };
20901 } else {
20902 return {
20903 x: lnode.positionX,
20904 y: lnode.positionY
20905 };
20906 }
20907 };
20908 };
20909 /**
20910 * @brief : Updates the positions of nodes in the network
20911 * @arg layoutInfo : LayoutInfo object
20912 * @arg cy : Cytoscape object
20913 * @arg options : Layout options
20914 */
20915
20916
20917 var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20918 // var s = 'Refreshing positions';
20919 // logDebug(s);
20920 var layout = options.layout;
20921 var nodes = options.eles.nodes();
20922 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20923 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20924
20925 if (true !== layoutInfo.ready) {
20926 // s = 'Triggering layoutready';
20927 // logDebug(s);
20928 layoutInfo.ready = true;
20929 layout.one('layoutready', options.ready);
20930 layout.emit({
20931 type: 'layoutready',
20932 layout: this
20933 });
20934 }
20935 };
20936 /**
20937 * @brief : Logs a debug message in JS console, if DEBUG is ON
20938 */
20939 // var logDebug = function(text) {
20940 // if (DEBUG) {
20941 // console.debug(text);
20942 // }
20943 // };
20944
20945 /**
20946 * @brief : Performs one iteration of the physical simulation
20947 * @arg layoutInfo : LayoutInfo object already initialized
20948 * @arg cy : Cytoscape object
20949 * @arg options : Layout options
20950 */
20951
20952
20953 var step$1 = function step(layoutInfo, options, _step) {
20954 // var s = "\n\n###############################";
20955 // s += "\nSTEP: " + step;
20956 // s += "\n###############################\n";
20957 // logDebug(s);
20958 // Calculate node repulsions
20959 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20960
20961 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20962
20963 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20964
20965 propagateForces(layoutInfo); // Update positions based on calculated forces
20966
20967 updatePositions(layoutInfo);
20968 };
20969 /**
20970 * @brief : Computes the node repulsion forces
20971 */
20972
20973
20974 var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20975 // Go through each of the graphs in graphSet
20976 // Nodes only repel each other if they belong to the same graph
20977 // var s = 'calculateNodeForces';
20978 // logDebug(s);
20979 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20980 var graph = layoutInfo.graphSet[i];
20981 var numNodes = graph.length; // s = "Set: " + graph.toString();
20982 // logDebug(s);
20983 // Now get all the pairs of nodes
20984 // Only get each pair once, (A, B) = (B, A)
20985
20986 for (var j = 0; j < numNodes; j++) {
20987 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20988
20989 for (var k = j + 1; k < numNodes; k++) {
20990 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20991 nodeRepulsion(node1, node2, layoutInfo, options);
20992 }
20993 }
20994 }
20995 };
20996
20997 var randomDistance = function randomDistance(max) {
20998 return -max + 2 * max * Math.random();
20999 };
21000 /**
21001 * @brief : Compute the node repulsion forces between a pair of nodes
21002 */
21003
21004
21005 var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
21006 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
21007 var cmptId1 = node1.cmptId;
21008 var cmptId2 = node2.cmptId;
21009
21010 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
21011 return;
21012 } // Get direction of line connecting both node centers
21013
21014
21015 var directionX = node2.positionX - node1.positionX;
21016 var directionY = node2.positionY - node1.positionY;
21017 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
21018 // If both centers are the same, apply a random force
21019
21020 if (0 === directionX && 0 === directionY) {
21021 directionX = randomDistance(maxRandDist);
21022 directionY = randomDistance(maxRandDist);
21023 }
21024
21025 var overlap = nodesOverlap(node1, node2, directionX, directionY);
21026
21027 if (overlap > 0) {
21028 // s += "\nNodes DO overlap.";
21029 // s += "\nOverlap: " + overlap;
21030 // If nodes overlap, repulsion force is proportional
21031 // to the overlap
21032 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
21033
21034 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
21035
21036 var forceX = force * directionX / distance;
21037 var forceY = force * directionY / distance;
21038 } else {
21039 // s += "\nNodes do NOT overlap.";
21040 // If there's no overlap, force is inversely proportional
21041 // to squared distance
21042 // Get clipping points for both nodes
21043 var point1 = findClippingPoint(node1, directionX, directionY);
21044 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
21045
21046 var distanceX = point2.x - point1.x;
21047 var distanceY = point2.y - point1.y;
21048 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
21049 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
21050 // Compute the module and components of the force vector
21051
21052 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
21053 var forceX = force * distanceX / distance;
21054 var forceY = force * distanceY / distance;
21055 } // Apply force
21056
21057
21058 if (!node1.isLocked) {
21059 node1.offsetX -= forceX;
21060 node1.offsetY -= forceY;
21061 }
21062
21063 if (!node2.isLocked) {
21064 node2.offsetX += forceX;
21065 node2.offsetY += forceY;
21066 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
21067 // logDebug(s);
21068
21069
21070 return;
21071 };
21072 /**
21073 * @brief : Determines whether two nodes overlap or not
21074 * @return : Amount of overlapping (0 => no overlap)
21075 */
21076
21077
21078 var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
21079 if (dX > 0) {
21080 var overlapX = node1.maxX - node2.minX;
21081 } else {
21082 var overlapX = node2.maxX - node1.minX;
21083 }
21084
21085 if (dY > 0) {
21086 var overlapY = node1.maxY - node2.minY;
21087 } else {
21088 var overlapY = node2.maxY - node1.minY;
21089 }
21090
21091 if (overlapX >= 0 && overlapY >= 0) {
21092 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
21093 } else {
21094 return 0;
21095 }
21096 };
21097 /**
21098 * @brief : Finds the point in which an edge (direction dX, dY) intersects
21099 * the rectangular bounding box of it's source/target node
21100 */
21101
21102
21103 var findClippingPoint = function findClippingPoint(node, dX, dY) {
21104 // Shorcuts
21105 var X = node.positionX;
21106 var Y = node.positionY;
21107 var H = node.height || 1;
21108 var W = node.width || 1;
21109 var dirSlope = dY / dX;
21110 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
21111 // " . Height: " + H + ", Width: " + W +
21112 // "\nDirection " + dX + ", " + dY;
21113 //
21114 // Compute intersection
21115
21116 var res = {}; // Case: Vertical direction (up)
21117
21118 if (0 === dX && 0 < dY) {
21119 res.x = X; // s += "\nUp direction";
21120
21121 res.y = Y + H / 2;
21122 return res;
21123 } // Case: Vertical direction (down)
21124
21125
21126 if (0 === dX && 0 > dY) {
21127 res.x = X;
21128 res.y = Y + H / 2; // s += "\nDown direction";
21129
21130 return res;
21131 } // Case: Intersects the right border
21132
21133
21134 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
21135 res.x = X + W / 2;
21136 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
21137
21138 return res;
21139 } // Case: Intersects the left border
21140
21141
21142 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
21143 res.x = X - W / 2;
21144 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
21145
21146 return res;
21147 } // Case: Intersects the top border
21148
21149
21150 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
21151 res.x = X + H * dX / 2 / dY;
21152 res.y = Y + H / 2; // s += "\nTop border";
21153
21154 return res;
21155 } // Case: Intersects the bottom border
21156
21157
21158 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
21159 res.x = X - H * dX / 2 / dY;
21160 res.y = Y - H / 2; // s += "\nBottom border";
21161
21162 return res;
21163 } // s += "\nClipping point found at " + res.x + ", " + res.y;
21164 // logDebug(s);
21165
21166
21167 return res;
21168 };
21169 /**
21170 * @brief : Calculates all edge forces
21171 */
21172
21173
21174 var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
21175 // Iterate over all edges
21176 for (var i = 0; i < layoutInfo.edgeSize; i++) {
21177 // Get edge, source & target nodes
21178 var edge = layoutInfo.layoutEdges[i];
21179 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
21180 var source = layoutInfo.layoutNodes[sourceIx];
21181 var targetIx = layoutInfo.idToIndex[edge.targetId];
21182 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
21183
21184 var directionX = target.positionX - source.positionX;
21185 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
21186 // A random force has already been applied as node repulsion
21187
21188 if (0 === directionX && 0 === directionY) {
21189 continue;
21190 } // Get clipping points for both nodes
21191
21192
21193 var point1 = findClippingPoint(source, directionX, directionY);
21194 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
21195 var lx = point2.x - point1.x;
21196 var ly = point2.y - point1.y;
21197 var l = Math.sqrt(lx * lx + ly * ly);
21198 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
21199
21200 if (0 !== l) {
21201 var forceX = force * lx / l;
21202 var forceY = force * ly / l;
21203 } else {
21204 var forceX = 0;
21205 var forceY = 0;
21206 } // Add this force to target and source nodes
21207
21208
21209 if (!source.isLocked) {
21210 source.offsetX += forceX;
21211 source.offsetY += forceY;
21212 }
21213
21214 if (!target.isLocked) {
21215 target.offsetX -= forceX;
21216 target.offsetY -= forceY;
21217 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
21218 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
21219 // logDebug(s);
21220
21221 }
21222 };
21223 /**
21224 * @brief : Computes gravity forces for all nodes
21225 */
21226
21227
21228 var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
21229 var distThreshold = 1; // var s = 'calculateGravityForces';
21230 // logDebug(s);
21231
21232 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
21233 var graph = layoutInfo.graphSet[i];
21234 var numNodes = graph.length; // s = "Set: " + graph.toString();
21235 // logDebug(s);
21236 // Compute graph center
21237
21238 if (0 === i) {
21239 var centerX = layoutInfo.clientHeight / 2;
21240 var centerY = layoutInfo.clientWidth / 2;
21241 } else {
21242 // Get Parent node for this graph, and use its position as center
21243 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
21244 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
21245 var centerX = parent.positionX;
21246 var centerY = parent.positionY;
21247 } // s = "Center found at: " + centerX + ", " + centerY;
21248 // logDebug(s);
21249 // Apply force to all nodes in graph
21250
21251
21252 for (var j = 0; j < numNodes; j++) {
21253 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
21254
21255 if (node.isLocked) {
21256 continue;
21257 }
21258
21259 var dx = centerX - node.positionX;
21260 var dy = centerY - node.positionY;
21261 var d = Math.sqrt(dx * dx + dy * dy);
21262
21263 if (d > distThreshold) {
21264 var fx = options.gravity * dx / d;
21265 var fy = options.gravity * dy / d;
21266 node.offsetX += fx;
21267 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
21268 } // s += ": skypped since it's too close to center";
21269 // logDebug(s);
21270
21271 }
21272 }
21273 };
21274 /**
21275 * @brief : This function propagates the existing offsets from
21276 * parent nodes to its descendents.
21277 * @arg layoutInfo : layoutInfo Object
21278 * @arg cy : cytoscape Object
21279 * @arg options : Layout options
21280 */
21281
21282
21283 var propagateForces = function propagateForces(layoutInfo, options) {
21284 // Inline implementation of a queue, used for traversing the graph in BFS order
21285 var queue = [];
21286 var start = 0; // Points to the start the queue
21287
21288 var end = -1; // Points to the end of the queue
21289 // logDebug('propagateForces');
21290 // Start by visiting the nodes in the root graph
21291
21292 queue.push.apply(queue, layoutInfo.graphSet[0]);
21293 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
21294
21295 while (start <= end) {
21296 // Get the node to visit and remove it from queue
21297 var nodeId = queue[start++];
21298 var nodeIndex = layoutInfo.idToIndex[nodeId];
21299 var node = layoutInfo.layoutNodes[nodeIndex];
21300 var children = node.children; // We only need to process the node if it's compound
21301
21302 if (0 < children.length && !node.isLocked) {
21303 var offX = node.offsetX;
21304 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
21305 // ". OffsetX: " + offX + ". OffsetY: " + offY;
21306 // s += "\n Children: " + children.toString();
21307 // logDebug(s);
21308
21309 for (var i = 0; i < children.length; i++) {
21310 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
21311
21312 childNode.offsetX += offX;
21313 childNode.offsetY += offY; // Add children to queue to be visited
21314
21315 queue[++end] = children[i];
21316 } // Reset parent offsets
21317
21318
21319 node.offsetX = 0;
21320 node.offsetY = 0;
21321 }
21322 }
21323 };
21324 /**
21325 * @brief : Updates the layout model positions, based on
21326 * the accumulated forces
21327 */
21328
21329
21330 var updatePositions = function updatePositions(layoutInfo, options) {
21331 // var s = 'Updating positions';
21332 // logDebug(s);
21333 // Reset boundaries for compound nodes
21334 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21335 var n = layoutInfo.layoutNodes[i];
21336
21337 if (0 < n.children.length) {
21338 // logDebug("Resetting boundaries of compound node: " + n.id);
21339 n.maxX = undefined;
21340 n.minX = undefined;
21341 n.maxY = undefined;
21342 n.minY = undefined;
21343 }
21344 }
21345
21346 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21347 var n = layoutInfo.layoutNodes[i];
21348
21349 if (0 < n.children.length || n.isLocked) {
21350 // No need to set compound or locked node position
21351 // logDebug("Skipping position update of node: " + n.id);
21352 continue;
21353 } // s = "Node: " + n.id + " Previous position: (" +
21354 // n.positionX + ", " + n.positionY + ").";
21355 // Limit displacement in order to improve stability
21356
21357
21358 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
21359 n.positionX += tempForce.x;
21360 n.positionY += tempForce.y;
21361 n.offsetX = 0;
21362 n.offsetY = 0;
21363 n.minX = n.positionX - n.width;
21364 n.maxX = n.positionX + n.width;
21365 n.minY = n.positionY - n.height;
21366 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
21367 // logDebug(s);
21368 // Update ancestry boudaries
21369
21370 updateAncestryBoundaries(n, layoutInfo);
21371 } // Update size, position of compund nodes
21372
21373
21374 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21375 var n = layoutInfo.layoutNodes[i];
21376
21377 if (0 < n.children.length && !n.isLocked) {
21378 n.positionX = (n.maxX + n.minX) / 2;
21379 n.positionY = (n.maxY + n.minY) / 2;
21380 n.width = n.maxX - n.minX;
21381 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
21382 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
21383 // s += "\nWidth: " + n.width + ", Height: " + n.height;
21384 // logDebug(s);
21385 }
21386 }
21387 };
21388 /**
21389 * @brief : Limits a force (forceX, forceY) to be not
21390 * greater (in modulo) than max.
21391 8 Preserves force direction.
21392 */
21393
21394
21395 var limitForce = function limitForce(forceX, forceY, max) {
21396 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
21397 var force = Math.sqrt(forceX * forceX + forceY * forceY);
21398
21399 if (force > max) {
21400 var res = {
21401 x: max * forceX / force,
21402 y: max * forceY / force
21403 };
21404 } else {
21405 var res = {
21406 x: forceX,
21407 y: forceY
21408 };
21409 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
21410 // logDebug(s);
21411
21412
21413 return res;
21414 };
21415 /**
21416 * @brief : Function used for keeping track of compound node
21417 * sizes, since they should bound all their subnodes.
21418 */
21419
21420
21421 var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
21422 // var s = "Propagating new position/size of node " + node.id;
21423 var parentId = node.parentId;
21424
21425 if (null == parentId) {
21426 // If there's no parent, we are done
21427 // s += ". No parent node.";
21428 // logDebug(s);
21429 return;
21430 } // Get Parent Node
21431
21432
21433 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
21434 var flag = false; // MaxX
21435
21436 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
21437 p.maxX = node.maxX + p.padRight;
21438 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
21439 } // MinX
21440
21441
21442 if (null == p.minX || node.minX - p.padLeft < p.minX) {
21443 p.minX = node.minX - p.padLeft;
21444 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
21445 } // MaxY
21446
21447
21448 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
21449 p.maxY = node.maxY + p.padBottom;
21450 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
21451 } // MinY
21452
21453
21454 if (null == p.minY || node.minY - p.padTop < p.minY) {
21455 p.minY = node.minY - p.padTop;
21456 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
21457 } // If updated boundaries, propagate changes upward
21458
21459
21460 if (flag) {
21461 // logDebug(s);
21462 return updateAncestryBoundaries(p, layoutInfo);
21463 } // s += ". No changes in boundaries/position of parent node " + p.id;
21464 // logDebug(s);
21465
21466
21467 return;
21468 };
21469
21470 var separateComponents = function separateComponents(layoutInfo, options) {
21471 var nodes = layoutInfo.layoutNodes;
21472 var components = [];
21473
21474 for (var i = 0; i < nodes.length; i++) {
21475 var node = nodes[i];
21476 var cid = node.cmptId;
21477 var component = components[cid] = components[cid] || [];
21478 component.push(node);
21479 }
21480
21481 var totalA = 0;
21482
21483 for (var i = 0; i < components.length; i++) {
21484 var c = components[i];
21485
21486 if (!c) {
21487 continue;
21488 }
21489
21490 c.x1 = Infinity;
21491 c.x2 = -Infinity;
21492 c.y1 = Infinity;
21493 c.y2 = -Infinity;
21494
21495 for (var j = 0; j < c.length; j++) {
21496 var n = c[j];
21497 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
21498 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
21499 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
21500 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
21501 }
21502
21503 c.w = c.x2 - c.x1;
21504 c.h = c.y2 - c.y1;
21505 totalA += c.w * c.h;
21506 }
21507
21508 components.sort(function (c1, c2) {
21509 return c2.w * c2.h - c1.w * c1.h;
21510 });
21511 var x = 0;
21512 var y = 0;
21513 var usedW = 0;
21514 var rowH = 0;
21515 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
21516
21517 for (var i = 0; i < components.length; i++) {
21518 var c = components[i];
21519
21520 if (!c) {
21521 continue;
21522 }
21523
21524 for (var j = 0; j < c.length; j++) {
21525 var n = c[j];
21526
21527 if (!n.isLocked) {
21528 n.positionX += x - c.x1;
21529 n.positionY += y - c.y1;
21530 }
21531 }
21532
21533 x += c.w + options.componentSpacing;
21534 usedW += c.w + options.componentSpacing;
21535 rowH = Math.max(rowH, c.h);
21536
21537 if (usedW > maxRowW) {
21538 y += rowH + options.componentSpacing;
21539 x = 0;
21540 usedW = 0;
21541 rowH = 0;
21542 }
21543 }
21544 };
21545
21546 var defaults$d = {
21547 fit: true,
21548 // whether to fit the viewport to the graph
21549 padding: 30,
21550 // padding used on fit
21551 boundingBox: undefined,
21552 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21553 avoidOverlap: true,
21554 // prevents node overlap, may overflow boundingBox if not enough space
21555 avoidOverlapPadding: 10,
21556 // extra spacing around nodes when avoidOverlap: true
21557 nodeDimensionsIncludeLabels: false,
21558 // Excludes the label when calculating node bounding boxes for the layout algorithm
21559 spacingFactor: undefined,
21560 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21561 condense: false,
21562 // uses all available space on false, uses minimal space on true
21563 rows: undefined,
21564 // force num of rows in the grid
21565 cols: undefined,
21566 // force num of columns in the grid
21567 position: function position(node) {},
21568 // returns { row, col } for element
21569 sort: undefined,
21570 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21571 animate: false,
21572 // whether to transition the node positions
21573 animationDuration: 500,
21574 // duration of animation in ms if enabled
21575 animationEasing: undefined,
21576 // easing of animation if enabled
21577 animateFilter: function animateFilter(node, i) {
21578 return true;
21579 },
21580 // 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
21581 ready: undefined,
21582 // callback on layoutready
21583 stop: undefined,
21584 // callback on layoutstop
21585 transform: function transform(node, position) {
21586 return position;
21587 } // transform a given node position. Useful for changing flow direction in discrete layouts
21588
21589 };
21590
21591 function GridLayout(options) {
21592 this.options = extend({}, defaults$d, options);
21593 }
21594
21595 GridLayout.prototype.run = function () {
21596 var params = this.options;
21597 var options = params;
21598 var cy = params.cy;
21599 var eles = options.eles;
21600 var nodes = eles.nodes().not(':parent');
21601
21602 if (options.sort) {
21603 nodes = nodes.sort(options.sort);
21604 }
21605
21606 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21607 x1: 0,
21608 y1: 0,
21609 w: cy.width(),
21610 h: cy.height()
21611 });
21612
21613 if (bb.h === 0 || bb.w === 0) {
21614 nodes.layoutPositions(this, options, function (ele) {
21615 return {
21616 x: bb.x1,
21617 y: bb.y1
21618 };
21619 });
21620 } else {
21621 // width/height * splits^2 = cells where splits is number of times to split width
21622 var cells = nodes.size();
21623 var splits = Math.sqrt(cells * bb.h / bb.w);
21624 var rows = Math.round(splits);
21625 var cols = Math.round(bb.w / bb.h * splits);
21626
21627 var small = function small(val) {
21628 if (val == null) {
21629 return Math.min(rows, cols);
21630 } else {
21631 var min = Math.min(rows, cols);
21632
21633 if (min == rows) {
21634 rows = val;
21635 } else {
21636 cols = val;
21637 }
21638 }
21639 };
21640
21641 var large = function large(val) {
21642 if (val == null) {
21643 return Math.max(rows, cols);
21644 } else {
21645 var max = Math.max(rows, cols);
21646
21647 if (max == rows) {
21648 rows = val;
21649 } else {
21650 cols = val;
21651 }
21652 }
21653 };
21654
21655 var oRows = options.rows;
21656 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21657
21658 if (oRows != null && oCols != null) {
21659 rows = oRows;
21660 cols = oCols;
21661 } else if (oRows != null && oCols == null) {
21662 rows = oRows;
21663 cols = Math.ceil(cells / rows);
21664 } else if (oRows == null && oCols != null) {
21665 cols = oCols;
21666 rows = Math.ceil(cells / cols);
21667 } // otherwise use the automatic values and adjust accordingly
21668 // if rounding was up, see if we can reduce rows or columns
21669 else if (cols * rows > cells) {
21670 var sm = small();
21671 var lg = large(); // reducing the small side takes away the most cells, so try it first
21672
21673 if ((sm - 1) * lg >= cells) {
21674 small(sm - 1);
21675 } else if ((lg - 1) * sm >= cells) {
21676 large(lg - 1);
21677 }
21678 } else {
21679 // if rounding was too low, add rows or columns
21680 while (cols * rows < cells) {
21681 var _sm = small();
21682
21683 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21684
21685
21686 if ((_lg + 1) * _sm >= cells) {
21687 large(_lg + 1);
21688 } else {
21689 small(_sm + 1);
21690 }
21691 }
21692 }
21693
21694 var cellWidth = bb.w / cols;
21695 var cellHeight = bb.h / rows;
21696
21697 if (options.condense) {
21698 cellWidth = 0;
21699 cellHeight = 0;
21700 }
21701
21702 if (options.avoidOverlap) {
21703 for (var i = 0; i < nodes.length; i++) {
21704 var node = nodes[i];
21705 var pos = node._private.position;
21706
21707 if (pos.x == null || pos.y == null) {
21708 // for bb
21709 pos.x = 0;
21710 pos.y = 0;
21711 }
21712
21713 var nbb = node.layoutDimensions(options);
21714 var p = options.avoidOverlapPadding;
21715 var w = nbb.w + p;
21716 var h = nbb.h + p;
21717 cellWidth = Math.max(cellWidth, w);
21718 cellHeight = Math.max(cellHeight, h);
21719 }
21720 }
21721
21722 var cellUsed = {}; // e.g. 'c-0-2' => true
21723
21724 var used = function used(row, col) {
21725 return cellUsed['c-' + row + '-' + col] ? true : false;
21726 };
21727
21728 var use = function use(row, col) {
21729 cellUsed['c-' + row + '-' + col] = true;
21730 }; // to keep track of current cell position
21731
21732
21733 var row = 0;
21734 var col = 0;
21735
21736 var moveToNextCell = function moveToNextCell() {
21737 col++;
21738
21739 if (col >= cols) {
21740 col = 0;
21741 row++;
21742 }
21743 }; // get a cache of all the manual positions
21744
21745
21746 var id2manPos = {};
21747
21748 for (var _i = 0; _i < nodes.length; _i++) {
21749 var _node = nodes[_i];
21750 var rcPos = options.position(_node);
21751
21752 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21753 // must have at least row or col def'd
21754 var _pos = {
21755 row: rcPos.row,
21756 col: rcPos.col
21757 };
21758
21759 if (_pos.col === undefined) {
21760 // find unused col
21761 _pos.col = 0;
21762
21763 while (used(_pos.row, _pos.col)) {
21764 _pos.col++;
21765 }
21766 } else if (_pos.row === undefined) {
21767 // find unused row
21768 _pos.row = 0;
21769
21770 while (used(_pos.row, _pos.col)) {
21771 _pos.row++;
21772 }
21773 }
21774
21775 id2manPos[_node.id()] = _pos;
21776 use(_pos.row, _pos.col);
21777 }
21778 }
21779
21780 var getPos = function getPos(element, i) {
21781 var x, y;
21782
21783 if (element.locked() || element.isParent()) {
21784 return false;
21785 } // see if we have a manual position set
21786
21787
21788 var rcPos = id2manPos[element.id()];
21789
21790 if (rcPos) {
21791 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21792 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21793 } else {
21794 // otherwise set automatically
21795 while (used(row, col)) {
21796 moveToNextCell();
21797 }
21798
21799 x = col * cellWidth + cellWidth / 2 + bb.x1;
21800 y = row * cellHeight + cellHeight / 2 + bb.y1;
21801 use(row, col);
21802 moveToNextCell();
21803 }
21804
21805 return {
21806 x: x,
21807 y: y
21808 };
21809 };
21810
21811 nodes.layoutPositions(this, options, getPos);
21812 }
21813
21814 return this; // chaining
21815 };
21816
21817 var defaults$e = {
21818 ready: function ready() {},
21819 // on layoutready
21820 stop: function stop() {} // on layoutstop
21821
21822 }; // constructor
21823 // options : object containing layout options
21824
21825 function NullLayout(options) {
21826 this.options = extend({}, defaults$e, options);
21827 } // runs the layout
21828
21829
21830 NullLayout.prototype.run = function () {
21831 var options = this.options;
21832 var eles = options.eles; // elements to consider in the layout
21833
21834 var layout = this; // cy is automatically populated for us in the constructor
21835 // (disable eslint for next line as this serves as example layout code to external developers)
21836 // eslint-disable-next-line no-unused-vars
21837
21838 var cy = options.cy;
21839 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21840 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21841
21842 eles.nodes().positions(function () {
21843 return {
21844 x: 0,
21845 y: 0
21846 };
21847 }); // trigger layoutready when each node has had its position set at least once
21848
21849 layout.one('layoutready', options.ready);
21850 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21851
21852 layout.one('layoutstop', options.stop);
21853 layout.emit('layoutstop');
21854 return this; // chaining
21855 }; // called on continuous layouts to stop them before they finish
21856
21857
21858 NullLayout.prototype.stop = function () {
21859 return this; // chaining
21860 };
21861
21862 var defaults$f = {
21863 positions: undefined,
21864 // map of (node id) => (position obj); or function(node){ return somPos; }
21865 zoom: undefined,
21866 // the zoom level to set (prob want fit = false if set)
21867 pan: undefined,
21868 // the pan level to set (prob want fit = false if set)
21869 fit: true,
21870 // whether to fit to viewport
21871 padding: 30,
21872 // padding on fit
21873 animate: false,
21874 // whether to transition the node positions
21875 animationDuration: 500,
21876 // duration of animation in ms if enabled
21877 animationEasing: undefined,
21878 // easing of animation if enabled
21879 animateFilter: function animateFilter(node, i) {
21880 return true;
21881 },
21882 // 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
21883 ready: undefined,
21884 // callback on layoutready
21885 stop: undefined,
21886 // callback on layoutstop
21887 transform: function transform(node, position) {
21888 return position;
21889 } // transform a given node position. Useful for changing flow direction in discrete layouts
21890
21891 };
21892
21893 function PresetLayout(options) {
21894 this.options = extend({}, defaults$f, options);
21895 }
21896
21897 PresetLayout.prototype.run = function () {
21898 var options = this.options;
21899 var eles = options.eles;
21900 var nodes = eles.nodes();
21901 var posIsFn = fn(options.positions);
21902
21903 function getPosition(node) {
21904 if (options.positions == null) {
21905 return copyPosition(node.position());
21906 }
21907
21908 if (posIsFn) {
21909 return options.positions(node);
21910 }
21911
21912 var pos = options.positions[node._private.data.id];
21913
21914 if (pos == null) {
21915 return null;
21916 }
21917
21918 return pos;
21919 }
21920
21921 nodes.layoutPositions(this, options, function (node, i) {
21922 var position = getPosition(node);
21923
21924 if (node.locked() || position == null) {
21925 return false;
21926 }
21927
21928 return position;
21929 });
21930 return this; // chaining
21931 };
21932
21933 var defaults$g = {
21934 fit: true,
21935 // whether to fit to viewport
21936 padding: 30,
21937 // fit padding
21938 boundingBox: undefined,
21939 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21940 animate: false,
21941 // whether to transition the node positions
21942 animationDuration: 500,
21943 // duration of animation in ms if enabled
21944 animationEasing: undefined,
21945 // easing of animation if enabled
21946 animateFilter: function animateFilter(node, i) {
21947 return true;
21948 },
21949 // 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
21950 ready: undefined,
21951 // callback on layoutready
21952 stop: undefined,
21953 // callback on layoutstop
21954 transform: function transform(node, position) {
21955 return position;
21956 } // transform a given node position. Useful for changing flow direction in discrete layouts
21957
21958 };
21959
21960 function RandomLayout(options) {
21961 this.options = extend({}, defaults$g, options);
21962 }
21963
21964 RandomLayout.prototype.run = function () {
21965 var options = this.options;
21966 var cy = options.cy;
21967 var eles = options.eles;
21968 var nodes = eles.nodes().not(':parent');
21969 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21970 x1: 0,
21971 y1: 0,
21972 w: cy.width(),
21973 h: cy.height()
21974 });
21975
21976 var getPos = function getPos(node, i) {
21977 return {
21978 x: bb.x1 + Math.round(Math.random() * bb.w),
21979 y: bb.y1 + Math.round(Math.random() * bb.h)
21980 };
21981 };
21982
21983 nodes.layoutPositions(this, options, getPos);
21984 return this; // chaining
21985 };
21986
21987 var layout = [{
21988 name: 'breadthfirst',
21989 impl: BreadthFirstLayout
21990 }, {
21991 name: 'circle',
21992 impl: CircleLayout
21993 }, {
21994 name: 'concentric',
21995 impl: ConcentricLayout
21996 }, {
21997 name: 'cose',
21998 impl: CoseLayout
21999 }, {
22000 name: 'grid',
22001 impl: GridLayout
22002 }, {
22003 name: 'null',
22004 impl: NullLayout
22005 }, {
22006 name: 'preset',
22007 impl: PresetLayout
22008 }, {
22009 name: 'random',
22010 impl: RandomLayout
22011 }];
22012
22013 function NullRenderer(options) {
22014 this.options = options;
22015 this.notifications = 0; // for testing
22016 }
22017
22018 var noop$1 = function noop() {};
22019
22020 var throwImgErr = function throwImgErr() {
22021 throw new Error('A headless instance can not render images');
22022 };
22023
22024 NullRenderer.prototype = {
22025 recalculateRenderedStyle: noop$1,
22026 notify: function notify() {
22027 this.notifications++;
22028 },
22029 init: noop$1,
22030 isHeadless: function isHeadless() {
22031 return true;
22032 },
22033 png: throwImgErr,
22034 jpg: throwImgErr
22035 };
22036
22037 var BRp = {};
22038 BRp.arrowShapeWidth = 0.3;
22039
22040 BRp.registerArrowShapes = function () {
22041 var arrowShapes = this.arrowShapes = {};
22042 var renderer = this; // Contract for arrow shapes:
22043 // 0, 0 is arrow tip
22044 // (0, 1) is direction towards node
22045 // (1, 0) is right
22046 //
22047 // functional api:
22048 // collide: check x, y in shape
22049 // roughCollide: called before collide, no false negatives
22050 // draw: draw
22051 // spacing: dist(arrowTip, nodeBoundary)
22052 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
22053
22054 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
22055 var x1 = translation.x - size / 2 - padding;
22056 var x2 = translation.x + size / 2 + padding;
22057 var y1 = translation.y - size / 2 - padding;
22058 var y2 = translation.y + size / 2 + padding;
22059 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
22060 return inside;
22061 };
22062
22063 var transform = function transform(x, y, size, angle, translation) {
22064 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
22065 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
22066 var xScaled = xRotated * size;
22067 var yScaled = yRotated * size;
22068 var xTranslated = xScaled + translation.x;
22069 var yTranslated = yScaled + translation.y;
22070 return {
22071 x: xTranslated,
22072 y: yTranslated
22073 };
22074 };
22075
22076 var transformPoints = function transformPoints(pts, size, angle, translation) {
22077 var retPts = [];
22078
22079 for (var i = 0; i < pts.length; i += 2) {
22080 var x = pts[i];
22081 var y = pts[i + 1];
22082 retPts.push(transform(x, y, size, angle, translation));
22083 }
22084
22085 return retPts;
22086 };
22087
22088 var pointsToArr = function pointsToArr(pts) {
22089 var ret = [];
22090
22091 for (var i = 0; i < pts.length; i++) {
22092 var p = pts[i];
22093 ret.push(p.x, p.y);
22094 }
22095
22096 return ret;
22097 };
22098
22099 var standardGap = function standardGap(edge) {
22100 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
22101 };
22102
22103 var defineArrowShape = function defineArrowShape(name, defn) {
22104 if (string(defn)) {
22105 defn = arrowShapes[defn];
22106 }
22107
22108 arrowShapes[name] = extend({
22109 name: name,
22110 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
22111 collide: function collide(x, y, size, angle, translation, padding) {
22112 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22113 var inside = pointInsidePolygonPoints(x, y, points);
22114 return inside;
22115 },
22116 roughCollide: bbCollide,
22117 draw: function draw(context, size, angle, translation) {
22118 var points = transformPoints(this.points, size, angle, translation);
22119 renderer.arrowShapeImpl('polygon')(context, points);
22120 },
22121 spacing: function spacing(edge) {
22122 return 0;
22123 },
22124 gap: standardGap
22125 }, defn);
22126 };
22127
22128 defineArrowShape('none', {
22129 collide: falsify,
22130 roughCollide: falsify,
22131 draw: noop,
22132 spacing: zeroify,
22133 gap: zeroify
22134 });
22135 defineArrowShape('triangle', {
22136 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
22137 });
22138 defineArrowShape('arrow', 'triangle');
22139 defineArrowShape('triangle-backcurve', {
22140 points: arrowShapes['triangle'].points,
22141 controlPoint: [0, -0.15],
22142 roughCollide: bbCollide,
22143 draw: function draw(context, size, angle, translation, edgeWidth) {
22144 var ptsTrans = transformPoints(this.points, size, angle, translation);
22145 var ctrlPt = this.controlPoint;
22146 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
22147 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
22148 },
22149 gap: function gap(edge) {
22150 return standardGap(edge) * 0.8;
22151 }
22152 });
22153 defineArrowShape('triangle-tee', {
22154 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
22155 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
22156 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22157 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22158 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
22159 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
22160 return inside;
22161 },
22162 draw: function draw(context, size, angle, translation, edgeWidth) {
22163 var triPts = transformPoints(this.points, size, angle, translation);
22164 var teePts = transformPoints(this.pointsTee, size, angle, translation);
22165 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
22166 }
22167 });
22168 defineArrowShape('triangle-cross', {
22169 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
22170 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
22171 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
22172 0.15, -0.4],
22173 crossLinePts: function crossLinePts(size, edgeWidth) {
22174 // shift points so that the distance between the cross points matches edge width
22175 var p = this.baseCrossLinePts.slice();
22176 var shiftFactor = edgeWidth / size;
22177 var y0 = 3;
22178 var y1 = 5;
22179 p[y0] = p[y0] - shiftFactor;
22180 p[y1] = p[y1] - shiftFactor;
22181 return p;
22182 },
22183 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22184 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22185 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
22186 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
22187 return inside;
22188 },
22189 draw: function draw(context, size, angle, translation, edgeWidth) {
22190 var triPts = transformPoints(this.points, size, angle, translation);
22191 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
22192 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
22193 }
22194 });
22195 defineArrowShape('vee', {
22196 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
22197 gap: function gap(edge) {
22198 return standardGap(edge) * 0.525;
22199 }
22200 });
22201 defineArrowShape('circle', {
22202 radius: 0.15,
22203 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22204 var t = translation;
22205 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
22206 return inside;
22207 },
22208 draw: function draw(context, size, angle, translation, edgeWidth) {
22209 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
22210 },
22211 spacing: function spacing(edge) {
22212 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
22213 }
22214 });
22215 defineArrowShape('tee', {
22216 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
22217 spacing: function spacing(edge) {
22218 return 1;
22219 },
22220 gap: function gap(edge) {
22221 return 1;
22222 }
22223 });
22224 defineArrowShape('square', {
22225 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
22226 });
22227 defineArrowShape('diamond', {
22228 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
22229 gap: function gap(edge) {
22230 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
22231 }
22232 });
22233 defineArrowShape('chevron', {
22234 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
22235 gap: function gap(edge) {
22236 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
22237 }
22238 });
22239 };
22240
22241 var BRp$1 = {}; // Project mouse
22242
22243 BRp$1.projectIntoViewport = function (clientX, clientY) {
22244 var cy = this.cy;
22245 var offsets = this.findContainerClientCoords();
22246 var offsetLeft = offsets[0];
22247 var offsetTop = offsets[1];
22248 var scale = offsets[4];
22249 var pan = cy.pan();
22250 var zoom = cy.zoom();
22251 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
22252 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
22253 return [x, y];
22254 };
22255
22256 BRp$1.findContainerClientCoords = function () {
22257 if (this.containerBB) {
22258 return this.containerBB;
22259 }
22260
22261 var container = this.container;
22262 var rect = container.getBoundingClientRect();
22263 var style = window$1.getComputedStyle(container);
22264
22265 var styleValue = function styleValue(name) {
22266 return parseFloat(style.getPropertyValue(name));
22267 };
22268
22269 var padding = {
22270 left: styleValue('padding-left'),
22271 right: styleValue('padding-right'),
22272 top: styleValue('padding-top'),
22273 bottom: styleValue('padding-bottom')
22274 };
22275 var border = {
22276 left: styleValue('border-left-width'),
22277 right: styleValue('border-right-width'),
22278 top: styleValue('border-top-width'),
22279 bottom: styleValue('border-bottom-width')
22280 };
22281 var clientWidth = container.clientWidth;
22282 var clientHeight = container.clientHeight;
22283 var paddingHor = padding.left + padding.right;
22284 var paddingVer = padding.top + padding.bottom;
22285 var borderHor = border.left + border.right;
22286 var scale = rect.width / (clientWidth + borderHor);
22287 var unscaledW = clientWidth - paddingHor;
22288 var unscaledH = clientHeight - paddingVer;
22289 var left = rect.left + padding.left + border.left;
22290 var top = rect.top + padding.top + border.top;
22291 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
22292 };
22293
22294 BRp$1.invalidateContainerClientCoordsCache = function () {
22295 this.containerBB = null;
22296 };
22297
22298 BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
22299 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
22300 };
22301
22302 BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
22303 var self = this;
22304 var r = this;
22305 var eles = r.getCachedZSortedEles();
22306 var near = []; // 1 node max, 1 edge max
22307
22308 var zoom = r.cy.zoom();
22309 var hasCompounds = r.cy.hasCompoundNodes();
22310 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
22311 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
22312 var labelThreshold = (isTouch ? 8 : 2) / zoom;
22313 var minSqDist = Infinity;
22314 var nearEdge;
22315 var nearNode;
22316
22317 if (interactiveElementsOnly) {
22318 eles = eles.interactive;
22319 }
22320
22321 function addEle(ele, sqDist) {
22322 if (ele.isNode()) {
22323 if (nearNode) {
22324 return; // can't replace node
22325 } else {
22326 nearNode = ele;
22327 near.push(ele);
22328 }
22329 }
22330
22331 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
22332 if (nearEdge) {
22333 // then replace existing edge
22334 // can replace only if same z-index
22335 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) {
22336 for (var i = 0; i < near.length; i++) {
22337 if (near[i].isEdge()) {
22338 near[i] = ele;
22339 nearEdge = ele;
22340 minSqDist = sqDist != null ? sqDist : minSqDist;
22341 break;
22342 }
22343 }
22344 }
22345 } else {
22346 near.push(ele);
22347 nearEdge = ele;
22348 minSqDist = sqDist != null ? sqDist : minSqDist;
22349 }
22350 }
22351 }
22352
22353 function checkNode(node) {
22354 var width = node.outerWidth() + 2 * nodeThreshold;
22355 var height = node.outerHeight() + 2 * nodeThreshold;
22356 var hw = width / 2;
22357 var hh = height / 2;
22358 var pos = node.position();
22359
22360 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
22361 && pos.y - hh <= y && y <= pos.y + hh // bb check y
22362 ) {
22363 var shape = r.nodeShapes[self.getNodeShape(node)];
22364
22365 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
22366 addEle(node, 0);
22367 return true;
22368 }
22369 }
22370 }
22371
22372 function checkEdge(edge) {
22373 var _p = edge._private;
22374 var rs = _p.rscratch;
22375 var styleWidth = edge.pstyle('width').pfValue;
22376 var scale = edge.pstyle('arrow-scale').value;
22377 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
22378
22379 var widthSq = width * width;
22380 var width2 = width * 2;
22381 var src = _p.source;
22382 var tgt = _p.target;
22383 var sqDist;
22384
22385 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
22386 var pts = rs.allpts;
22387
22388 for (var i = 0; i + 3 < pts.length; i += 2) {
22389 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]))) {
22390 addEle(edge, sqDist);
22391 return true;
22392 }
22393 }
22394 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22395 var pts = rs.allpts;
22396
22397 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
22398 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]))) {
22399 addEle(edge, sqDist);
22400 return true;
22401 }
22402 }
22403 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
22404
22405
22406 var src = src || _p.source;
22407 var tgt = tgt || _p.target;
22408 var arSize = self.getArrowWidth(styleWidth, scale);
22409 var arrows = [{
22410 name: 'source',
22411 x: rs.arrowStartX,
22412 y: rs.arrowStartY,
22413 angle: rs.srcArrowAngle
22414 }, {
22415 name: 'target',
22416 x: rs.arrowEndX,
22417 y: rs.arrowEndY,
22418 angle: rs.tgtArrowAngle
22419 }, {
22420 name: 'mid-source',
22421 x: rs.midX,
22422 y: rs.midY,
22423 angle: rs.midsrcArrowAngle
22424 }, {
22425 name: 'mid-target',
22426 x: rs.midX,
22427 y: rs.midY,
22428 angle: rs.midtgtArrowAngle
22429 }];
22430
22431 for (var i = 0; i < arrows.length; i++) {
22432 var ar = arrows[i];
22433 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
22434 var edgeWidth = edge.pstyle('width').pfValue;
22435
22436 if (shape.roughCollide(x, y, arSize, ar.angle, {
22437 x: ar.x,
22438 y: ar.y
22439 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
22440 x: ar.x,
22441 y: ar.y
22442 }, edgeWidth, edgeThreshold)) {
22443 addEle(edge);
22444 return true;
22445 }
22446 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
22447
22448
22449 if (hasCompounds && near.length > 0) {
22450 checkNode(src);
22451 checkNode(tgt);
22452 }
22453 }
22454
22455 function preprop(obj, name, pre) {
22456 return getPrefixedProperty(obj, name, pre);
22457 }
22458
22459 function checkLabel(ele, prefix) {
22460 var _p = ele._private;
22461 var th = labelThreshold;
22462 var prefixDash;
22463
22464 if (prefix) {
22465 prefixDash = prefix + '-';
22466 } else {
22467 prefixDash = '';
22468 }
22469
22470 ele.boundingBox();
22471 var bb = _p.labelBounds[prefix || 'main'];
22472 var text = ele.pstyle(prefixDash + 'label').value;
22473 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
22474
22475 if (!eventsEnabled || !text) {
22476 return;
22477 }
22478
22479 var rstyle = _p.rstyle;
22480 var lx = preprop(rstyle, 'labelX', prefix);
22481 var ly = preprop(rstyle, 'labelY', prefix);
22482 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
22483 var lx1 = bb.x1 - th;
22484 var lx2 = bb.x2 + th;
22485 var ly1 = bb.y1 - th;
22486 var ly2 = bb.y2 + th;
22487
22488 if (theta) {
22489 var cos = Math.cos(theta);
22490 var sin = Math.sin(theta);
22491
22492 var rotate = function rotate(x, y) {
22493 x = x - lx;
22494 y = y - ly;
22495 return {
22496 x: x * cos - y * sin + lx,
22497 y: x * sin + y * cos + ly
22498 };
22499 };
22500
22501 var px1y1 = rotate(lx1, ly1);
22502 var px1y2 = rotate(lx1, ly2);
22503 var px2y1 = rotate(lx2, ly1);
22504 var px2y2 = rotate(lx2, ly2);
22505 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
22506
22507 if (pointInsidePolygonPoints(x, y, points)) {
22508 addEle(ele);
22509 return true;
22510 }
22511 } else {
22512 // do a cheaper bb check
22513 if (inBoundingBox(bb, x, y)) {
22514 addEle(ele);
22515 return true;
22516 }
22517 }
22518 }
22519
22520 for (var i = eles.length - 1; i >= 0; i--) {
22521 // reverse order for precedence
22522 var ele = eles[i];
22523
22524 if (ele.isNode()) {
22525 checkNode(ele) || checkLabel(ele);
22526 } else {
22527 // then edge
22528 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
22529 }
22530 }
22531
22532 return near;
22533 }; // 'Give me everything from this box'
22534
22535
22536 BRp$1.getAllInBox = function (x1, y1, x2, y2) {
22537 var eles = this.getCachedZSortedEles().interactive;
22538 var box = [];
22539 var x1c = Math.min(x1, x2);
22540 var x2c = Math.max(x1, x2);
22541 var y1c = Math.min(y1, y2);
22542 var y2c = Math.max(y1, y2);
22543 x1 = x1c;
22544 x2 = x2c;
22545 y1 = y1c;
22546 y2 = y2c;
22547 var boxBb = makeBoundingBox({
22548 x1: x1,
22549 y1: y1,
22550 x2: x2,
22551 y2: y2
22552 });
22553
22554 for (var e = 0; e < eles.length; e++) {
22555 var ele = eles[e];
22556
22557 if (ele.isNode()) {
22558 var node = ele;
22559 var nodeBb = node.boundingBox({
22560 includeNodes: true,
22561 includeEdges: false,
22562 includeLabels: false
22563 });
22564
22565 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22566 box.push(node);
22567 }
22568 } else {
22569 var edge = ele;
22570 var _p = edge._private;
22571 var rs = _p.rscratch;
22572
22573 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22574 continue;
22575 }
22576
22577 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22578 continue;
22579 }
22580
22581 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22582 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22583 var allInside = true;
22584
22585 for (var i = 0; i < pts.length; i++) {
22586 if (!pointInBoundingBox(boxBb, pts[i])) {
22587 allInside = false;
22588 break;
22589 }
22590 }
22591
22592 if (allInside) {
22593 box.push(edge);
22594 }
22595 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22596 box.push(edge);
22597 }
22598 }
22599 }
22600
22601 return box;
22602 };
22603
22604 var BRp$2 = {};
22605
22606 BRp$2.calculateArrowAngles = function (edge) {
22607 var rs = edge._private.rscratch;
22608 var isHaystack = rs.edgeType === 'haystack';
22609 var isBezier = rs.edgeType === 'bezier';
22610 var isMultibezier = rs.edgeType === 'multibezier';
22611 var isSegments = rs.edgeType === 'segments';
22612 var isCompound = rs.edgeType === 'compound';
22613 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22614
22615 var dispX, dispY;
22616 var startX, startY, endX, endY, midX, midY;
22617
22618 if (isHaystack) {
22619 startX = rs.haystackPts[0];
22620 startY = rs.haystackPts[1];
22621 endX = rs.haystackPts[2];
22622 endY = rs.haystackPts[3];
22623 } else {
22624 startX = rs.arrowStartX;
22625 startY = rs.arrowStartY;
22626 endX = rs.arrowEndX;
22627 endY = rs.arrowEndY;
22628 }
22629
22630 midX = rs.midX;
22631 midY = rs.midY; // source
22632 //
22633
22634 if (isSegments) {
22635 dispX = startX - rs.segpts[0];
22636 dispY = startY - rs.segpts[1];
22637 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22638 var pts = rs.allpts;
22639 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22640 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22641 dispX = startX - bX;
22642 dispY = startY - bY;
22643 } else {
22644 dispX = startX - midX;
22645 dispY = startY - midY;
22646 }
22647
22648 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22649 //
22650
22651 var midX = rs.midX;
22652 var midY = rs.midY;
22653
22654 if (isHaystack) {
22655 midX = (startX + endX) / 2;
22656 midY = (startY + endY) / 2;
22657 }
22658
22659 dispX = endX - startX;
22660 dispY = endY - startY;
22661
22662 if (isSegments) {
22663 var pts = rs.allpts;
22664
22665 if (pts.length / 2 % 2 === 0) {
22666 var i2 = pts.length / 2;
22667 var i1 = i2 - 2;
22668 dispX = pts[i2] - pts[i1];
22669 dispY = pts[i2 + 1] - pts[i1 + 1];
22670 } else {
22671 var i2 = pts.length / 2 - 1;
22672 var i1 = i2 - 2;
22673 var i3 = i2 + 2;
22674 dispX = pts[i2] - pts[i1];
22675 dispY = pts[i2 + 1] - pts[i1 + 1];
22676 }
22677 } else if (isMultibezier || isCompound || isSelf) {
22678 var pts = rs.allpts;
22679 var cpts = rs.ctrlpts;
22680 var bp0x, bp0y;
22681 var bp1x, bp1y;
22682
22683 if (cpts.length / 2 % 2 === 0) {
22684 var p0 = pts.length / 2 - 1; // startpt
22685
22686 var ic = p0 + 2;
22687 var p1 = ic + 2;
22688 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22689 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22690 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22691 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22692 } else {
22693 var ic = pts.length / 2 - 1; // ctrpt
22694
22695 var p0 = ic - 2; // startpt
22696
22697 var p1 = ic + 2; // endpt
22698
22699 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22700 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22701 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22702 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22703 }
22704
22705 dispX = bp1x - bp0x;
22706 dispY = bp1y - bp0y;
22707 }
22708
22709 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22710 rs.midDispX = dispX;
22711 rs.midDispY = dispY; // mid source
22712 //
22713
22714 dispX *= -1;
22715 dispY *= -1;
22716
22717 if (isSegments) {
22718 var pts = rs.allpts;
22719
22720 if (pts.length / 2 % 2 === 0) ; else {
22721 var i2 = pts.length / 2 - 1;
22722 var i3 = i2 + 2;
22723 dispX = -(pts[i3] - pts[i2]);
22724 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22725 }
22726 }
22727
22728 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22729 //
22730
22731 if (isSegments) {
22732 dispX = endX - rs.segpts[rs.segpts.length - 2];
22733 dispY = endY - rs.segpts[rs.segpts.length - 1];
22734 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22735 var pts = rs.allpts;
22736 var l = pts.length;
22737 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22738 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22739 dispX = endX - bX;
22740 dispY = endY - bY;
22741 } else {
22742 dispX = endX - midX;
22743 dispY = endY - midY;
22744 }
22745
22746 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22747 };
22748
22749 BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22750 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22751 var cachedVal = cache[edgeWidth + ', ' + scale];
22752
22753 if (cachedVal) {
22754 return cachedVal;
22755 }
22756
22757 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22758 cache[edgeWidth + ', ' + scale] = cachedVal;
22759 return cachedVal;
22760 };
22761
22762 var BRp$3 = {};
22763
22764 BRp$3.findHaystackPoints = function (edges) {
22765 for (var i = 0; i < edges.length; i++) {
22766 var edge = edges[i];
22767 var _p = edge._private;
22768 var rs = _p.rscratch;
22769
22770 if (!rs.haystack) {
22771 var angle = Math.random() * 2 * Math.PI;
22772 rs.source = {
22773 x: Math.cos(angle),
22774 y: Math.sin(angle)
22775 };
22776 angle = Math.random() * 2 * Math.PI;
22777 rs.target = {
22778 x: Math.cos(angle),
22779 y: Math.sin(angle)
22780 };
22781 }
22782
22783 var src = _p.source;
22784 var tgt = _p.target;
22785 var srcPos = src.position();
22786 var tgtPos = tgt.position();
22787 var srcW = src.width();
22788 var tgtW = tgt.width();
22789 var srcH = src.height();
22790 var tgtH = tgt.height();
22791 var radius = edge.pstyle('haystack-radius').value;
22792 var halfRadius = radius / 2; // b/c have to half width/height
22793
22794 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];
22795 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22796 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22797
22798 rs.edgeType = 'haystack';
22799 rs.haystack = true;
22800 this.storeEdgeProjections(edge);
22801 this.calculateArrowAngles(edge);
22802 this.recalculateEdgeLabelProjections(edge);
22803 this.calculateLabelAngles(edge);
22804 }
22805 };
22806
22807 BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22808 // Segments (multiple straight lines)
22809 var rs = edge._private.rscratch;
22810 var posPts = pairInfo.posPts,
22811 intersectionPts = pairInfo.intersectionPts,
22812 vectorNormInverse = pairInfo.vectorNormInverse;
22813 var edgeDistances = edge.pstyle('edge-distances').value;
22814 var segmentWs = edge.pstyle('segment-weights');
22815 var segmentDs = edge.pstyle('segment-distances');
22816 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22817 rs.edgeType = 'segments';
22818 rs.segpts = [];
22819
22820 for (var s = 0; s < segmentsN; s++) {
22821 var w = segmentWs.pfValue[s];
22822 var d = segmentDs.pfValue[s];
22823 var w1 = 1 - w;
22824 var w2 = w;
22825 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22826 var adjustedMidpt = {
22827 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22828 y: midptPts.y1 * w1 + midptPts.y2 * w2
22829 };
22830 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22831 }
22832 };
22833
22834 BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22835 // Self-edge
22836 var rs = edge._private.rscratch;
22837 var dirCounts = pairInfo.dirCounts,
22838 srcPos = pairInfo.srcPos;
22839 var ctrlptDists = edge.pstyle('control-point-distances');
22840 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22841 var loopDir = edge.pstyle('loop-direction').pfValue;
22842 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22843 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22844 rs.edgeType = 'self';
22845 var j = i;
22846 var loopDist = stepSize;
22847
22848 if (edgeIsUnbundled) {
22849 j = 0;
22850 loopDist = ctrlptDist;
22851 }
22852
22853 var loopAngle = loopDir - Math.PI / 2;
22854 var outAngle = loopAngle - loopSwp / 2;
22855 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22856
22857 var dc = String(loopDir + '_' + loopSwp);
22858 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22859 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)];
22860 };
22861
22862 BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22863 // Compound edge
22864 var rs = edge._private.rscratch;
22865 rs.edgeType = 'compound';
22866 var srcPos = pairInfo.srcPos,
22867 tgtPos = pairInfo.tgtPos,
22868 srcW = pairInfo.srcW,
22869 srcH = pairInfo.srcH,
22870 tgtW = pairInfo.tgtW,
22871 tgtH = pairInfo.tgtH;
22872 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22873 var ctrlptDists = edge.pstyle('control-point-distances');
22874 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22875 var j = i;
22876 var loopDist = stepSize;
22877
22878 if (edgeIsUnbundled) {
22879 j = 0;
22880 loopDist = ctrlptDist;
22881 }
22882
22883 var loopW = 50;
22884 var loopaPos = {
22885 x: srcPos.x - srcW / 2,
22886 y: srcPos.y - srcH / 2
22887 };
22888 var loopbPos = {
22889 x: tgtPos.x - tgtW / 2,
22890 y: tgtPos.y - tgtH / 2
22891 };
22892 var loopPos = {
22893 x: Math.min(loopaPos.x, loopbPos.x),
22894 y: Math.min(loopaPos.y, loopbPos.y)
22895 }; // avoids cases with impossible beziers
22896
22897 var minCompoundStretch = 0.5;
22898 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22899 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22900 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];
22901 };
22902
22903 BRp$3.findStraightEdgePoints = function (edge) {
22904 // Straight edge within bundle
22905 edge._private.rscratch.edgeType = 'straight';
22906 };
22907
22908 BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22909 var rs = edge._private.rscratch;
22910 var vectorNormInverse = pairInfo.vectorNormInverse,
22911 posPts = pairInfo.posPts,
22912 intersectionPts = pairInfo.intersectionPts;
22913 var edgeDistances = edge.pstyle('edge-distances').value;
22914 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22915 var ctrlptDists = edge.pstyle('control-point-distances');
22916 var ctrlptWs = edge.pstyle('control-point-weights');
22917 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22918 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22919 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22920
22921 var multi = edgeIsUnbundled;
22922 rs.edgeType = multi ? 'multibezier' : 'bezier';
22923 rs.ctrlpts = [];
22924
22925 for (var b = 0; b < bezierN; b++) {
22926 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22927 var manctrlptDist = void 0;
22928 var sign = signum(normctrlptDist);
22929
22930 if (multi) {
22931 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22932
22933 ctrlptWeight = ctrlptWs.value[b];
22934 }
22935
22936 if (edgeIsUnbundled) {
22937 // multi or single unbundled
22938 manctrlptDist = ctrlptDist;
22939 } else {
22940 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22941 }
22942
22943 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22944 var w1 = 1 - ctrlptWeight;
22945 var w2 = ctrlptWeight;
22946 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22947 var adjustedMidpt = {
22948 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22949 y: midptPts.y1 * w1 + midptPts.y2 * w2
22950 };
22951 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22952 }
22953 };
22954
22955 BRp$3.findTaxiPoints = function (edge, pairInfo) {
22956 // Taxicab geometry with two turns maximum
22957 var rs = edge._private.rscratch;
22958 rs.edgeType = 'segments';
22959 var VERTICAL = 'vertical';
22960 var HORIZONTAL = 'horizontal';
22961 var LEFTWARD = 'leftward';
22962 var RIGHTWARD = 'rightward';
22963 var DOWNWARD = 'downward';
22964 var UPWARD = 'upward';
22965 var AUTO = 'auto';
22966 var posPts = pairInfo.posPts,
22967 srcW = pairInfo.srcW,
22968 srcH = pairInfo.srcH,
22969 tgtW = pairInfo.tgtW,
22970 tgtH = pairInfo.tgtH;
22971 var edgeDistances = edge.pstyle('edge-distances').value;
22972 var dIncludesNodeBody = edgeDistances !== 'node-position';
22973 var taxiDir = edge.pstyle('taxi-direction').value;
22974 var rawTaxiDir = taxiDir; // unprocessed value
22975
22976 var taxiTurn = edge.pstyle('taxi-turn');
22977 var turnIsPercent = taxiTurn.units === '%';
22978 var taxiTurnPfVal = turnIsPercent && taxiTurn.pfValue < 0 ? 1 + taxiTurn.pfValue : taxiTurn.pfValue;
22979 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22980 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22981 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22982 var pdx = posPts.x2 - posPts.x1;
22983 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22984
22985 var subDWH = function subDWH(dxy, dwh) {
22986 if (dxy > 0) {
22987 return Math.max(dxy - dwh, 0);
22988 } else {
22989 return Math.min(dxy + dwh, 0);
22990 }
22991 };
22992
22993 var dx = subDWH(pdx, dw);
22994 var dy = subDWH(pdy, dh);
22995 var isExplicitDir = false;
22996
22997 if (taxiDir === AUTO) {
22998 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22999 } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
23000 taxiDir = VERTICAL;
23001 isExplicitDir = true;
23002 } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
23003 taxiDir = HORIZONTAL;
23004 isExplicitDir = true;
23005 }
23006
23007 var isVert = taxiDir === VERTICAL;
23008 var l = isVert ? dy : dx;
23009 var pl = isVert ? pdy : pdx;
23010 var sgnL = signum(pl);
23011 var forcedDir = false;
23012
23013 if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
23014 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
23015 sgnL *= -1;
23016 l = sgnL * Math.abs(l);
23017 forcedDir = true;
23018 }
23019
23020 var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
23021
23022 var getIsTooClose = function getIsTooClose(d) {
23023 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
23024 };
23025
23026 var isTooCloseSrc = getIsTooClose(d);
23027 var isTooCloseTgt = getIsTooClose(l - Math.abs(d));
23028 var isTooClose = isTooCloseSrc || isTooCloseTgt;
23029
23030 if (isTooClose && !forcedDir) {
23031 // non-ideal routing
23032 if (isVert) {
23033 // vertical fallbacks
23034 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
23035 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
23036
23037 if (lShapeInsideSrc) {
23038 // horizontal Z-shape (direction not respected)
23039 var x = (posPts.x1 + posPts.x2) / 2;
23040 var y1 = posPts.y1,
23041 y2 = posPts.y2;
23042 rs.segpts = [x, y1, x, y2];
23043 } else if (lShapeInsideTgt) {
23044 // vertical Z-shape (distance not respected)
23045 var y = (posPts.y1 + posPts.y2) / 2;
23046 var x1 = posPts.x1,
23047 x2 = posPts.x2;
23048 rs.segpts = [x1, y, x2, y];
23049 } else {
23050 // L-shape fallback (turn distance not respected, but works well with tree siblings)
23051 rs.segpts = [posPts.x1, posPts.y2];
23052 }
23053 } else {
23054 // horizontal fallbacks
23055 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
23056
23057 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
23058
23059 if (_lShapeInsideSrc) {
23060 // vertical Z-shape (direction not respected)
23061 var _y = (posPts.y1 + posPts.y2) / 2;
23062
23063 var _x = posPts.x1,
23064 _x2 = posPts.x2;
23065 rs.segpts = [_x, _y, _x2, _y];
23066 } else if (_lShapeInsideTgt) {
23067 // horizontal Z-shape (turn distance not respected)
23068 var _x3 = (posPts.x1 + posPts.x2) / 2;
23069
23070 var _y2 = posPts.y1,
23071 _y3 = posPts.y2;
23072 rs.segpts = [_x3, _y2, _x3, _y3];
23073 } else {
23074 // L-shape (turn distance not respected, but works well for tree siblings)
23075 rs.segpts = [posPts.x2, posPts.y1];
23076 }
23077 }
23078 } else {
23079 // ideal routing
23080 if (isVert) {
23081 var _y4 = (d < 0 ? posPts.y2 : posPts.y1) + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
23082
23083 var _x4 = posPts.x1,
23084 _x5 = posPts.x2;
23085 rs.segpts = [_x4, _y4, _x5, _y4];
23086 } else {
23087 // horizontal
23088 var _x6 = (d < 0 ? posPts.x2 : posPts.x1) + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
23089
23090 var _y5 = posPts.y1,
23091 _y6 = posPts.y2;
23092 rs.segpts = [_x6, _y5, _x6, _y6];
23093 }
23094 }
23095 };
23096
23097 BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
23098 var rs = edge._private.rscratch; // can only correct beziers for now...
23099
23100 if (rs.edgeType === 'bezier') {
23101 var srcPos = pairInfo.srcPos,
23102 tgtPos = pairInfo.tgtPos,
23103 srcW = pairInfo.srcW,
23104 srcH = pairInfo.srcH,
23105 tgtW = pairInfo.tgtW,
23106 tgtH = pairInfo.tgtH,
23107 srcShape = pairInfo.srcShape,
23108 tgtShape = pairInfo.tgtShape;
23109 var badStart = !number(rs.startX) || !number(rs.startY);
23110 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
23111 var badEnd = !number(rs.endX) || !number(rs.endY);
23112 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
23113 var minCpADistFactor = 3;
23114 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23115 var minCpADist = minCpADistFactor * arrowW;
23116 var startACpDist = dist({
23117 x: rs.ctrlpts[0],
23118 y: rs.ctrlpts[1]
23119 }, {
23120 x: rs.startX,
23121 y: rs.startY
23122 });
23123 var closeStartACp = startACpDist < minCpADist;
23124 var endACpDist = dist({
23125 x: rs.ctrlpts[0],
23126 y: rs.ctrlpts[1]
23127 }, {
23128 x: rs.endX,
23129 y: rs.endY
23130 });
23131 var closeEndACp = endACpDist < minCpADist;
23132 var overlapping = false;
23133
23134 if (badStart || badAStart || closeStartACp) {
23135 overlapping = true; // project control point along line from src centre to outside the src shape
23136 // (otherwise intersection will yield nothing)
23137
23138 var cpD = {
23139 // delta
23140 x: rs.ctrlpts[0] - srcPos.x,
23141 y: rs.ctrlpts[1] - srcPos.y
23142 };
23143 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
23144
23145 var cpM = {
23146 // normalised delta
23147 x: cpD.x / cpL,
23148 y: cpD.y / cpL
23149 };
23150 var radius = Math.max(srcW, srcH);
23151 var cpProj = {
23152 // *2 radius guarantees outside shape
23153 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
23154 y: rs.ctrlpts[1] + cpM.y * 2 * radius
23155 };
23156 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
23157
23158 if (closeStartACp) {
23159 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
23160 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
23161 } else {
23162 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
23163 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
23164 }
23165 }
23166
23167 if (badEnd || badAEnd || closeEndACp) {
23168 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
23169 // (otherwise intersection will yield nothing)
23170
23171 var _cpD = {
23172 // delta
23173 x: rs.ctrlpts[0] - tgtPos.x,
23174 y: rs.ctrlpts[1] - tgtPos.y
23175 };
23176
23177 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
23178
23179
23180 var _cpM = {
23181 // normalised delta
23182 x: _cpD.x / _cpL,
23183 y: _cpD.y / _cpL
23184 };
23185
23186 var _radius = Math.max(srcW, srcH);
23187
23188 var _cpProj = {
23189 // *2 radius guarantees outside shape
23190 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
23191 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
23192 };
23193 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
23194
23195 if (closeEndACp) {
23196 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
23197 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
23198 } else {
23199 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
23200 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
23201 }
23202 }
23203
23204 if (overlapping) {
23205 // recalc endpts
23206 this.findEndpoints(edge);
23207 }
23208 }
23209 };
23210
23211 BRp$3.storeAllpts = function (edge) {
23212 var rs = edge._private.rscratch;
23213
23214 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
23215 rs.allpts = [];
23216 rs.allpts.push(rs.startX, rs.startY);
23217
23218 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
23219 // ctrl pt itself
23220 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
23221
23222 if (b + 3 < rs.ctrlpts.length) {
23223 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
23224 }
23225 }
23226
23227 rs.allpts.push(rs.endX, rs.endY);
23228 var m, mt;
23229
23230 if (rs.ctrlpts.length / 2 % 2 === 0) {
23231 m = rs.allpts.length / 2 - 1;
23232 rs.midX = rs.allpts[m];
23233 rs.midY = rs.allpts[m + 1];
23234 } else {
23235 m = rs.allpts.length / 2 - 3;
23236 mt = 0.5;
23237 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
23238 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
23239 }
23240 } else if (rs.edgeType === 'straight') {
23241 // need to calc these after endpts
23242 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
23243
23244 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
23245 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
23246 } else if (rs.edgeType === 'segments') {
23247 rs.allpts = [];
23248 rs.allpts.push(rs.startX, rs.startY);
23249 rs.allpts.push.apply(rs.allpts, rs.segpts);
23250 rs.allpts.push(rs.endX, rs.endY);
23251
23252 if (rs.segpts.length % 4 === 0) {
23253 var i2 = rs.segpts.length / 2;
23254 var i1 = i2 - 2;
23255 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
23256 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
23257 } else {
23258 var _i = rs.segpts.length / 2 - 1;
23259
23260 rs.midX = rs.segpts[_i];
23261 rs.midY = rs.segpts[_i + 1];
23262 }
23263 }
23264 };
23265
23266 BRp$3.checkForInvalidEdgeWarning = function (edge) {
23267 var rs = edge[0]._private.rscratch;
23268
23269 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
23270 rs.loggedErr = false;
23271 } else {
23272 if (!rs.loggedErr) {
23273 rs.loggedErr = true;
23274 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.');
23275 }
23276 }
23277 };
23278
23279 BRp$3.findEdgeControlPoints = function (edges) {
23280 var _this = this;
23281
23282 if (!edges || edges.length === 0) {
23283 return;
23284 }
23285
23286 var r = this;
23287 var cy = r.cy;
23288 var hasCompounds = cy.hasCompoundNodes();
23289 var hashTable = {
23290 map: new Map$1(),
23291 get: function get(pairId) {
23292 var map2 = this.map.get(pairId[0]);
23293
23294 if (map2 != null) {
23295 return map2.get(pairId[1]);
23296 } else {
23297 return null;
23298 }
23299 },
23300 set: function set(pairId, val) {
23301 var map2 = this.map.get(pairId[0]);
23302
23303 if (map2 == null) {
23304 map2 = new Map$1();
23305 this.map.set(pairId[0], map2);
23306 }
23307
23308 map2.set(pairId[1], val);
23309 }
23310 };
23311 var pairIds = [];
23312 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
23313
23314 for (var i = 0; i < edges.length; i++) {
23315 var edge = edges[i];
23316 var _p = edge._private;
23317 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
23318 // they shouldn't take up space
23319
23320 if (edge.removed() || !edge.takesUpSpace()) {
23321 continue;
23322 }
23323
23324 if (curveStyle === 'haystack') {
23325 haystackEdges.push(edge);
23326 continue;
23327 }
23328
23329 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
23330 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
23331 var src = _p.source;
23332 var tgt = _p.target;
23333 var srcIndex = src.poolIndex();
23334 var tgtIndex = tgt.poolIndex();
23335 var pairId = [srcIndex, tgtIndex].sort();
23336 var tableEntry = hashTable.get(pairId);
23337
23338 if (tableEntry == null) {
23339 tableEntry = {
23340 eles: []
23341 };
23342 hashTable.set(pairId, tableEntry);
23343 pairIds.push(pairId);
23344 }
23345
23346 tableEntry.eles.push(edge);
23347
23348 if (edgeIsUnbundled) {
23349 tableEntry.hasUnbundled = true;
23350 }
23351
23352 if (edgeIsBezier) {
23353 tableEntry.hasBezier = true;
23354 }
23355 } // for each pair (src, tgt), create the ctrl pts
23356 // Nested for loop is OK; total number of iterations for both loops = edgeCount
23357
23358
23359 var _loop = function _loop(p) {
23360 var pairId = pairIds[p];
23361 var pairInfo = hashTable.get(pairId);
23362 var swappedpairInfo = void 0;
23363
23364 if (!pairInfo.hasUnbundled) {
23365 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
23366 return e.isBundledBezier();
23367 });
23368 clearArray(pairInfo.eles);
23369 pllEdges.forEach(function (edge) {
23370 return pairInfo.eles.push(edge);
23371 }); // for each pair id, the edges should be sorted by index
23372
23373 pairInfo.eles.sort(function (edge1, edge2) {
23374 return edge1.poolIndex() - edge2.poolIndex();
23375 });
23376 }
23377
23378 var firstEdge = pairInfo.eles[0];
23379 var src = firstEdge.source();
23380 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
23381
23382 if (src.poolIndex() > tgt.poolIndex()) {
23383 var temp = src;
23384 src = tgt;
23385 tgt = temp;
23386 }
23387
23388 var srcPos = pairInfo.srcPos = src.position();
23389 var tgtPos = pairInfo.tgtPos = tgt.position();
23390 var srcW = pairInfo.srcW = src.outerWidth();
23391 var srcH = pairInfo.srcH = src.outerHeight();
23392 var tgtW = pairInfo.tgtW = tgt.outerWidth();
23393 var tgtH = pairInfo.tgtH = tgt.outerHeight();
23394
23395 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
23396
23397 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
23398
23399 pairInfo.dirCounts = {
23400 'north': 0,
23401 'west': 0,
23402 'south': 0,
23403 'east': 0,
23404 'northwest': 0,
23405 'southwest': 0,
23406 'northeast': 0,
23407 'southeast': 0
23408 };
23409
23410 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
23411 var _edge = pairInfo.eles[_i2];
23412 var rs = _edge[0]._private.rscratch;
23413
23414 var _curveStyle = _edge.pstyle('curve-style').value;
23415
23416 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
23417
23418
23419 var edgeIsSwapped = !src.same(_edge.source());
23420
23421 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
23422 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
23423
23424 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
23425 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
23426
23427 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
23428 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
23429 var intersectionPts = pairInfo.intersectionPts = {
23430 x1: srcOutside[0],
23431 x2: tgtOutside[0],
23432 y1: srcOutside[1],
23433 y2: tgtOutside[1]
23434 };
23435 var posPts = pairInfo.posPts = {
23436 x1: srcPos.x,
23437 x2: tgtPos.x,
23438 y1: srcPos.y,
23439 y2: tgtPos.y
23440 };
23441 var dy = tgtOutside[1] - srcOutside[1];
23442 var dx = tgtOutside[0] - srcOutside[0];
23443 var l = Math.sqrt(dx * dx + dy * dy);
23444 var vector = pairInfo.vector = {
23445 x: dx,
23446 y: dy
23447 };
23448 var vectorNorm = pairInfo.vectorNorm = {
23449 x: vector.x / l,
23450 y: vector.y / l
23451 };
23452 var vectorNormInverse = {
23453 x: -vectorNorm.y,
23454 y: vectorNorm.x
23455 }; // if node shapes overlap, then no ctrl pts to draw
23456
23457 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);
23458 pairInfo.vectorNormInverse = vectorNormInverse;
23459 swappedpairInfo = {
23460 nodesOverlap: pairInfo.nodesOverlap,
23461 dirCounts: pairInfo.dirCounts,
23462 calculatedIntersection: true,
23463 hasBezier: pairInfo.hasBezier,
23464 hasUnbundled: pairInfo.hasUnbundled,
23465 eles: pairInfo.eles,
23466 srcPos: tgtPos,
23467 tgtPos: srcPos,
23468 srcW: tgtW,
23469 srcH: tgtH,
23470 tgtW: srcW,
23471 tgtH: srcH,
23472 srcIntn: tgtIntn,
23473 tgtIntn: srcIntn,
23474 srcShape: tgtShape,
23475 tgtShape: srcShape,
23476 posPts: {
23477 x1: posPts.x2,
23478 y1: posPts.y2,
23479 x2: posPts.x1,
23480 y2: posPts.y1
23481 },
23482 intersectionPts: {
23483 x1: intersectionPts.x2,
23484 y1: intersectionPts.y2,
23485 x2: intersectionPts.x1,
23486 y2: intersectionPts.y1
23487 },
23488 vector: {
23489 x: -vector.x,
23490 y: -vector.y
23491 },
23492 vectorNorm: {
23493 x: -vectorNorm.x,
23494 y: -vectorNorm.y
23495 },
23496 vectorNormInverse: {
23497 x: -vectorNormInverse.x,
23498 y: -vectorNormInverse.y
23499 }
23500 };
23501 }
23502
23503 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
23504 rs.nodesOverlap = passedPairInfo.nodesOverlap;
23505 rs.srcIntn = passedPairInfo.srcIntn;
23506 rs.tgtIntn = passedPairInfo.tgtIntn;
23507
23508 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
23509 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23510 } else if (src === tgt) {
23511 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23512 } else if (_curveStyle === 'segments') {
23513 _this.findSegmentsPoints(_edge, passedPairInfo);
23514 } else if (_curveStyle === 'taxi') {
23515 _this.findTaxiPoints(_edge, passedPairInfo);
23516 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
23517 _this.findStraightEdgePoints(_edge);
23518 } else {
23519 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
23520 }
23521
23522 _this.findEndpoints(_edge);
23523
23524 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
23525
23526 _this.checkForInvalidEdgeWarning(_edge);
23527
23528 _this.storeAllpts(_edge);
23529
23530 _this.storeEdgeProjections(_edge);
23531
23532 _this.calculateArrowAngles(_edge);
23533
23534 _this.recalculateEdgeLabelProjections(_edge);
23535
23536 _this.calculateLabelAngles(_edge);
23537 } // for pair edges
23538
23539 };
23540
23541 for (var p = 0; p < pairIds.length; p++) {
23542 _loop(p);
23543 } // for pair ids
23544 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
23545
23546
23547 this.findHaystackPoints(haystackEdges);
23548 };
23549
23550 function getPts(pts) {
23551 var retPts = [];
23552
23553 if (pts == null) {
23554 return;
23555 }
23556
23557 for (var i = 0; i < pts.length; i += 2) {
23558 var x = pts[i];
23559 var y = pts[i + 1];
23560 retPts.push({
23561 x: x,
23562 y: y
23563 });
23564 }
23565
23566 return retPts;
23567 }
23568
23569 BRp$3.getSegmentPoints = function (edge) {
23570 var rs = edge[0]._private.rscratch;
23571 var type = rs.edgeType;
23572
23573 if (type === 'segments') {
23574 this.recalculateRenderedStyle(edge);
23575 return getPts(rs.segpts);
23576 }
23577 };
23578
23579 BRp$3.getControlPoints = function (edge) {
23580 var rs = edge[0]._private.rscratch;
23581 var type = rs.edgeType;
23582
23583 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23584 this.recalculateRenderedStyle(edge);
23585 return getPts(rs.ctrlpts);
23586 }
23587 };
23588
23589 BRp$3.getEdgeMidpoint = function (edge) {
23590 var rs = edge[0]._private.rscratch;
23591 this.recalculateRenderedStyle(edge);
23592 return {
23593 x: rs.midX,
23594 y: rs.midY
23595 };
23596 };
23597
23598 var BRp$4 = {};
23599
23600 BRp$4.manualEndptToPx = function (node, prop) {
23601 var r = this;
23602 var npos = node.position();
23603 var w = node.outerWidth();
23604 var h = node.outerHeight();
23605
23606 if (prop.value.length === 2) {
23607 var p = [prop.pfValue[0], prop.pfValue[1]];
23608
23609 if (prop.units[0] === '%') {
23610 p[0] = p[0] * w;
23611 }
23612
23613 if (prop.units[1] === '%') {
23614 p[1] = p[1] * h;
23615 }
23616
23617 p[0] += npos.x;
23618 p[1] += npos.y;
23619 return p;
23620 } else {
23621 var angle = prop.pfValue[0];
23622 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23623
23624 var l = 2 * Math.max(w, h);
23625 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23626 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23627 }
23628 };
23629
23630 BRp$4.findEndpoints = function (edge) {
23631 var r = this;
23632 var intersect;
23633 var source = edge.source()[0];
23634 var target = edge.target()[0];
23635 var srcPos = source.position();
23636 var tgtPos = target.position();
23637 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23638 var srcArShape = edge.pstyle('source-arrow-shape').value;
23639 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23640 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23641 var curveStyle = edge.pstyle('curve-style').value;
23642 var rs = edge._private.rscratch;
23643 var et = rs.edgeType;
23644 var taxi = curveStyle === 'taxi';
23645 var self = et === 'self' || et === 'compound';
23646 var bezier = et === 'bezier' || et === 'multibezier' || self;
23647 var multi = et !== 'bezier';
23648 var lines = et === 'straight' || et === 'segments';
23649 var segments = et === 'segments';
23650 var hasEndpts = bezier || multi || lines;
23651 var overrideEndpts = self || taxi;
23652 var srcManEndpt = edge.pstyle('source-endpoint');
23653 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23654 var tgtManEndpt = edge.pstyle('target-endpoint');
23655 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23656 rs.srcManEndpt = srcManEndpt;
23657 rs.tgtManEndpt = tgtManEndpt;
23658 var p1; // last known point of edge on target side
23659
23660 var p2; // last known point of edge on source side
23661
23662 var p1_i; // point to intersect with target shape
23663
23664 var p2_i; // point to intersect with source shape
23665
23666 if (bezier) {
23667 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23668 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23669 p1 = cpEnd;
23670 p2 = cpStart;
23671 } else if (lines) {
23672 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23673 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23674 p1 = tgtArrowFromPt;
23675 p2 = srcArrowFromPt;
23676 }
23677
23678 if (tgtManEndptVal === 'inside-to-node') {
23679 intersect = [tgtPos.x, tgtPos.y];
23680 } else if (tgtManEndpt.units) {
23681 intersect = this.manualEndptToPx(target, tgtManEndpt);
23682 } else if (tgtManEndptVal === 'outside-to-line') {
23683 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23684 } else {
23685 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23686 p1_i = p1;
23687 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23688 p1_i = [srcPos.x, srcPos.y];
23689 }
23690
23691 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23692
23693 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23694 var trs = target._private.rscratch;
23695 var lw = trs.labelWidth;
23696 var lh = trs.labelHeight;
23697 var lx = trs.labelX;
23698 var ly = trs.labelY;
23699 var lw2 = lw / 2;
23700 var lh2 = lh / 2;
23701 var va = target.pstyle('text-valign').value;
23702
23703 if (va === 'top') {
23704 ly -= lh2;
23705 } else if (va === 'bottom') {
23706 ly += lh2;
23707 }
23708
23709 var ha = target.pstyle('text-halign').value;
23710
23711 if (ha === 'left') {
23712 lx -= lw2;
23713 } else if (ha === 'right') {
23714 lx += lw2;
23715 }
23716
23717 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);
23718
23719 if (labelIntersect.length > 0) {
23720 var refPt = srcPos;
23721 var intSqdist = sqdist(refPt, array2point(intersect));
23722 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23723 var minSqDist = intSqdist;
23724
23725 if (labIntSqdist < intSqdist) {
23726 intersect = labelIntersect;
23727 minSqDist = labIntSqdist;
23728 }
23729
23730 if (labelIntersect.length > 2) {
23731 var labInt2SqDist = sqdist(refPt, {
23732 x: labelIntersect[2],
23733 y: labelIntersect[3]
23734 });
23735
23736 if (labInt2SqDist < minSqDist) {
23737 intersect = [labelIntersect[2], labelIntersect[3]];
23738 }
23739 }
23740 }
23741 }
23742 }
23743
23744 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23745 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23746 rs.endX = edgeEnd[0];
23747 rs.endY = edgeEnd[1];
23748 rs.arrowEndX = arrowEnd[0];
23749 rs.arrowEndY = arrowEnd[1];
23750
23751 if (srcManEndptVal === 'inside-to-node') {
23752 intersect = [srcPos.x, srcPos.y];
23753 } else if (srcManEndpt.units) {
23754 intersect = this.manualEndptToPx(source, srcManEndpt);
23755 } else if (srcManEndptVal === 'outside-to-line') {
23756 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23757 } else {
23758 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23759 p2_i = p2;
23760 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23761 p2_i = [tgtPos.x, tgtPos.y];
23762 }
23763
23764 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23765
23766 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23767 var srs = source._private.rscratch;
23768 var _lw = srs.labelWidth;
23769 var _lh = srs.labelHeight;
23770 var _lx = srs.labelX;
23771 var _ly = srs.labelY;
23772
23773 var _lw2 = _lw / 2;
23774
23775 var _lh2 = _lh / 2;
23776
23777 var _va = source.pstyle('text-valign').value;
23778
23779 if (_va === 'top') {
23780 _ly -= _lh2;
23781 } else if (_va === 'bottom') {
23782 _ly += _lh2;
23783 }
23784
23785 var _ha = source.pstyle('text-halign').value;
23786
23787 if (_ha === 'left') {
23788 _lx -= _lw2;
23789 } else if (_ha === 'right') {
23790 _lx += _lw2;
23791 }
23792
23793 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);
23794
23795 if (_labelIntersect.length > 0) {
23796 var _refPt = tgtPos;
23797
23798 var _intSqdist = sqdist(_refPt, array2point(intersect));
23799
23800 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23801
23802 var _minSqDist = _intSqdist;
23803
23804 if (_labIntSqdist < _intSqdist) {
23805 intersect = [_labelIntersect[0], _labelIntersect[1]];
23806 _minSqDist = _labIntSqdist;
23807 }
23808
23809 if (_labelIntersect.length > 2) {
23810 var _labInt2SqDist = sqdist(_refPt, {
23811 x: _labelIntersect[2],
23812 y: _labelIntersect[3]
23813 });
23814
23815 if (_labInt2SqDist < _minSqDist) {
23816 intersect = [_labelIntersect[2], _labelIntersect[3]];
23817 }
23818 }
23819 }
23820 }
23821 }
23822
23823 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23824 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23825 rs.startX = edgeStart[0];
23826 rs.startY = edgeStart[1];
23827 rs.arrowStartX = arrowStart[0];
23828 rs.arrowStartY = arrowStart[1];
23829
23830 if (hasEndpts) {
23831 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23832 rs.badLine = true;
23833 } else {
23834 rs.badLine = false;
23835 }
23836 }
23837 };
23838
23839 BRp$4.getSourceEndpoint = function (edge) {
23840 var rs = edge[0]._private.rscratch;
23841 this.recalculateRenderedStyle(edge);
23842
23843 switch (rs.edgeType) {
23844 case 'haystack':
23845 return {
23846 x: rs.haystackPts[0],
23847 y: rs.haystackPts[1]
23848 };
23849
23850 default:
23851 return {
23852 x: rs.arrowStartX,
23853 y: rs.arrowStartY
23854 };
23855 }
23856 };
23857
23858 BRp$4.getTargetEndpoint = function (edge) {
23859 var rs = edge[0]._private.rscratch;
23860 this.recalculateRenderedStyle(edge);
23861
23862 switch (rs.edgeType) {
23863 case 'haystack':
23864 return {
23865 x: rs.haystackPts[2],
23866 y: rs.haystackPts[3]
23867 };
23868
23869 default:
23870 return {
23871 x: rs.arrowEndX,
23872 y: rs.arrowEndY
23873 };
23874 }
23875 };
23876
23877 var BRp$5 = {};
23878
23879 function pushBezierPts(r, edge, pts) {
23880 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23881 return qbezierAt(p1, p2, p3, t);
23882 };
23883
23884 var _p = edge._private;
23885 var bpts = _p.rstyle.bezierPts;
23886
23887 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23888 var p = r.bezierProjPcts[i];
23889 bpts.push({
23890 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23891 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23892 });
23893 }
23894 }
23895
23896 BRp$5.storeEdgeProjections = function (edge) {
23897 var _p = edge._private;
23898 var rs = _p.rscratch;
23899 var et = rs.edgeType; // clear the cached points state
23900
23901 _p.rstyle.bezierPts = null;
23902 _p.rstyle.linePts = null;
23903 _p.rstyle.haystackPts = null;
23904
23905 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23906 _p.rstyle.bezierPts = [];
23907
23908 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23909 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23910 }
23911 } else if (et === 'segments') {
23912 var lpts = _p.rstyle.linePts = [];
23913
23914 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23915 lpts.push({
23916 x: rs.allpts[i],
23917 y: rs.allpts[i + 1]
23918 });
23919 }
23920 } else if (et === 'haystack') {
23921 var hpts = rs.haystackPts;
23922 _p.rstyle.haystackPts = [{
23923 x: hpts[0],
23924 y: hpts[1]
23925 }, {
23926 x: hpts[2],
23927 y: hpts[3]
23928 }];
23929 }
23930
23931 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23932 };
23933
23934 BRp$5.recalculateEdgeProjections = function (edges) {
23935 this.findEdgeControlPoints(edges);
23936 };
23937
23938 var BRp$6 = {};
23939
23940 BRp$6.recalculateNodeLabelProjection = function (node) {
23941 var content = node.pstyle('label').strValue;
23942
23943 if (emptyString(content)) {
23944 return;
23945 }
23946
23947 var textX, textY;
23948 var _p = node._private;
23949 var nodeWidth = node.width();
23950 var nodeHeight = node.height();
23951 var padding = node.padding();
23952 var nodePos = node.position();
23953 var textHalign = node.pstyle('text-halign').strValue;
23954 var textValign = node.pstyle('text-valign').strValue;
23955 var rs = _p.rscratch;
23956 var rstyle = _p.rstyle;
23957
23958 switch (textHalign) {
23959 case 'left':
23960 textX = nodePos.x - nodeWidth / 2 - padding;
23961 break;
23962
23963 case 'right':
23964 textX = nodePos.x + nodeWidth / 2 + padding;
23965 break;
23966
23967 default:
23968 // e.g. center
23969 textX = nodePos.x;
23970 }
23971
23972 switch (textValign) {
23973 case 'top':
23974 textY = nodePos.y - nodeHeight / 2 - padding;
23975 break;
23976
23977 case 'bottom':
23978 textY = nodePos.y + nodeHeight / 2 + padding;
23979 break;
23980
23981 default:
23982 // e.g. middle
23983 textY = nodePos.y;
23984 }
23985
23986 rs.labelX = textX;
23987 rs.labelY = textY;
23988 rstyle.labelX = textX;
23989 rstyle.labelY = textY;
23990 this.applyLabelDimensions(node);
23991 };
23992
23993 var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23994 var angle = Math.atan(dy / dx);
23995
23996 if (dx === 0 && angle < 0) {
23997 angle = angle * -1;
23998 }
23999
24000 return angle;
24001 };
24002
24003 var lineAngle = function lineAngle(p0, p1) {
24004 var dx = p1.x - p0.x;
24005 var dy = p1.y - p0.y;
24006 return lineAngleFromDelta(dx, dy);
24007 };
24008
24009 var bezierAngle = function bezierAngle(p0, p1, p2, t) {
24010 var t0 = bound(0, t - 0.001, 1);
24011 var t1 = bound(0, t + 0.001, 1);
24012 var lp0 = qbezierPtAt(p0, p1, p2, t0);
24013 var lp1 = qbezierPtAt(p0, p1, p2, t1);
24014 return lineAngle(lp0, lp1);
24015 };
24016
24017 BRp$6.recalculateEdgeLabelProjections = function (edge) {
24018 var p;
24019 var _p = edge._private;
24020 var rs = _p.rscratch;
24021 var r = this;
24022 var content = {
24023 mid: edge.pstyle('label').strValue,
24024 source: edge.pstyle('source-label').strValue,
24025 target: edge.pstyle('target-label').strValue
24026 };
24027
24028 if (content.mid || content.source || content.target) ; else {
24029 return; // no labels => no calcs
24030 } // add center point to style so bounding box calculations can use it
24031 //
24032
24033
24034 p = {
24035 x: rs.midX,
24036 y: rs.midY
24037 };
24038
24039 var setRs = function setRs(propName, prefix, value) {
24040 setPrefixedProperty(_p.rscratch, propName, prefix, value);
24041 setPrefixedProperty(_p.rstyle, propName, prefix, value);
24042 };
24043
24044 setRs('labelX', null, p.x);
24045 setRs('labelY', null, p.y);
24046 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
24047 setRs('labelAutoAngle', null, midAngle);
24048
24049 var createControlPointInfo = function createControlPointInfo() {
24050 if (createControlPointInfo.cache) {
24051 return createControlPointInfo.cache;
24052 } // use cache so only 1x per edge
24053
24054
24055 var ctrlpts = []; // store each ctrlpt info init
24056
24057 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
24058 var p0 = {
24059 x: rs.allpts[i],
24060 y: rs.allpts[i + 1]
24061 };
24062 var p1 = {
24063 x: rs.allpts[i + 2],
24064 y: rs.allpts[i + 3]
24065 }; // ctrlpt
24066
24067 var p2 = {
24068 x: rs.allpts[i + 4],
24069 y: rs.allpts[i + 5]
24070 };
24071 ctrlpts.push({
24072 p0: p0,
24073 p1: p1,
24074 p2: p2,
24075 startDist: 0,
24076 length: 0,
24077 segments: []
24078 });
24079 }
24080
24081 var bpts = _p.rstyle.bezierPts;
24082 var nProjs = r.bezierProjPcts.length;
24083
24084 function addSegment(cp, p0, p1, t0, t1) {
24085 var length = dist(p0, p1);
24086 var prevSegment = cp.segments[cp.segments.length - 1];
24087 var segment = {
24088 p0: p0,
24089 p1: p1,
24090 t0: t0,
24091 t1: t1,
24092 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
24093 length: length
24094 };
24095 cp.segments.push(segment);
24096 cp.length += length;
24097 } // update each ctrlpt with segment info
24098
24099
24100 for (var _i = 0; _i < ctrlpts.length; _i++) {
24101 var cp = ctrlpts[_i];
24102 var prevCp = ctrlpts[_i - 1];
24103
24104 if (prevCp) {
24105 cp.startDist = prevCp.startDist + prevCp.length;
24106 }
24107
24108 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
24109
24110 for (var j = 0; j < nProjs - 1; j++) {
24111 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
24112 }
24113
24114 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
24115 }
24116
24117 return createControlPointInfo.cache = ctrlpts;
24118 };
24119
24120 var calculateEndProjection = function calculateEndProjection(prefix) {
24121 var angle;
24122 var isSrc = prefix === 'source';
24123
24124 if (!content[prefix]) {
24125 return;
24126 }
24127
24128 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
24129
24130 switch (rs.edgeType) {
24131 case 'self':
24132 case 'compound':
24133 case 'bezier':
24134 case 'multibezier':
24135 {
24136 var cps = createControlPointInfo();
24137 var selected;
24138 var startDist = 0;
24139 var totalDist = 0; // find the segment we're on
24140
24141 for (var i = 0; i < cps.length; i++) {
24142 var _cp = cps[isSrc ? i : cps.length - 1 - i];
24143
24144 for (var j = 0; j < _cp.segments.length; j++) {
24145 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
24146 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
24147 startDist = totalDist;
24148 totalDist += _seg.length;
24149
24150 if (totalDist >= offset || lastSeg) {
24151 selected = {
24152 cp: _cp,
24153 segment: _seg
24154 };
24155 break;
24156 }
24157 }
24158
24159 if (selected) {
24160 break;
24161 }
24162 }
24163
24164 var cp = selected.cp;
24165 var seg = selected.segment;
24166 var tSegment = (offset - startDist) / seg.length;
24167 var segDt = seg.t1 - seg.t0;
24168 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
24169 t = bound(0, t, 1);
24170 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
24171 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
24172 break;
24173 }
24174
24175 case 'straight':
24176 case 'segments':
24177 case 'haystack':
24178 {
24179 var d = 0,
24180 di,
24181 d0;
24182 var p0, p1;
24183 var l = rs.allpts.length;
24184
24185 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
24186 if (isSrc) {
24187 p0 = {
24188 x: rs.allpts[_i2],
24189 y: rs.allpts[_i2 + 1]
24190 };
24191 p1 = {
24192 x: rs.allpts[_i2 + 2],
24193 y: rs.allpts[_i2 + 3]
24194 };
24195 } else {
24196 p0 = {
24197 x: rs.allpts[l - 2 - _i2],
24198 y: rs.allpts[l - 1 - _i2]
24199 };
24200 p1 = {
24201 x: rs.allpts[l - 4 - _i2],
24202 y: rs.allpts[l - 3 - _i2]
24203 };
24204 }
24205
24206 di = dist(p0, p1);
24207 d0 = d;
24208 d += di;
24209
24210 if (d >= offset) {
24211 break;
24212 }
24213 }
24214
24215 var pD = offset - d0;
24216
24217 var _t = pD / di;
24218
24219 _t = bound(0, _t, 1);
24220 p = lineAt(p0, p1, _t);
24221 angle = lineAngle(p0, p1);
24222 break;
24223 }
24224 }
24225
24226 setRs('labelX', prefix, p.x);
24227 setRs('labelY', prefix, p.y);
24228 setRs('labelAutoAngle', prefix, angle);
24229 };
24230
24231 calculateEndProjection('source');
24232 calculateEndProjection('target');
24233 this.applyLabelDimensions(edge);
24234 };
24235
24236 BRp$6.applyLabelDimensions = function (ele) {
24237 this.applyPrefixedLabelDimensions(ele);
24238
24239 if (ele.isEdge()) {
24240 this.applyPrefixedLabelDimensions(ele, 'source');
24241 this.applyPrefixedLabelDimensions(ele, 'target');
24242 }
24243 };
24244
24245 BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
24246 var _p = ele._private;
24247 var text = this.getLabelText(ele, prefix);
24248 var labelDims = this.calculateLabelDimensions(ele, text);
24249 var lineHeight = ele.pstyle('line-height').pfValue;
24250 var textWrap = ele.pstyle('text-wrap').strValue;
24251 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
24252 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
24253 var normPerLineHeight = labelDims.height / numLines;
24254 var labelLineHeight = normPerLineHeight * lineHeight;
24255 var width = labelDims.width;
24256 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
24257 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
24258 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
24259 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
24260 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
24261 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
24262 };
24263
24264 BRp$6.getLabelText = function (ele, prefix) {
24265 var _p = ele._private;
24266 var pfd = prefix ? prefix + '-' : '';
24267 var text = ele.pstyle(pfd + 'label').strValue;
24268 var textTransform = ele.pstyle('text-transform').value;
24269
24270 var rscratch = function rscratch(propName, value) {
24271 if (value) {
24272 setPrefixedProperty(_p.rscratch, propName, prefix, value);
24273 return value;
24274 } else {
24275 return getPrefixedProperty(_p.rscratch, propName, prefix);
24276 }
24277 }; // for empty text, skip all processing
24278
24279
24280 if (!text) {
24281 return '';
24282 }
24283
24284 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
24285 text = text.toUpperCase();
24286 } else if (textTransform == 'lowercase') {
24287 text = text.toLowerCase();
24288 }
24289
24290 var wrapStyle = ele.pstyle('text-wrap').value;
24291
24292 if (wrapStyle === 'wrap') {
24293 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
24294
24295 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
24296 return rscratch('labelWrapCachedText');
24297 }
24298
24299 var zwsp = "\u200B";
24300 var lines = text.split('\n');
24301 var maxW = ele.pstyle('text-max-width').pfValue;
24302 var overflow = ele.pstyle('text-overflow-wrap').value;
24303 var overflowAny = overflow === 'anywhere';
24304 var wrappedLines = [];
24305 var wordsRegex = /[\s\u200b]+/;
24306 var wordSeparator = overflowAny ? '' : ' ';
24307
24308 for (var l = 0; l < lines.length; l++) {
24309 var line = lines[l];
24310 var lineDims = this.calculateLabelDimensions(ele, line);
24311 var lineW = lineDims.width;
24312
24313 if (overflowAny) {
24314 var processedLine = line.split('').join(zwsp);
24315 line = processedLine;
24316 }
24317
24318 if (lineW > maxW) {
24319 // line is too long
24320 var words = line.split(wordsRegex);
24321 var subline = '';
24322
24323 for (var w = 0; w < words.length; w++) {
24324 var word = words[w];
24325 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
24326 var testDims = this.calculateLabelDimensions(ele, testLine);
24327 var testW = testDims.width;
24328
24329 if (testW <= maxW) {
24330 // word fits on current line
24331 subline += word + wordSeparator;
24332 } else {
24333 // word starts new line
24334 if (subline) {
24335 wrappedLines.push(subline);
24336 }
24337
24338 subline = word + wordSeparator;
24339 }
24340 } // if there's remaining text, put it in a wrapped line
24341
24342
24343 if (!subline.match(/^[\s\u200b]+$/)) {
24344 wrappedLines.push(subline);
24345 }
24346 } else {
24347 // line is already short enough
24348 wrappedLines.push(line);
24349 }
24350 } // for
24351
24352
24353 rscratch('labelWrapCachedLines', wrappedLines);
24354 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
24355 rscratch('labelWrapKey', labelKey);
24356 } else if (wrapStyle === 'ellipsis') {
24357 var _maxW = ele.pstyle('text-max-width').pfValue;
24358 var ellipsized = '';
24359 var ellipsis = "\u2026";
24360 var incLastCh = false;
24361
24362 for (var i = 0; i < text.length; i++) {
24363 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
24364
24365 if (widthWithNextCh > _maxW) {
24366 break;
24367 }
24368
24369 ellipsized += text[i];
24370
24371 if (i === text.length - 1) {
24372 incLastCh = true;
24373 }
24374 }
24375
24376 if (!incLastCh) {
24377 ellipsized += ellipsis;
24378 }
24379
24380 return ellipsized;
24381 } // if ellipsize
24382
24383
24384 return text;
24385 };
24386
24387 BRp$6.getLabelJustification = function (ele) {
24388 var justification = ele.pstyle('text-justification').strValue;
24389 var textHalign = ele.pstyle('text-halign').strValue;
24390
24391 if (justification === 'auto') {
24392 if (ele.isNode()) {
24393 switch (textHalign) {
24394 case 'left':
24395 return 'right';
24396
24397 case 'right':
24398 return 'left';
24399
24400 default:
24401 return 'center';
24402 }
24403 } else {
24404 return 'center';
24405 }
24406 } else {
24407 return justification;
24408 }
24409 };
24410
24411 BRp$6.calculateLabelDimensions = function (ele, text) {
24412 var r = this;
24413 var cacheKey = hashString(text, ele._private.labelDimsKey);
24414 var cache = r.labelDimCache || (r.labelDimCache = []);
24415 var existingVal = cache[cacheKey];
24416
24417 if (existingVal != null) {
24418 return existingVal;
24419 }
24420
24421 var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
24422
24423 var fStyle = ele.pstyle('font-style').strValue;
24424 var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
24425 var family = ele.pstyle('font-family').strValue;
24426 var weight = ele.pstyle('font-weight').strValue;
24427 var div = this.labelCalcDiv;
24428
24429 if (!div) {
24430 div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
24431
24432 document.body.appendChild(div); // eslint-disable-line no-undef
24433 }
24434
24435 var ds = div.style; // from ele style
24436
24437 ds.fontFamily = family;
24438 ds.fontStyle = fStyle;
24439 ds.fontSize = size;
24440 ds.fontWeight = weight; // forced style
24441
24442 ds.position = 'absolute';
24443 ds.left = '-9999px';
24444 ds.top = '-9999px';
24445 ds.zIndex = '-1';
24446 ds.visibility = 'hidden';
24447 ds.pointerEvents = 'none';
24448 ds.padding = '0';
24449 ds.lineHeight = '1'; // - newlines must be taken into account for text-wrap:wrap
24450 // - since spaces are not collapsed, each space must be taken into account
24451
24452 ds.whiteSpace = 'pre'; // put label content in div
24453
24454 div.textContent = text;
24455 return cache[cacheKey] = {
24456 width: Math.ceil(div.clientWidth / sizeMult),
24457 height: Math.ceil(div.clientHeight / sizeMult)
24458 };
24459 };
24460
24461 BRp$6.calculateLabelAngle = function (ele, prefix) {
24462 var _p = ele._private;
24463 var rs = _p.rscratch;
24464 var isEdge = ele.isEdge();
24465 var prefixDash = prefix ? prefix + '-' : '';
24466 var rot = ele.pstyle(prefixDash + 'text-rotation');
24467 var rotStr = rot.strValue;
24468
24469 if (rotStr === 'none') {
24470 return 0;
24471 } else if (isEdge && rotStr === 'autorotate') {
24472 return rs.labelAutoAngle;
24473 } else if (rotStr === 'autorotate') {
24474 return 0;
24475 } else {
24476 return rot.pfValue;
24477 }
24478 };
24479
24480 BRp$6.calculateLabelAngles = function (ele) {
24481 var r = this;
24482 var isEdge = ele.isEdge();
24483 var _p = ele._private;
24484 var rs = _p.rscratch;
24485 rs.labelAngle = r.calculateLabelAngle(ele);
24486
24487 if (isEdge) {
24488 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
24489 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
24490 }
24491 };
24492
24493 var BRp$7 = {};
24494 var TOO_SMALL_CUT_RECT = 28;
24495 var warnedCutRect = false;
24496
24497 BRp$7.getNodeShape = function (node) {
24498 var r = this;
24499 var shape = node.pstyle('shape').value;
24500
24501 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
24502 if (!warnedCutRect) {
24503 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
24504 warnedCutRect = true;
24505 }
24506
24507 return 'rectangle';
24508 }
24509
24510 if (node.isParent()) {
24511 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
24512 return shape;
24513 } else {
24514 return 'rectangle';
24515 }
24516 }
24517
24518 if (shape === 'polygon') {
24519 var points = node.pstyle('shape-polygon-points').value;
24520 return r.nodeShapes.makePolygon(points).name;
24521 }
24522
24523 return shape;
24524 };
24525
24526 var BRp$8 = {};
24527
24528 BRp$8.registerCalculationListeners = function () {
24529 var cy = this.cy;
24530 var elesToUpdate = cy.collection();
24531 var r = this;
24532
24533 var enqueue = function enqueue(eles) {
24534 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
24535 elesToUpdate.merge(eles);
24536
24537 if (dirtyStyleCaches) {
24538 for (var i = 0; i < eles.length; i++) {
24539 var ele = eles[i];
24540 var _p = ele._private;
24541 var rstyle = _p.rstyle;
24542 rstyle.clean = false;
24543 rstyle.cleanConnected = false;
24544 }
24545 }
24546 };
24547
24548 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24549 var ele = e.target;
24550 enqueue(ele);
24551 }).on('style.* background.*', function onDirtyStyle(e) {
24552 var ele = e.target;
24553 enqueue(ele, false);
24554 });
24555
24556 var updateEleCalcs = function updateEleCalcs(willDraw) {
24557 if (willDraw) {
24558 var fns = r.onUpdateEleCalcsFns;
24559
24560 for (var i = 0; i < elesToUpdate.length; i++) {
24561 var ele = elesToUpdate[i];
24562 var rstyle = ele._private.rstyle;
24563
24564 if (ele.isNode() && !rstyle.cleanConnected) {
24565 enqueue(ele.connectedEdges());
24566 rstyle.cleanConnected = true;
24567 }
24568 }
24569
24570 if (fns) {
24571 for (var _i = 0; _i < fns.length; _i++) {
24572 var fn = fns[_i];
24573 fn(willDraw, elesToUpdate);
24574 }
24575 }
24576
24577 r.recalculateRenderedStyle(elesToUpdate);
24578 elesToUpdate = cy.collection();
24579 }
24580 };
24581
24582 r.flushRenderedStyleQueue = function () {
24583 updateEleCalcs(true);
24584 };
24585
24586 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24587 };
24588
24589 BRp$8.onUpdateEleCalcs = function (fn) {
24590 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24591 fns.push(fn);
24592 };
24593
24594 BRp$8.recalculateRenderedStyle = function (eles, useCache) {
24595 var isCleanConnected = function isCleanConnected(ele) {
24596 return ele._private.rstyle.cleanConnected;
24597 };
24598
24599 var edges = [];
24600 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24601
24602 if (this.destroyed) {
24603 return;
24604 } // use cache by default for perf
24605
24606
24607 if (useCache === undefined) {
24608 useCache = true;
24609 }
24610
24611 for (var i = 0; i < eles.length; i++) {
24612 var ele = eles[i];
24613 var _p = ele._private;
24614 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24615 // (and a request for recalc may come in between frames)
24616
24617 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24618 rstyle.clean = false;
24619 } // only update if dirty and in graph
24620
24621
24622 if (useCache && rstyle.clean || ele.removed()) {
24623 continue;
24624 } // only update if not display: none
24625
24626
24627 if (ele.pstyle('display').value === 'none') {
24628 continue;
24629 }
24630
24631 if (_p.group === 'nodes') {
24632 nodes.push(ele);
24633 } else {
24634 // edges
24635 edges.push(ele);
24636 }
24637
24638 rstyle.clean = true;
24639 } // update node data from projections
24640
24641
24642 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24643 var _ele = nodes[_i2];
24644 var _p2 = _ele._private;
24645 var _rstyle = _p2.rstyle;
24646
24647 var pos = _ele.position();
24648
24649 this.recalculateNodeLabelProjection(_ele);
24650 _rstyle.nodeX = pos.x;
24651 _rstyle.nodeY = pos.y;
24652 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24653 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24654 }
24655
24656 this.recalculateEdgeProjections(edges); // update edge data from projections
24657
24658 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24659 var _ele2 = edges[_i3];
24660 var _p3 = _ele2._private;
24661 var _rstyle2 = _p3.rstyle;
24662 var rs = _p3.rscratch; // update rstyle positions
24663
24664 _rstyle2.srcX = rs.arrowStartX;
24665 _rstyle2.srcY = rs.arrowStartY;
24666 _rstyle2.tgtX = rs.arrowEndX;
24667 _rstyle2.tgtY = rs.arrowEndY;
24668 _rstyle2.midX = rs.midX;
24669 _rstyle2.midY = rs.midY;
24670 _rstyle2.labelAngle = rs.labelAngle;
24671 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24672 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24673 }
24674 };
24675
24676 var BRp$9 = {};
24677
24678 BRp$9.updateCachedGrabbedEles = function () {
24679 var eles = this.cachedZSortedEles;
24680
24681 if (!eles) {
24682 // just let this be recalculated on the next z sort tick
24683 return;
24684 }
24685
24686 eles.drag = [];
24687 eles.nondrag = [];
24688 var grabTargets = [];
24689
24690 for (var i = 0; i < eles.length; i++) {
24691 var ele = eles[i];
24692 var rs = ele._private.rscratch;
24693
24694 if (ele.grabbed() && !ele.isParent()) {
24695 grabTargets.push(ele);
24696 } else if (rs.inDragLayer) {
24697 eles.drag.push(ele);
24698 } else {
24699 eles.nondrag.push(ele);
24700 }
24701 } // put the grab target nodes last so it's on top of its neighbourhood
24702
24703
24704 for (var i = 0; i < grabTargets.length; i++) {
24705 var ele = grabTargets[i];
24706 eles.drag.push(ele);
24707 }
24708 };
24709
24710 BRp$9.invalidateCachedZSortedEles = function () {
24711 this.cachedZSortedEles = null;
24712 };
24713
24714 BRp$9.getCachedZSortedEles = function (forceRecalc) {
24715 if (forceRecalc || !this.cachedZSortedEles) {
24716 var eles = this.cy.mutableElements().toArray();
24717 eles.sort(zIndexSort);
24718 eles.interactive = eles.filter(function (ele) {
24719 return ele.interactive();
24720 });
24721 this.cachedZSortedEles = eles;
24722 this.updateCachedGrabbedEles();
24723 } else {
24724 eles = this.cachedZSortedEles;
24725 }
24726
24727 return eles;
24728 };
24729
24730 var BRp$a = {};
24731 [BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24732 extend(BRp$a, props);
24733 });
24734
24735 var BRp$b = {};
24736
24737 BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24738 var r = this;
24739 var imageCache = r.imageCache = r.imageCache || {};
24740 var cache = imageCache[url];
24741
24742 if (cache) {
24743 if (!cache.image.complete) {
24744 cache.image.addEventListener('load', onLoad);
24745 }
24746
24747 return cache.image;
24748 } else {
24749 cache = imageCache[url] = imageCache[url] || {};
24750 var image = cache.image = new Image(); // eslint-disable-line no-undef
24751
24752 image.addEventListener('load', onLoad);
24753 image.addEventListener('error', function () {
24754 image.error = true;
24755 }); // #1582 safari doesn't load data uris with crossOrigin properly
24756 // https://bugs.webkit.org/show_bug.cgi?id=123978
24757
24758 var dataUriPrefix = 'data:';
24759 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24760
24761 if (!isDataUri) {
24762 image.crossOrigin = crossOrigin; // prevent tainted canvas
24763 }
24764
24765 image.src = url;
24766 return image;
24767 }
24768 };
24769
24770 var BRp$c = {};
24771 /* global document, window, ResizeObserver, MutationObserver */
24772
24773 BRp$c.registerBinding = function (target, event, handler, useCapture) {
24774 // eslint-disable-line no-unused-vars
24775 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24776
24777 var b = this.binder(target);
24778 return b.on.apply(b, args);
24779 };
24780
24781 BRp$c.binder = function (tgt) {
24782 var r = this;
24783 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24784
24785 if (r.supportsPassiveEvents == null) {
24786 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24787 var supportsPassive = false;
24788
24789 try {
24790 var opts = Object.defineProperty({}, 'passive', {
24791 get: function get() {
24792 supportsPassive = true;
24793 return true;
24794 }
24795 });
24796 window.addEventListener('test', null, opts);
24797 } catch (err) {// not supported
24798 }
24799
24800 r.supportsPassiveEvents = supportsPassive;
24801 }
24802
24803 var on = function on(event, handler, useCapture) {
24804 var args = Array.prototype.slice.call(arguments);
24805
24806 if (tgtIsDom && r.supportsPassiveEvents) {
24807 // replace useCapture w/ opts obj
24808 args[2] = {
24809 capture: useCapture != null ? useCapture : false,
24810 passive: false,
24811 once: false
24812 };
24813 }
24814
24815 r.bindings.push({
24816 target: tgt,
24817 args: args
24818 });
24819 (tgt.addEventListener || tgt.on).apply(tgt, args);
24820 return this;
24821 };
24822
24823 return {
24824 on: on,
24825 addEventListener: on,
24826 addListener: on,
24827 bind: on
24828 };
24829 };
24830
24831 BRp$c.nodeIsDraggable = function (node) {
24832 return node && node.isNode() && !node.locked() && node.grabbable();
24833 };
24834
24835 BRp$c.nodeIsGrabbable = function (node) {
24836 return this.nodeIsDraggable(node) && node.interactive();
24837 };
24838
24839 BRp$c.load = function () {
24840 var r = this;
24841
24842 var isSelected = function isSelected(ele) {
24843 return ele.selected();
24844 };
24845
24846 var triggerEvents = function triggerEvents(target, names, e, position) {
24847 if (target == null) {
24848 target = r.cy;
24849 }
24850
24851 for (var i = 0; i < names.length; i++) {
24852 var name = names[i];
24853 target.emit({
24854 originalEvent: e,
24855 type: name,
24856 position: position
24857 });
24858 }
24859 };
24860
24861 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24862 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24863 };
24864
24865 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24866 var allowPassthrough = true;
24867
24868 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24869 // a grabbable compound node below the ele => no passthrough panning
24870 for (var i = 0; downs && i < downs.length; i++) {
24871 var down = downs[i];
24872
24873 if (down.isNode() && down.isParent()) {
24874 allowPassthrough = false;
24875 break;
24876 }
24877 }
24878 } else {
24879 allowPassthrough = true;
24880 }
24881
24882 return allowPassthrough;
24883 };
24884
24885 var setGrabbed = function setGrabbed(ele) {
24886 ele[0]._private.grabbed = true;
24887 };
24888
24889 var setFreed = function setFreed(ele) {
24890 ele[0]._private.grabbed = false;
24891 };
24892
24893 var setInDragLayer = function setInDragLayer(ele) {
24894 ele[0]._private.rscratch.inDragLayer = true;
24895 };
24896
24897 var setOutDragLayer = function setOutDragLayer(ele) {
24898 ele[0]._private.rscratch.inDragLayer = false;
24899 };
24900
24901 var setGrabTarget = function setGrabTarget(ele) {
24902 ele[0]._private.rscratch.isGrabTarget = true;
24903 };
24904
24905 var removeGrabTarget = function removeGrabTarget(ele) {
24906 ele[0]._private.rscratch.isGrabTarget = false;
24907 };
24908
24909 var addToDragList = function addToDragList(ele, opts) {
24910 var list = opts.addToList;
24911 var listHasEle = list.has(ele);
24912
24913 if (!listHasEle) {
24914 list.merge(ele);
24915 setGrabbed(ele);
24916 }
24917 }; // helper function to determine which child nodes and inner edges
24918 // of a compound node to be dragged as well as the grabbed and selected nodes
24919
24920
24921 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24922 if (!node.cy().hasCompoundNodes()) {
24923 return;
24924 }
24925
24926 if (opts.inDragLayer == null && opts.addToList == null) {
24927 return;
24928 } // nothing to do
24929
24930
24931 var innerNodes = node.descendants();
24932
24933 if (opts.inDragLayer) {
24934 innerNodes.forEach(setInDragLayer);
24935 innerNodes.connectedEdges().forEach(setInDragLayer);
24936 }
24937
24938 if (opts.addToList) {
24939 opts.addToList.unmerge(innerNodes);
24940 }
24941 }; // adds the given nodes and its neighbourhood to the drag layer
24942
24943
24944 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24945 opts = opts || {};
24946 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24947
24948 if (opts.inDragLayer) {
24949 nodes.forEach(setInDragLayer);
24950 nodes.neighborhood().stdFilter(function (ele) {
24951 return !hasCompoundNodes || ele.isEdge();
24952 }).forEach(setInDragLayer);
24953 }
24954
24955 if (opts.addToList) {
24956 nodes.forEach(function (ele) {
24957 addToDragList(ele, opts);
24958 });
24959 }
24960
24961 addDescendantsToDrag(nodes, opts); // always add to drag
24962 // also add nodes and edges related to the topmost ancestor
24963
24964 updateAncestorsInDragLayer(nodes, {
24965 inDragLayer: opts.inDragLayer
24966 });
24967 r.updateCachedGrabbedEles();
24968 };
24969
24970 var addNodeToDrag = addNodesToDrag;
24971
24972 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24973 if (!grabbedEles) {
24974 return;
24975 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24976
24977
24978 r.getCachedZSortedEles().forEach(function (ele) {
24979 setFreed(ele);
24980 setOutDragLayer(ele);
24981 removeGrabTarget(ele);
24982 });
24983 r.updateCachedGrabbedEles();
24984 }; // helper function to determine which ancestor nodes and edges should go
24985 // to the drag layer (or should be removed from drag layer).
24986
24987
24988 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24989 if (opts.inDragLayer == null && opts.addToList == null) {
24990 return;
24991 } // nothing to do
24992
24993
24994 if (!node.cy().hasCompoundNodes()) {
24995 return;
24996 } // find top-level parent
24997
24998
24999 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
25000
25001 if (parent.same(node)) {
25002 return;
25003 }
25004
25005 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
25006 var edges = nodes.connectedEdges();
25007
25008 if (opts.inDragLayer) {
25009 edges.forEach(setInDragLayer);
25010 nodes.forEach(setInDragLayer);
25011 }
25012
25013 if (opts.addToList) {
25014 nodes.forEach(function (ele) {
25015 addToDragList(ele, opts);
25016 });
25017 }
25018 };
25019
25020 var blurActiveDomElement = function blurActiveDomElement() {
25021 if (document.activeElement != null && document.activeElement.blur != null) {
25022 document.activeElement.blur();
25023 }
25024 };
25025
25026 var haveMutationsApi = typeof MutationObserver !== 'undefined';
25027 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
25028
25029 if (haveMutationsApi) {
25030 r.removeObserver = new MutationObserver(function (mutns) {
25031 // eslint-disable-line no-undef
25032 for (var i = 0; i < mutns.length; i++) {
25033 var mutn = mutns[i];
25034 var rNodes = mutn.removedNodes;
25035
25036 if (rNodes) {
25037 for (var j = 0; j < rNodes.length; j++) {
25038 var rNode = rNodes[j];
25039
25040 if (rNode === r.container) {
25041 r.destroy();
25042 break;
25043 }
25044 }
25045 }
25046 }
25047 });
25048
25049 if (r.container.parentNode) {
25050 r.removeObserver.observe(r.container.parentNode, {
25051 childList: true
25052 });
25053 }
25054 } else {
25055 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
25056 // eslint-disable-line no-unused-vars
25057 r.destroy();
25058 });
25059 }
25060
25061 var onResize = lodash_debounce(function () {
25062 r.cy.resize();
25063 }, 100);
25064
25065 if (haveMutationsApi) {
25066 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
25067
25068 r.styleObserver.observe(r.container, {
25069 attributes: true
25070 });
25071 } // auto resize
25072
25073
25074 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
25075
25076 if (haveResizeObserverApi) {
25077 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
25078
25079 r.resizeObserver.observe(r.container);
25080 }
25081
25082 var forEachUp = function forEachUp(domEle, fn) {
25083 while (domEle != null) {
25084 fn(domEle);
25085 domEle = domEle.parentNode;
25086 }
25087 };
25088
25089 var invalidateCoords = function invalidateCoords() {
25090 r.invalidateContainerClientCoordsCache();
25091 };
25092
25093 forEachUp(r.container, function (domEle) {
25094 r.registerBinding(domEle, 'transitionend', invalidateCoords);
25095 r.registerBinding(domEle, 'animationend', invalidateCoords);
25096 r.registerBinding(domEle, 'scroll', invalidateCoords);
25097 }); // stop right click menu from appearing on cy
25098
25099 r.registerBinding(r.container, 'contextmenu', function (e) {
25100 e.preventDefault();
25101 });
25102
25103 var inBoxSelection = function inBoxSelection() {
25104 return r.selection[4] !== 0;
25105 };
25106
25107 var eventInContainer = function eventInContainer(e) {
25108 // save cycles if mouse events aren't to be captured
25109 var containerPageCoords = r.findContainerClientCoords();
25110 var x = containerPageCoords[0];
25111 var y = containerPageCoords[1];
25112 var width = containerPageCoords[2];
25113 var height = containerPageCoords[3];
25114 var positions = e.touches ? e.touches : [e];
25115 var atLeastOnePosInside = false;
25116
25117 for (var i = 0; i < positions.length; i++) {
25118 var p = positions[i];
25119
25120 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
25121 atLeastOnePosInside = true;
25122 break;
25123 }
25124 }
25125
25126 if (!atLeastOnePosInside) {
25127 return false;
25128 }
25129
25130 var container = r.container;
25131 var target = e.target;
25132 var tParent = target.parentNode;
25133 var containerIsTarget = false;
25134
25135 while (tParent) {
25136 if (tParent === container) {
25137 containerIsTarget = true;
25138 break;
25139 }
25140
25141 tParent = tParent.parentNode;
25142 }
25143
25144 if (!containerIsTarget) {
25145 return false;
25146 } // if target is outisde cy container, then this event is not for us
25147
25148
25149 return true;
25150 }; // Primary key
25151
25152
25153 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
25154 if (!eventInContainer(e)) {
25155 return;
25156 }
25157
25158 e.preventDefault();
25159 blurActiveDomElement();
25160 r.hoverData.capture = true;
25161 r.hoverData.which = e.which;
25162 var cy = r.cy;
25163 var gpos = [e.clientX, e.clientY];
25164 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
25165 var select = r.selection;
25166 var nears = r.findNearestElements(pos[0], pos[1], true, false);
25167 var near = nears[0];
25168 var draggedElements = r.dragData.possibleDragElements;
25169 r.hoverData.mdownPos = pos;
25170 r.hoverData.mdownGPos = gpos;
25171
25172 var checkForTaphold = function checkForTaphold() {
25173 r.hoverData.tapholdCancelled = false;
25174 clearTimeout(r.hoverData.tapholdTimeout);
25175 r.hoverData.tapholdTimeout = setTimeout(function () {
25176 if (r.hoverData.tapholdCancelled) {
25177 return;
25178 } else {
25179 var ele = r.hoverData.down;
25180
25181 if (ele) {
25182 ele.emit({
25183 originalEvent: e,
25184 type: 'taphold',
25185 position: {
25186 x: pos[0],
25187 y: pos[1]
25188 }
25189 });
25190 } else {
25191 cy.emit({
25192 originalEvent: e,
25193 type: 'taphold',
25194 position: {
25195 x: pos[0],
25196 y: pos[1]
25197 }
25198 });
25199 }
25200 }
25201 }, r.tapholdDuration);
25202 }; // Right click button
25203
25204
25205 if (e.which == 3) {
25206 r.hoverData.cxtStarted = true;
25207 var cxtEvt = {
25208 originalEvent: e,
25209 type: 'cxttapstart',
25210 position: {
25211 x: pos[0],
25212 y: pos[1]
25213 }
25214 };
25215
25216 if (near) {
25217 near.activate();
25218 near.emit(cxtEvt);
25219 r.hoverData.down = near;
25220 } else {
25221 cy.emit(cxtEvt);
25222 }
25223
25224 r.hoverData.downTime = new Date().getTime();
25225 r.hoverData.cxtDragged = false; // Primary button
25226 } else if (e.which == 1) {
25227 if (near) {
25228 near.activate();
25229 } // Element dragging
25230
25231
25232 {
25233 // If something is under the cursor and it is draggable, prepare to grab it
25234 if (near != null) {
25235 if (r.nodeIsGrabbable(near)) {
25236 var makeEvent = function makeEvent(type) {
25237 return {
25238 originalEvent: e,
25239 type: type,
25240 position: {
25241 x: pos[0],
25242 y: pos[1]
25243 }
25244 };
25245 };
25246
25247 var triggerGrab = function triggerGrab(ele) {
25248 ele.emit(makeEvent('grab'));
25249 };
25250
25251 setGrabTarget(near);
25252
25253 if (!near.selected()) {
25254 draggedElements = r.dragData.possibleDragElements = cy.collection();
25255 addNodeToDrag(near, {
25256 addToList: draggedElements
25257 });
25258 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
25259 } else {
25260 draggedElements = r.dragData.possibleDragElements = cy.collection();
25261 var selectedNodes = cy.$(function (ele) {
25262 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
25263 });
25264 addNodesToDrag(selectedNodes, {
25265 addToList: draggedElements
25266 });
25267 near.emit(makeEvent('grabon'));
25268 selectedNodes.forEach(triggerGrab);
25269 }
25270
25271 r.redrawHint('eles', true);
25272 r.redrawHint('drag', true);
25273 }
25274 }
25275
25276 r.hoverData.down = near;
25277 r.hoverData.downs = nears;
25278 r.hoverData.downTime = new Date().getTime();
25279 }
25280 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
25281 x: pos[0],
25282 y: pos[1]
25283 });
25284
25285 if (near == null) {
25286 select[4] = 1;
25287 r.data.bgActivePosistion = {
25288 x: pos[0],
25289 y: pos[1]
25290 };
25291 r.redrawHint('select', true);
25292 r.redraw();
25293 } else if (near.pannable()) {
25294 select[4] = 1; // for future pan
25295 }
25296
25297 checkForTaphold();
25298 } // Initialize selection box coordinates
25299
25300
25301 select[0] = select[2] = pos[0];
25302 select[1] = select[3] = pos[1];
25303 }, false);
25304 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
25305 // eslint-disable-line no-undef
25306 var capture = r.hoverData.capture;
25307
25308 if (!capture && !eventInContainer(e)) {
25309 return;
25310 }
25311
25312 var preventDefault = false;
25313 var cy = r.cy;
25314 var zoom = cy.zoom();
25315 var gpos = [e.clientX, e.clientY];
25316 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
25317 var mdownPos = r.hoverData.mdownPos;
25318 var mdownGPos = r.hoverData.mdownGPos;
25319 var select = r.selection;
25320 var near = null;
25321
25322 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
25323 near = r.findNearestElement(pos[0], pos[1], true, false);
25324 }
25325
25326 var last = r.hoverData.last;
25327 var down = r.hoverData.down;
25328 var disp = [pos[0] - select[2], pos[1] - select[3]];
25329 var draggedElements = r.dragData.possibleDragElements;
25330 var isOverThresholdDrag;
25331
25332 if (mdownGPos) {
25333 var dx = gpos[0] - mdownGPos[0];
25334 var dx2 = dx * dx;
25335 var dy = gpos[1] - mdownGPos[1];
25336 var dy2 = dy * dy;
25337 var dist2 = dx2 + dy2;
25338 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
25339 }
25340
25341 var multSelKeyDown = isMultSelKeyDown(e);
25342
25343 if (isOverThresholdDrag) {
25344 r.hoverData.tapholdCancelled = true;
25345 }
25346
25347 var updateDragDelta = function updateDragDelta() {
25348 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
25349
25350 if (dragDelta.length === 0) {
25351 dragDelta.push(disp[0]);
25352 dragDelta.push(disp[1]);
25353 } else {
25354 dragDelta[0] += disp[0];
25355 dragDelta[1] += disp[1];
25356 }
25357 };
25358
25359 preventDefault = true;
25360 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
25361 x: pos[0],
25362 y: pos[1]
25363 });
25364
25365 var goIntoBoxMode = function goIntoBoxMode() {
25366 r.data.bgActivePosistion = undefined;
25367
25368 if (!r.hoverData.selecting) {
25369 cy.emit({
25370 originalEvent: e,
25371 type: 'boxstart',
25372 position: {
25373 x: pos[0],
25374 y: pos[1]
25375 }
25376 });
25377 }
25378
25379 select[4] = 1;
25380 r.hoverData.selecting = true;
25381 r.redrawHint('select', true);
25382 r.redraw();
25383 }; // trigger context drag if rmouse down
25384
25385
25386 if (r.hoverData.which === 3) {
25387 // but only if over threshold
25388 if (isOverThresholdDrag) {
25389 var cxtEvt = {
25390 originalEvent: e,
25391 type: 'cxtdrag',
25392 position: {
25393 x: pos[0],
25394 y: pos[1]
25395 }
25396 };
25397
25398 if (down) {
25399 down.emit(cxtEvt);
25400 } else {
25401 cy.emit(cxtEvt);
25402 }
25403
25404 r.hoverData.cxtDragged = true;
25405
25406 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
25407 if (r.hoverData.cxtOver) {
25408 r.hoverData.cxtOver.emit({
25409 originalEvent: e,
25410 type: 'cxtdragout',
25411 position: {
25412 x: pos[0],
25413 y: pos[1]
25414 }
25415 });
25416 }
25417
25418 r.hoverData.cxtOver = near;
25419
25420 if (near) {
25421 near.emit({
25422 originalEvent: e,
25423 type: 'cxtdragover',
25424 position: {
25425 x: pos[0],
25426 y: pos[1]
25427 }
25428 });
25429 }
25430 }
25431 } // Check if we are drag panning the entire graph
25432
25433 } else if (r.hoverData.dragging) {
25434 preventDefault = true;
25435
25436 if (cy.panningEnabled() && cy.userPanningEnabled()) {
25437 var deltaP;
25438
25439 if (r.hoverData.justStartedPan) {
25440 var mdPos = r.hoverData.mdownPos;
25441 deltaP = {
25442 x: (pos[0] - mdPos[0]) * zoom,
25443 y: (pos[1] - mdPos[1]) * zoom
25444 };
25445 r.hoverData.justStartedPan = false;
25446 } else {
25447 deltaP = {
25448 x: disp[0] * zoom,
25449 y: disp[1] * zoom
25450 };
25451 }
25452
25453 cy.panBy(deltaP);
25454 r.hoverData.dragged = true;
25455 } // Needs reproject due to pan changing viewport
25456
25457
25458 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
25459 } else if (select[4] == 1 && (down == null || down.pannable())) {
25460 if (isOverThresholdDrag) {
25461 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
25462 goIntoBoxMode();
25463 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
25464 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
25465
25466 if (allowPassthrough) {
25467 r.hoverData.dragging = true;
25468 r.hoverData.justStartedPan = true;
25469 select[4] = 0;
25470 r.data.bgActivePosistion = array2point(mdownPos);
25471 r.redrawHint('select', true);
25472 r.redraw();
25473 }
25474 }
25475
25476 if (down && down.pannable() && down.active()) {
25477 down.unactivate();
25478 }
25479 }
25480 } else {
25481 if (down && down.pannable() && down.active()) {
25482 down.unactivate();
25483 }
25484
25485 if ((!down || !down.grabbed()) && near != last) {
25486 if (last) {
25487 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
25488 x: pos[0],
25489 y: pos[1]
25490 });
25491 }
25492
25493 if (near) {
25494 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
25495 x: pos[0],
25496 y: pos[1]
25497 });
25498 }
25499
25500 r.hoverData.last = near;
25501 }
25502
25503 if (down) {
25504 if (isOverThresholdDrag) {
25505 // then we can take action
25506 if (cy.boxSelectionEnabled() && multSelKeyDown) {
25507 // then selection overrides
25508 if (down && down.grabbed()) {
25509 freeDraggedElements(draggedElements);
25510 down.emit('freeon');
25511 draggedElements.emit('free');
25512
25513 if (r.dragData.didDrag) {
25514 down.emit('dragfreeon');
25515 draggedElements.emit('dragfree');
25516 }
25517 }
25518
25519 goIntoBoxMode();
25520 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
25521 // drag node
25522 var justStartedDrag = !r.dragData.didDrag;
25523
25524 if (justStartedDrag) {
25525 r.redrawHint('eles', true);
25526 }
25527
25528 r.dragData.didDrag = true; // indicate that we actually did drag the node
25529
25530 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
25531
25532 if (!r.hoverData.draggingEles) {
25533 addNodesToDrag(draggedElements, {
25534 inDragLayer: true
25535 });
25536 }
25537
25538 var totalShift = {
25539 x: 0,
25540 y: 0
25541 };
25542
25543 if (number(disp[0]) && number(disp[1])) {
25544 totalShift.x += disp[0];
25545 totalShift.y += disp[1];
25546
25547 if (justStartedDrag) {
25548 var dragDelta = r.hoverData.dragDelta;
25549
25550 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25551 totalShift.x += dragDelta[0];
25552 totalShift.y += dragDelta[1];
25553 }
25554 }
25555 }
25556
25557 for (var i = 0; i < draggedElements.length; i++) {
25558 var dEle = draggedElements[i];
25559
25560 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
25561 toTrigger.merge(dEle);
25562 }
25563 }
25564
25565 r.hoverData.draggingEles = true;
25566 toTrigger.silentShift(totalShift).emit('position drag');
25567 r.redrawHint('drag', true);
25568 r.redraw();
25569 }
25570 } else {
25571 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25572 updateDragDelta();
25573 }
25574 } // prevent the dragging from triggering text selection on the page
25575
25576
25577 preventDefault = true;
25578 }
25579
25580 select[2] = pos[0];
25581 select[3] = pos[1];
25582
25583 if (preventDefault) {
25584 if (e.stopPropagation) e.stopPropagation();
25585 if (e.preventDefault) e.preventDefault();
25586 return false;
25587 }
25588 }, false);
25589 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25590 // eslint-disable-line no-undef
25591 var capture = r.hoverData.capture;
25592
25593 if (!capture) {
25594 return;
25595 }
25596
25597 r.hoverData.capture = false;
25598 var cy = r.cy;
25599 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25600 var select = r.selection;
25601 var near = r.findNearestElement(pos[0], pos[1], true, false);
25602 var draggedElements = r.dragData.possibleDragElements;
25603 var down = r.hoverData.down;
25604 var multSelKeyDown = isMultSelKeyDown(e);
25605
25606 if (r.data.bgActivePosistion) {
25607 r.redrawHint('select', true);
25608 r.redraw();
25609 }
25610
25611 r.hoverData.tapholdCancelled = true;
25612 r.data.bgActivePosistion = undefined; // not active bg now
25613
25614 if (down) {
25615 down.unactivate();
25616 }
25617
25618 if (r.hoverData.which === 3) {
25619 var cxtEvt = {
25620 originalEvent: e,
25621 type: 'cxttapend',
25622 position: {
25623 x: pos[0],
25624 y: pos[1]
25625 }
25626 };
25627
25628 if (down) {
25629 down.emit(cxtEvt);
25630 } else {
25631 cy.emit(cxtEvt);
25632 }
25633
25634 if (!r.hoverData.cxtDragged) {
25635 var cxtTap = {
25636 originalEvent: e,
25637 type: 'cxttap',
25638 position: {
25639 x: pos[0],
25640 y: pos[1]
25641 }
25642 };
25643
25644 if (down) {
25645 down.emit(cxtTap);
25646 } else {
25647 cy.emit(cxtTap);
25648 }
25649 }
25650
25651 r.hoverData.cxtDragged = false;
25652 r.hoverData.which = null;
25653 } else if (r.hoverData.which === 1) {
25654 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25655 x: pos[0],
25656 y: pos[1]
25657 });
25658
25659 if (!r.dragData.didDrag // didn't move a node around
25660 && !r.hoverData.dragged // didn't pan
25661 && !r.hoverData.selecting // not box selection
25662 && !r.hoverData.isOverThresholdDrag // didn't move too much
25663 ) {
25664 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
25665 x: pos[0],
25666 y: pos[1]
25667 });
25668 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25669
25670
25671 if (down == null && // not mousedown on node
25672 !r.dragData.didDrag // didn't move the node around
25673 && !r.hoverData.selecting // not box selection
25674 && !r.hoverData.dragged // didn't pan
25675 && !isMultSelKeyDown(e)) {
25676 cy.$(isSelected).unselect(['tapunselect']);
25677
25678 if (draggedElements.length > 0) {
25679 r.redrawHint('eles', true);
25680 }
25681
25682 r.dragData.possibleDragElements = draggedElements = cy.collection();
25683 } // Single selection
25684
25685
25686 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25687 if (near != null && near._private.selectable) {
25688 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25689 if (near.selected()) {
25690 near.unselect(['tapunselect']);
25691 } else {
25692 near.select(['tapselect']);
25693 }
25694 } else {
25695 if (!multSelKeyDown) {
25696 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25697 near.select(['tapselect']);
25698 }
25699 }
25700
25701 r.redrawHint('eles', true);
25702 }
25703 }
25704
25705 if (r.hoverData.selecting) {
25706 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25707 r.redrawHint('select', true);
25708
25709 if (box.length > 0) {
25710 r.redrawHint('eles', true);
25711 }
25712
25713 cy.emit({
25714 type: 'boxend',
25715 originalEvent: e,
25716 position: {
25717 x: pos[0],
25718 y: pos[1]
25719 }
25720 });
25721
25722 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25723 return ele.selectable() && !ele.selected();
25724 };
25725
25726 if (cy.selectionType() === 'additive') {
25727 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25728 } else {
25729 if (!multSelKeyDown) {
25730 cy.$(isSelected).unmerge(box).unselect();
25731 }
25732
25733 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25734 } // always need redraw in case eles unselectable
25735
25736
25737 r.redraw();
25738 } // Cancel drag pan
25739
25740
25741 if (r.hoverData.dragging) {
25742 r.hoverData.dragging = false;
25743 r.redrawHint('select', true);
25744 r.redrawHint('eles', true);
25745 r.redraw();
25746 }
25747
25748 if (!select[4]) {
25749 r.redrawHint('drag', true);
25750 r.redrawHint('eles', true);
25751 var downWasGrabbed = down && down.grabbed();
25752 freeDraggedElements(draggedElements);
25753
25754 if (downWasGrabbed) {
25755 down.emit('freeon');
25756 draggedElements.emit('free');
25757
25758 if (r.dragData.didDrag) {
25759 down.emit('dragfreeon');
25760 draggedElements.emit('dragfree');
25761 }
25762 }
25763 }
25764 } // else not right mouse
25765
25766
25767 select[4] = 0;
25768 r.hoverData.down = null;
25769 r.hoverData.cxtStarted = false;
25770 r.hoverData.draggingEles = false;
25771 r.hoverData.selecting = false;
25772 r.hoverData.isOverThresholdDrag = false;
25773 r.dragData.didDrag = false;
25774 r.hoverData.dragged = false;
25775 r.hoverData.dragDelta = [];
25776 r.hoverData.mdownPos = null;
25777 r.hoverData.mdownGPos = null;
25778 }, false);
25779
25780 var wheelHandler = function wheelHandler(e) {
25781 if (r.scrollingPage) {
25782 return;
25783 } // while scrolling, ignore wheel-to-zoom
25784
25785
25786 var cy = r.cy;
25787 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25788 var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
25789
25790 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25791 // if pan dragging or cxt dragging, wheel movements make no zoom
25792 e.preventDefault();
25793 return;
25794 }
25795
25796 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25797 e.preventDefault();
25798 r.data.wheelZooming = true;
25799 clearTimeout(r.data.wheelTimeout);
25800 r.data.wheelTimeout = setTimeout(function () {
25801 r.data.wheelZooming = false;
25802 r.redrawHint('eles', true);
25803 r.redraw();
25804 }, 150);
25805 var diff;
25806
25807 if (e.deltaY != null) {
25808 diff = e.deltaY / -250;
25809 } else if (e.wheelDeltaY != null) {
25810 diff = e.wheelDeltaY / 1000;
25811 } else {
25812 diff = e.wheelDelta / 1000;
25813 }
25814
25815 diff = diff * r.wheelSensitivity;
25816 var needsWheelFix = e.deltaMode === 1;
25817
25818 if (needsWheelFix) {
25819 // fixes slow wheel events on ff/linux and ff/windows
25820 diff *= 33;
25821 }
25822
25823 cy.zoom({
25824 level: cy.zoom() * Math.pow(10, diff),
25825 renderedPosition: {
25826 x: rpos[0],
25827 y: rpos[1]
25828 }
25829 });
25830 }
25831 }; // Functions to help with whether mouse wheel should trigger zooming
25832 // --
25833
25834
25835 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25836 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25837 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25838 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25839
25840 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25841 // eslint-disable-line no-unused-vars
25842 r.scrollingPage = true;
25843 clearTimeout(r.scrollingPageTimeout);
25844 r.scrollingPageTimeout = setTimeout(function () {
25845 r.scrollingPage = false;
25846 }, 250);
25847 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25848 // Handle mouseout on Cytoscape container
25849
25850 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25851 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25852 r.cy.emit({
25853 originalEvent: e,
25854 type: 'mouseout',
25855 position: {
25856 x: pos[0],
25857 y: pos[1]
25858 }
25859 });
25860 }, false);
25861 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25862 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25863 r.cy.emit({
25864 originalEvent: e,
25865 type: 'mouseover',
25866 position: {
25867 x: pos[0],
25868 y: pos[1]
25869 }
25870 });
25871 }, false);
25872 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25873
25874 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25875
25876 var center1, modelCenter1; // center point on start pinch to zoom
25877
25878 var offsetLeft, offsetTop;
25879 var containerWidth, containerHeight;
25880 var twoFingersStartInside;
25881
25882 var distance = function distance(x1, y1, x2, y2) {
25883 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25884 };
25885
25886 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25887 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25888 };
25889
25890 var touchstartHandler;
25891 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25892 if (!eventInContainer(e)) {
25893 return;
25894 }
25895
25896 blurActiveDomElement();
25897 r.touchData.capture = true;
25898 r.data.bgActivePosistion = undefined;
25899 var cy = r.cy;
25900 var now = r.touchData.now;
25901 var earlier = r.touchData.earlier;
25902
25903 if (e.touches[0]) {
25904 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25905 now[0] = pos[0];
25906 now[1] = pos[1];
25907 }
25908
25909 if (e.touches[1]) {
25910 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25911 now[2] = pos[0];
25912 now[3] = pos[1];
25913 }
25914
25915 if (e.touches[2]) {
25916 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25917 now[4] = pos[0];
25918 now[5] = pos[1];
25919 } // record starting points for pinch-to-zoom
25920
25921
25922 if (e.touches[1]) {
25923 r.touchData.singleTouchMoved = true;
25924 freeDraggedElements(r.dragData.touchDragEles);
25925 var offsets = r.findContainerClientCoords();
25926 offsetLeft = offsets[0];
25927 offsetTop = offsets[1];
25928 containerWidth = offsets[2];
25929 containerHeight = offsets[3];
25930 f1x1 = e.touches[0].clientX - offsetLeft;
25931 f1y1 = e.touches[0].clientY - offsetTop;
25932 f2x1 = e.touches[1].clientX - offsetLeft;
25933 f2y1 = e.touches[1].clientY - offsetTop;
25934 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25935 var pan = cy.pan();
25936 var zoom = cy.zoom();
25937 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25938 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25939 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25940 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25941
25942 var cxtDistThreshold = 200;
25943 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25944
25945 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25946 var near1 = r.findNearestElement(now[0], now[1], true, true);
25947 var near2 = r.findNearestElement(now[2], now[3], true, true);
25948
25949 if (near1 && near1.isNode()) {
25950 near1.activate().emit({
25951 originalEvent: e,
25952 type: 'cxttapstart',
25953 position: {
25954 x: now[0],
25955 y: now[1]
25956 }
25957 });
25958 r.touchData.start = near1;
25959 } else if (near2 && near2.isNode()) {
25960 near2.activate().emit({
25961 originalEvent: e,
25962 type: 'cxttapstart',
25963 position: {
25964 x: now[0],
25965 y: now[1]
25966 }
25967 });
25968 r.touchData.start = near2;
25969 } else {
25970 cy.emit({
25971 originalEvent: e,
25972 type: 'cxttapstart',
25973 position: {
25974 x: now[0],
25975 y: now[1]
25976 }
25977 });
25978 }
25979
25980 if (r.touchData.start) {
25981 r.touchData.start._private.grabbed = false;
25982 }
25983
25984 r.touchData.cxt = true;
25985 r.touchData.cxtDragged = false;
25986 r.data.bgActivePosistion = undefined;
25987 r.redraw();
25988 return;
25989 }
25990 }
25991
25992 if (e.touches[2]) {
25993 // ignore
25994 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25995 if (cy.boxSelectionEnabled()) {
25996 e.preventDefault();
25997 }
25998 } else if (e.touches[1]) ; else if (e.touches[0]) {
25999 var nears = r.findNearestElements(now[0], now[1], true, true);
26000 var near = nears[0];
26001
26002 if (near != null) {
26003 near.activate();
26004 r.touchData.start = near;
26005 r.touchData.starts = nears;
26006
26007 if (r.nodeIsGrabbable(near)) {
26008 var draggedEles = r.dragData.touchDragEles = cy.collection();
26009 var selectedNodes = null;
26010 r.redrawHint('eles', true);
26011 r.redrawHint('drag', true);
26012
26013 if (near.selected()) {
26014 // reset drag elements, since near will be added again
26015 selectedNodes = cy.$(function (ele) {
26016 return ele.selected() && r.nodeIsGrabbable(ele);
26017 });
26018 addNodesToDrag(selectedNodes, {
26019 addToList: draggedEles
26020 });
26021 } else {
26022 addNodeToDrag(near, {
26023 addToList: draggedEles
26024 });
26025 }
26026
26027 setGrabTarget(near);
26028
26029 var makeEvent = function makeEvent(type) {
26030 return {
26031 originalEvent: e,
26032 type: type,
26033 position: {
26034 x: now[0],
26035 y: now[1]
26036 }
26037 };
26038 };
26039
26040 near.emit(makeEvent('grabon'));
26041
26042 if (selectedNodes) {
26043 selectedNodes.forEach(function (n) {
26044 n.emit(makeEvent('grab'));
26045 });
26046 } else {
26047 near.emit(makeEvent('grab'));
26048 }
26049 }
26050 }
26051
26052 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
26053 x: now[0],
26054 y: now[1]
26055 });
26056
26057 if (near == null) {
26058 r.data.bgActivePosistion = {
26059 x: pos[0],
26060 y: pos[1]
26061 };
26062 r.redrawHint('select', true);
26063 r.redraw();
26064 } // Tap, taphold
26065 // -----
26066
26067
26068 r.touchData.singleTouchMoved = false;
26069 r.touchData.singleTouchStartTime = +new Date();
26070 clearTimeout(r.touchData.tapholdTimeout);
26071 r.touchData.tapholdTimeout = setTimeout(function () {
26072 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
26073 && !r.touchData.selecting // box selection shouldn't allow taphold through
26074 ) {
26075 triggerEvents(r.touchData.start, ['taphold'], e, {
26076 x: now[0],
26077 y: now[1]
26078 });
26079 }
26080 }, r.tapholdDuration);
26081 }
26082
26083 if (e.touches.length >= 1) {
26084 var sPos = r.touchData.startPosition = [];
26085
26086 for (var i = 0; i < now.length; i++) {
26087 sPos[i] = earlier[i] = now[i];
26088 }
26089
26090 var touch0 = e.touches[0];
26091 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
26092 }
26093 }, false);
26094 var touchmoveHandler;
26095 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
26096 // eslint-disable-line no-undef
26097 var capture = r.touchData.capture;
26098
26099 if (!capture && !eventInContainer(e)) {
26100 return;
26101 }
26102
26103 var select = r.selection;
26104 var cy = r.cy;
26105 var now = r.touchData.now;
26106 var earlier = r.touchData.earlier;
26107 var zoom = cy.zoom();
26108
26109 if (e.touches[0]) {
26110 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26111 now[0] = pos[0];
26112 now[1] = pos[1];
26113 }
26114
26115 if (e.touches[1]) {
26116 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26117 now[2] = pos[0];
26118 now[3] = pos[1];
26119 }
26120
26121 if (e.touches[2]) {
26122 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26123 now[4] = pos[0];
26124 now[5] = pos[1];
26125 }
26126
26127 var startGPos = r.touchData.startGPosition;
26128 var isOverThresholdDrag;
26129
26130 if (capture && e.touches[0] && startGPos) {
26131 var disp = [];
26132
26133 for (var j = 0; j < now.length; j++) {
26134 disp[j] = now[j] - earlier[j];
26135 }
26136
26137 var dx = e.touches[0].clientX - startGPos[0];
26138 var dx2 = dx * dx;
26139 var dy = e.touches[0].clientY - startGPos[1];
26140 var dy2 = dy * dy;
26141 var dist2 = dx2 + dy2;
26142 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
26143 } // context swipe cancelling
26144
26145
26146 if (capture && r.touchData.cxt) {
26147 e.preventDefault();
26148 var f1x2 = e.touches[0].clientX - offsetLeft,
26149 f1y2 = e.touches[0].clientY - offsetTop;
26150 var f2x2 = e.touches[1].clientX - offsetLeft,
26151 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
26152
26153 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
26154 var factorSq = distance2Sq / distance1Sq;
26155 var distThreshold = 150;
26156 var distThresholdSq = distThreshold * distThreshold;
26157 var factorThreshold = 1.5;
26158 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
26159
26160 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
26161 r.touchData.cxt = false;
26162 r.data.bgActivePosistion = undefined;
26163 r.redrawHint('select', true);
26164 var cxtEvt = {
26165 originalEvent: e,
26166 type: 'cxttapend',
26167 position: {
26168 x: now[0],
26169 y: now[1]
26170 }
26171 };
26172
26173 if (r.touchData.start) {
26174 r.touchData.start.unactivate().emit(cxtEvt);
26175 r.touchData.start = null;
26176 } else {
26177 cy.emit(cxtEvt);
26178 }
26179 }
26180 } // context swipe
26181
26182
26183 if (capture && r.touchData.cxt) {
26184 var cxtEvt = {
26185 originalEvent: e,
26186 type: 'cxtdrag',
26187 position: {
26188 x: now[0],
26189 y: now[1]
26190 }
26191 };
26192 r.data.bgActivePosistion = undefined;
26193 r.redrawHint('select', true);
26194
26195 if (r.touchData.start) {
26196 r.touchData.start.emit(cxtEvt);
26197 } else {
26198 cy.emit(cxtEvt);
26199 }
26200
26201 if (r.touchData.start) {
26202 r.touchData.start._private.grabbed = false;
26203 }
26204
26205 r.touchData.cxtDragged = true;
26206 var near = r.findNearestElement(now[0], now[1], true, true);
26207
26208 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
26209 if (r.touchData.cxtOver) {
26210 r.touchData.cxtOver.emit({
26211 originalEvent: e,
26212 type: 'cxtdragout',
26213 position: {
26214 x: now[0],
26215 y: now[1]
26216 }
26217 });
26218 }
26219
26220 r.touchData.cxtOver = near;
26221
26222 if (near) {
26223 near.emit({
26224 originalEvent: e,
26225 type: 'cxtdragover',
26226 position: {
26227 x: now[0],
26228 y: now[1]
26229 }
26230 });
26231 }
26232 } // box selection
26233
26234 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
26235 e.preventDefault();
26236 r.data.bgActivePosistion = undefined;
26237 this.lastThreeTouch = +new Date();
26238
26239 if (!r.touchData.selecting) {
26240 cy.emit({
26241 originalEvent: e,
26242 type: 'boxstart',
26243 position: {
26244 x: now[0],
26245 y: now[1]
26246 }
26247 });
26248 }
26249
26250 r.touchData.selecting = true;
26251 r.touchData.didSelect = true;
26252 select[4] = 1;
26253
26254 if (!select || select.length === 0 || select[0] === undefined) {
26255 select[0] = (now[0] + now[2] + now[4]) / 3;
26256 select[1] = (now[1] + now[3] + now[5]) / 3;
26257 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
26258 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
26259 } else {
26260 select[2] = (now[0] + now[2] + now[4]) / 3;
26261 select[3] = (now[1] + now[3] + now[5]) / 3;
26262 }
26263
26264 r.redrawHint('select', true);
26265 r.redraw(); // pinch to zoom
26266 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
26267 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
26268 // two fingers => pinch to zoom
26269 e.preventDefault();
26270 r.data.bgActivePosistion = undefined;
26271 r.redrawHint('select', true);
26272 var draggedEles = r.dragData.touchDragEles;
26273
26274 if (draggedEles) {
26275 r.redrawHint('drag', true);
26276
26277 for (var i = 0; i < draggedEles.length; i++) {
26278 var de_p = draggedEles[i]._private;
26279 de_p.grabbed = false;
26280 de_p.rscratch.inDragLayer = false;
26281 }
26282 }
26283
26284 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
26285
26286 var f1x2 = e.touches[0].clientX - offsetLeft,
26287 f1y2 = e.touches[0].clientY - offsetTop;
26288 var f2x2 = e.touches[1].clientX - offsetLeft,
26289 f2y2 = e.touches[1].clientY - offsetTop;
26290 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
26291 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
26292
26293 var factor = distance2 / distance1;
26294
26295 if (twoFingersStartInside) {
26296 // delta finger1
26297 var df1x = f1x2 - f1x1;
26298 var df1y = f1y2 - f1y1; // delta finger 2
26299
26300 var df2x = f2x2 - f2x1;
26301 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
26302 // i.e. so pinching cancels out and moving together pans
26303
26304 var tx = (df1x + df2x) / 2;
26305 var ty = (df1y + df2y) / 2; // now calculate the zoom
26306
26307 var zoom1 = cy.zoom();
26308 var zoom2 = zoom1 * factor;
26309 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
26310
26311 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
26312 var ctry = modelCenter1[1] * zoom1 + pan1.y;
26313 var pan2 = {
26314 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
26315 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
26316 }; // remove dragged eles
26317
26318 if (_start && _start.active()) {
26319 var draggedEles = r.dragData.touchDragEles;
26320 freeDraggedElements(draggedEles);
26321 r.redrawHint('drag', true);
26322 r.redrawHint('eles', true);
26323
26324 _start.unactivate().emit('freeon');
26325
26326 draggedEles.emit('free');
26327
26328 if (r.dragData.didDrag) {
26329 _start.emit('dragfreeon');
26330
26331 draggedEles.emit('dragfree');
26332 }
26333 }
26334
26335 cy.viewport({
26336 zoom: zoom2,
26337 pan: pan2,
26338 cancelOnFailedZoom: true
26339 });
26340 distance1 = distance2;
26341 f1x1 = f1x2;
26342 f1y1 = f1y2;
26343 f2x1 = f2x2;
26344 f2y1 = f2y2;
26345 r.pinching = true;
26346 } // Re-project
26347
26348
26349 if (e.touches[0]) {
26350 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26351 now[0] = pos[0];
26352 now[1] = pos[1];
26353 }
26354
26355 if (e.touches[1]) {
26356 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26357 now[2] = pos[0];
26358 now[3] = pos[1];
26359 }
26360
26361 if (e.touches[2]) {
26362 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26363 now[4] = pos[0];
26364 now[5] = pos[1];
26365 }
26366 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
26367 ) {
26368 var start = r.touchData.start;
26369 var last = r.touchData.last;
26370 var near;
26371
26372 if (!r.hoverData.draggingEles && !r.swipePanning) {
26373 near = r.findNearestElement(now[0], now[1], true, true);
26374 }
26375
26376 if (capture && start != null) {
26377 e.preventDefault();
26378 } // dragging nodes
26379
26380
26381 if (capture && start != null && r.nodeIsDraggable(start)) {
26382 if (isOverThresholdDrag) {
26383 // then dragging can happen
26384 var draggedEles = r.dragData.touchDragEles;
26385 var justStartedDrag = !r.dragData.didDrag;
26386
26387 if (justStartedDrag) {
26388 addNodesToDrag(draggedEles, {
26389 inDragLayer: true
26390 });
26391 }
26392
26393 r.dragData.didDrag = true;
26394 var totalShift = {
26395 x: 0,
26396 y: 0
26397 };
26398
26399 if (number(disp[0]) && number(disp[1])) {
26400 totalShift.x += disp[0];
26401 totalShift.y += disp[1];
26402
26403 if (justStartedDrag) {
26404 r.redrawHint('eles', true);
26405 var dragDelta = r.touchData.dragDelta;
26406
26407 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
26408 totalShift.x += dragDelta[0];
26409 totalShift.y += dragDelta[1];
26410 }
26411 }
26412 }
26413
26414 r.hoverData.draggingEles = true;
26415 draggedEles.silentShift(totalShift).emit('position drag');
26416 r.redrawHint('drag', true);
26417
26418 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
26419 r.redrawHint('eles', true);
26420 }
26421
26422 r.redraw();
26423 } else {
26424 // otherise keep track of drag delta for later
26425 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
26426
26427 if (dragDelta.length === 0) {
26428 dragDelta.push(disp[0]);
26429 dragDelta.push(disp[1]);
26430 } else {
26431 dragDelta[0] += disp[0];
26432 dragDelta[1] += disp[1];
26433 }
26434 }
26435 } // touchmove
26436
26437
26438 {
26439 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
26440 x: now[0],
26441 y: now[1]
26442 });
26443
26444 if ((!start || !start.grabbed()) && near != last) {
26445 if (last) {
26446 last.emit({
26447 originalEvent: e,
26448 type: 'tapdragout',
26449 position: {
26450 x: now[0],
26451 y: now[1]
26452 }
26453 });
26454 }
26455
26456 if (near) {
26457 near.emit({
26458 originalEvent: e,
26459 type: 'tapdragover',
26460 position: {
26461 x: now[0],
26462 y: now[1]
26463 }
26464 });
26465 }
26466 }
26467
26468 r.touchData.last = near;
26469 } // check to cancel taphold
26470
26471 if (capture) {
26472 for (var i = 0; i < now.length; i++) {
26473 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
26474 r.touchData.singleTouchMoved = true;
26475 }
26476 }
26477 } // panning
26478
26479
26480 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
26481 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
26482
26483 if (allowPassthrough) {
26484 e.preventDefault();
26485
26486 if (!r.data.bgActivePosistion) {
26487 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
26488 }
26489
26490 if (r.swipePanning) {
26491 cy.panBy({
26492 x: disp[0] * zoom,
26493 y: disp[1] * zoom
26494 });
26495 } else if (isOverThresholdDrag) {
26496 r.swipePanning = true;
26497 cy.panBy({
26498 x: dx * zoom,
26499 y: dy * zoom
26500 });
26501
26502 if (start) {
26503 start.unactivate();
26504 r.redrawHint('select', true);
26505 r.touchData.start = null;
26506 }
26507 }
26508 } // Re-project
26509
26510
26511 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26512 now[0] = pos[0];
26513 now[1] = pos[1];
26514 }
26515 }
26516
26517 for (var j = 0; j < now.length; j++) {
26518 earlier[j] = now[j];
26519 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26520
26521
26522 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26523 r.data.bgActivePosistion = undefined;
26524 r.redrawHint('select', true);
26525 r.redraw();
26526 }
26527 }, false);
26528 var touchcancelHandler;
26529 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26530 // eslint-disable-line no-unused-vars
26531 var start = r.touchData.start;
26532 r.touchData.capture = false;
26533
26534 if (start) {
26535 start.unactivate();
26536 }
26537 });
26538 var touchendHandler;
26539 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26540 // eslint-disable-line no-unused-vars
26541 var start = r.touchData.start;
26542 var capture = r.touchData.capture;
26543
26544 if (capture) {
26545 if (e.touches.length === 0) {
26546 r.touchData.capture = false;
26547 }
26548
26549 e.preventDefault();
26550 } else {
26551 return;
26552 }
26553
26554 var select = r.selection;
26555 r.swipePanning = false;
26556 r.hoverData.draggingEles = false;
26557 var cy = r.cy;
26558 var zoom = cy.zoom();
26559 var now = r.touchData.now;
26560 var earlier = r.touchData.earlier;
26561
26562 if (e.touches[0]) {
26563 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26564 now[0] = pos[0];
26565 now[1] = pos[1];
26566 }
26567
26568 if (e.touches[1]) {
26569 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26570 now[2] = pos[0];
26571 now[3] = pos[1];
26572 }
26573
26574 if (e.touches[2]) {
26575 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26576 now[4] = pos[0];
26577 now[5] = pos[1];
26578 }
26579
26580 if (start) {
26581 start.unactivate();
26582 }
26583
26584 var ctxTapend;
26585
26586 if (r.touchData.cxt) {
26587 ctxTapend = {
26588 originalEvent: e,
26589 type: 'cxttapend',
26590 position: {
26591 x: now[0],
26592 y: now[1]
26593 }
26594 };
26595
26596 if (start) {
26597 start.emit(ctxTapend);
26598 } else {
26599 cy.emit(ctxTapend);
26600 }
26601
26602 if (!r.touchData.cxtDragged) {
26603 var ctxTap = {
26604 originalEvent: e,
26605 type: 'cxttap',
26606 position: {
26607 x: now[0],
26608 y: now[1]
26609 }
26610 };
26611
26612 if (start) {
26613 start.emit(ctxTap);
26614 } else {
26615 cy.emit(ctxTap);
26616 }
26617 }
26618
26619 if (r.touchData.start) {
26620 r.touchData.start._private.grabbed = false;
26621 }
26622
26623 r.touchData.cxt = false;
26624 r.touchData.start = null;
26625 r.redraw();
26626 return;
26627 } // no more box selection if we don't have three fingers
26628
26629
26630 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26631 r.touchData.selecting = false;
26632 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26633 select[0] = undefined;
26634 select[1] = undefined;
26635 select[2] = undefined;
26636 select[3] = undefined;
26637 select[4] = 0;
26638 r.redrawHint('select', true);
26639 cy.emit({
26640 type: 'boxend',
26641 originalEvent: e,
26642 position: {
26643 x: now[0],
26644 y: now[1]
26645 }
26646 });
26647
26648 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26649 return ele.selectable() && !ele.selected();
26650 };
26651
26652 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26653
26654 if (box.nonempty()) {
26655 r.redrawHint('eles', true);
26656 }
26657
26658 r.redraw();
26659 }
26660
26661 if (start != null) {
26662 start.unactivate();
26663 }
26664
26665 if (e.touches[2]) {
26666 r.data.bgActivePosistion = undefined;
26667 r.redrawHint('select', true);
26668 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26669 r.data.bgActivePosistion = undefined;
26670 r.redrawHint('select', true);
26671 var draggedEles = r.dragData.touchDragEles;
26672
26673 if (start != null) {
26674 var startWasGrabbed = start._private.grabbed;
26675 freeDraggedElements(draggedEles);
26676 r.redrawHint('drag', true);
26677 r.redrawHint('eles', true);
26678
26679 if (startWasGrabbed) {
26680 start.emit('freeon');
26681 draggedEles.emit('free');
26682
26683 if (r.dragData.didDrag) {
26684 start.emit('dragfreeon');
26685 draggedEles.emit('dragfree');
26686 }
26687 }
26688
26689 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26690 x: now[0],
26691 y: now[1]
26692 });
26693 start.unactivate();
26694 r.touchData.start = null;
26695 } else {
26696 var near = r.findNearestElement(now[0], now[1], true, true);
26697 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26698 x: now[0],
26699 y: now[1]
26700 });
26701 }
26702
26703 var dx = r.touchData.startPosition[0] - now[0];
26704 var dx2 = dx * dx;
26705 var dy = r.touchData.startPosition[1] - now[1];
26706 var dy2 = dy * dy;
26707 var dist2 = dx2 + dy2;
26708 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26709
26710 if (!r.touchData.singleTouchMoved) {
26711 if (!start) {
26712 cy.$(':selected').unselect(['tapunselect']);
26713 }
26714
26715 triggerEvents(start, ['tap', 'vclick'], e, {
26716 x: now[0],
26717 y: now[1]
26718 });
26719 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26720
26721
26722 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26723 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26724 ) {
26725 if (cy.selectionType() === 'single') {
26726 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26727 start.select(['tapselect']);
26728 } else {
26729 if (start.selected()) {
26730 start.unselect(['tapunselect']);
26731 } else {
26732 start.select(['tapselect']);
26733 }
26734 }
26735
26736 r.redrawHint('eles', true);
26737 }
26738
26739 r.touchData.singleTouchMoved = true;
26740 }
26741
26742 for (var j = 0; j < now.length; j++) {
26743 earlier[j] = now[j];
26744 }
26745
26746 r.dragData.didDrag = false; // reset for next touchstart
26747
26748 if (e.touches.length === 0) {
26749 r.touchData.dragDelta = [];
26750 r.touchData.startPosition = null;
26751 r.touchData.startGPosition = null;
26752 r.touchData.didSelect = false;
26753 }
26754
26755 if (e.touches.length < 2) {
26756 if (e.touches.length === 1) {
26757 // the old start global pos'n may not be the same finger that remains
26758 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26759 }
26760
26761 r.pinching = false;
26762 r.redrawHint('eles', true);
26763 r.redraw();
26764 } //r.redraw();
26765
26766 }, false); // fallback compatibility layer for ms pointer events
26767
26768 if (typeof TouchEvent === 'undefined') {
26769 var pointers = [];
26770
26771 var makeTouch = function makeTouch(e) {
26772 return {
26773 clientX: e.clientX,
26774 clientY: e.clientY,
26775 force: 1,
26776 identifier: e.pointerId,
26777 pageX: e.pageX,
26778 pageY: e.pageY,
26779 radiusX: e.width / 2,
26780 radiusY: e.height / 2,
26781 screenX: e.screenX,
26782 screenY: e.screenY,
26783 target: e.target
26784 };
26785 };
26786
26787 var makePointer = function makePointer(e) {
26788 return {
26789 event: e,
26790 touch: makeTouch(e)
26791 };
26792 };
26793
26794 var addPointer = function addPointer(e) {
26795 pointers.push(makePointer(e));
26796 };
26797
26798 var removePointer = function removePointer(e) {
26799 for (var i = 0; i < pointers.length; i++) {
26800 var p = pointers[i];
26801
26802 if (p.event.pointerId === e.pointerId) {
26803 pointers.splice(i, 1);
26804 return;
26805 }
26806 }
26807 };
26808
26809 var updatePointer = function updatePointer(e) {
26810 var p = pointers.filter(function (p) {
26811 return p.event.pointerId === e.pointerId;
26812 })[0];
26813 p.event = e;
26814 p.touch = makeTouch(e);
26815 };
26816
26817 var addTouchesToEvent = function addTouchesToEvent(e) {
26818 e.touches = pointers.map(function (p) {
26819 return p.touch;
26820 });
26821 };
26822
26823 var pointerIsMouse = function pointerIsMouse(e) {
26824 return e.pointerType === 'mouse' || e.pointerType === 4;
26825 };
26826
26827 r.registerBinding(r.container, 'pointerdown', function (e) {
26828 if (pointerIsMouse(e)) {
26829 return;
26830 } // mouse already handled
26831
26832
26833 e.preventDefault();
26834 addPointer(e);
26835 addTouchesToEvent(e);
26836 touchstartHandler(e);
26837 });
26838 r.registerBinding(r.container, 'pointerup', function (e) {
26839 if (pointerIsMouse(e)) {
26840 return;
26841 } // mouse already handled
26842
26843
26844 removePointer(e);
26845 addTouchesToEvent(e);
26846 touchendHandler(e);
26847 });
26848 r.registerBinding(r.container, 'pointercancel', function (e) {
26849 if (pointerIsMouse(e)) {
26850 return;
26851 } // mouse already handled
26852
26853
26854 removePointer(e);
26855 addTouchesToEvent(e);
26856 touchcancelHandler(e);
26857 });
26858 r.registerBinding(r.container, 'pointermove', function (e) {
26859 if (pointerIsMouse(e)) {
26860 return;
26861 } // mouse already handled
26862
26863
26864 e.preventDefault();
26865 updatePointer(e);
26866 addTouchesToEvent(e);
26867 touchmoveHandler(e);
26868 });
26869 }
26870 };
26871
26872 var BRp$d = {};
26873
26874 BRp$d.generatePolygon = function (name, points) {
26875 return this.nodeShapes[name] = {
26876 renderer: this,
26877 name: name,
26878 points: points,
26879 draw: function draw(context, centerX, centerY, width, height) {
26880 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26881 },
26882 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26883 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26884 },
26885 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26886 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26887 }
26888 };
26889 };
26890
26891 BRp$d.generateEllipse = function () {
26892 return this.nodeShapes['ellipse'] = {
26893 renderer: this,
26894 name: 'ellipse',
26895 draw: function draw(context, centerX, centerY, width, height) {
26896 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26897 },
26898 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26899 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26900 },
26901 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26902 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26903 }
26904 };
26905 };
26906
26907 BRp$d.generateRoundPolygon = function (name, points) {
26908 // Pre-compute control points
26909 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26910 // the unit vectors.
26911 // For simplicity the layout will be:
26912 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26913 var allPoints = new Array(points.length * 2);
26914
26915 for (var i = 0; i < points.length / 2; i++) {
26916 var sourceIndex = i * 2;
26917 var destIndex = void 0;
26918
26919 if (i < points.length / 2 - 1) {
26920 destIndex = (i + 1) * 2;
26921 } else {
26922 destIndex = 0;
26923 }
26924
26925 allPoints[i * 4] = points[sourceIndex];
26926 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26927 var xDest = points[destIndex] - points[sourceIndex];
26928 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26929 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26930 allPoints[i * 4 + 2] = xDest / norm;
26931 allPoints[i * 4 + 3] = yDest / norm;
26932 }
26933
26934 return this.nodeShapes[name] = {
26935 renderer: this,
26936 name: name,
26937 points: allPoints,
26938 draw: function draw(context, centerX, centerY, width, height) {
26939 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26940 },
26941 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26942 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26943 },
26944 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26945 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26946 }
26947 };
26948 };
26949
26950 BRp$d.generateRoundRectangle = function () {
26951 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26952 renderer: this,
26953 name: 'round-rectangle',
26954 points: generateUnitNgonPointsFitToSquare(4, 0),
26955 draw: function draw(context, centerX, centerY, width, height) {
26956 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26957 },
26958 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26959 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26960 },
26961 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26962 var cornerRadius = getRoundRectangleRadius(width, height);
26963 var diam = cornerRadius * 2; // Check hBox
26964
26965 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26966 return true;
26967 } // Check vBox
26968
26969
26970 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26971 return true;
26972 } // Check top left quarter circle
26973
26974
26975 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26976 return true;
26977 } // Check top right quarter circle
26978
26979
26980 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26981 return true;
26982 } // Check bottom right quarter circle
26983
26984
26985 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26986 return true;
26987 } // Check bottom left quarter circle
26988
26989
26990 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26991 return true;
26992 }
26993
26994 return false;
26995 }
26996 };
26997 };
26998
26999 BRp$d.generateCutRectangle = function () {
27000 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
27001 renderer: this,
27002 name: 'cut-rectangle',
27003 cornerLength: getCutRectangleCornerLength(),
27004 points: generateUnitNgonPointsFitToSquare(4, 0),
27005 draw: function draw(context, centerX, centerY, width, height) {
27006 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27007 },
27008 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
27009 var cl = this.cornerLength;
27010 var hh = height / 2;
27011 var hw = width / 2;
27012 var xBegin = centerX - hw;
27013 var xEnd = centerX + hw;
27014 var yBegin = centerY - hh;
27015 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
27016
27017 return {
27018 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
27019 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
27020 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
27021 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
27022 };
27023 },
27024 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27025 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27026 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
27027 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27028 },
27029 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27030 // Check hBox
27031 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
27032 return true;
27033 } // Check vBox
27034
27035
27036 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
27037 return true;
27038 }
27039
27040 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
27041 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
27042 }
27043 };
27044 };
27045
27046 BRp$d.generateBarrel = function () {
27047 return this.nodeShapes['barrel'] = {
27048 renderer: this,
27049 name: 'barrel',
27050 points: generateUnitNgonPointsFitToSquare(4, 0),
27051 draw: function draw(context, centerX, centerY, width, height) {
27052 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27053 },
27054 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27055 // use two fixed t values for the bezier curve approximation
27056 var t0 = 0.15;
27057 var t1 = 0.5;
27058 var t2 = 0.85;
27059 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27060
27061 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
27062 // approximate curve pts based on the two t values
27063 var m0 = qbezierPtAt({
27064 x: pts[0],
27065 y: pts[1]
27066 }, {
27067 x: pts[2],
27068 y: pts[3]
27069 }, {
27070 x: pts[4],
27071 y: pts[5]
27072 }, t0);
27073 var m1 = qbezierPtAt({
27074 x: pts[0],
27075 y: pts[1]
27076 }, {
27077 x: pts[2],
27078 y: pts[3]
27079 }, {
27080 x: pts[4],
27081 y: pts[5]
27082 }, t1);
27083 var m2 = qbezierPtAt({
27084 x: pts[0],
27085 y: pts[1]
27086 }, {
27087 x: pts[2],
27088 y: pts[3]
27089 }, {
27090 x: pts[4],
27091 y: pts[5]
27092 }, t2);
27093 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
27094 };
27095
27096 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
27097 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27098 },
27099 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
27100 var hh = height / 2;
27101 var hw = width / 2;
27102 var xBegin = centerX - hw;
27103 var xEnd = centerX + hw;
27104 var yBegin = centerY - hh;
27105 var yEnd = centerY + hh;
27106 var curveConstants = getBarrelCurveConstants(width, height);
27107 var hOffset = curveConstants.heightOffset;
27108 var wOffset = curveConstants.widthOffset;
27109 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
27110
27111 var pts = {
27112 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
27113 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
27114 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
27115 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
27116 };
27117 pts.topLeft.isTop = true;
27118 pts.topRight.isTop = true;
27119 pts.bottomLeft.isBottom = true;
27120 pts.bottomRight.isBottom = true;
27121 return pts;
27122 },
27123 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27124 var curveConstants = getBarrelCurveConstants(width, height);
27125 var hOffset = curveConstants.heightOffset;
27126 var wOffset = curveConstants.widthOffset; // Check hBox
27127
27128 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
27129 return true;
27130 } // Check vBox
27131
27132
27133 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
27134 return true;
27135 }
27136
27137 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
27138
27139 var getCurveT = function getCurveT(x, y, curvePts) {
27140 var x0 = curvePts[4];
27141 var x1 = curvePts[2];
27142 var x2 = curvePts[0];
27143 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
27144
27145 var y2 = curvePts[1];
27146 var xMin = Math.min(x0, x2);
27147 var xMax = Math.max(x0, x2);
27148 var yMin = Math.min(y0, y2);
27149 var yMax = Math.max(y0, y2);
27150
27151 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
27152 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
27153 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
27154 var validRoots = roots.filter(function (r) {
27155 return 0 <= r && r <= 1;
27156 });
27157
27158 if (validRoots.length > 0) {
27159 return validRoots[0];
27160 }
27161 }
27162
27163 return null;
27164 };
27165
27166 var curveRegions = Object.keys(barrelCurvePts);
27167
27168 for (var i = 0; i < curveRegions.length; i++) {
27169 var corner = curveRegions[i];
27170 var cornerPts = barrelCurvePts[corner];
27171 var t = getCurveT(x, y, cornerPts);
27172
27173 if (t == null) {
27174 continue;
27175 }
27176
27177 var y0 = cornerPts[5];
27178 var y1 = cornerPts[3];
27179 var y2 = cornerPts[1];
27180 var bezY = qbezierAt(y0, y1, y2, t);
27181
27182 if (cornerPts.isTop && bezY <= y) {
27183 return true;
27184 }
27185
27186 if (cornerPts.isBottom && y <= bezY) {
27187 return true;
27188 }
27189 }
27190
27191 return false;
27192 }
27193 };
27194 };
27195
27196 BRp$d.generateBottomRoundrectangle = function () {
27197 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
27198 renderer: this,
27199 name: 'bottom-round-rectangle',
27200 points: generateUnitNgonPointsFitToSquare(4, 0),
27201 draw: function draw(context, centerX, centerY, width, height) {
27202 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27203 },
27204 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27205 var topStartX = nodeX - (width / 2 + padding);
27206 var topStartY = nodeY - (height / 2 + padding);
27207 var topEndY = topStartY;
27208 var topEndX = nodeX + (width / 2 + padding);
27209 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
27210
27211 if (topIntersections.length > 0) {
27212 return topIntersections;
27213 }
27214
27215 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27216 },
27217 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27218 var cornerRadius = getRoundRectangleRadius(width, height);
27219 var diam = 2 * cornerRadius; // Check hBox
27220
27221 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27222 return true;
27223 } // Check vBox
27224
27225
27226 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27227 return true;
27228 } // check non-rounded top side
27229
27230
27231 var outerWidth = width / 2 + 2 * padding;
27232 var outerHeight = height / 2 + 2 * padding;
27233 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
27234
27235 if (pointInsidePolygonPoints(x, y, points)) {
27236 return true;
27237 } // Check bottom right quarter circle
27238
27239
27240 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27241 return true;
27242 } // Check bottom left quarter circle
27243
27244
27245 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27246 return true;
27247 }
27248
27249 return false;
27250 }
27251 };
27252 };
27253
27254 BRp$d.registerNodeShapes = function () {
27255 var nodeShapes = this.nodeShapes = {};
27256 var renderer = this;
27257 this.generateEllipse();
27258 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
27259 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
27260 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
27261 nodeShapes['square'] = nodeShapes['rectangle'];
27262 this.generateRoundRectangle();
27263 this.generateCutRectangle();
27264 this.generateBarrel();
27265 this.generateBottomRoundrectangle();
27266 {
27267 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
27268 this.generatePolygon('diamond', diamondPoints);
27269 this.generateRoundPolygon('round-diamond', diamondPoints);
27270 }
27271 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27272 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27273 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27274 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27275 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27276 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27277 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
27278 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
27279 var star5Points = new Array(20);
27280 {
27281 var outerPoints = generateUnitNgonPoints(5, 0);
27282 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
27283
27284 var innerRadius = 0.5 * (3 - Math.sqrt(5));
27285 innerRadius *= 1.57;
27286
27287 for (var i = 0; i < innerPoints.length / 2; i++) {
27288 innerPoints[i * 2] *= innerRadius;
27289 innerPoints[i * 2 + 1] *= innerRadius;
27290 }
27291
27292 for (var i = 0; i < 20 / 4; i++) {
27293 star5Points[i * 4] = outerPoints[i * 2];
27294 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
27295 star5Points[i * 4 + 2] = innerPoints[i * 2];
27296 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
27297 }
27298 }
27299 star5Points = fitPolygonToSquare(star5Points);
27300 this.generatePolygon('star', star5Points);
27301 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
27302 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
27303 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]);
27304 {
27305 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
27306 this.generatePolygon('tag', tagPoints);
27307 this.generateRoundPolygon('round-tag', tagPoints);
27308 }
27309
27310 nodeShapes.makePolygon = function (points) {
27311 // use caching on user-specified polygons so they are as fast as native shapes
27312 var key = points.join('$');
27313 var name = 'polygon-' + key;
27314 var shape;
27315
27316 if (shape = this[name]) {
27317 // got cached shape
27318 return shape;
27319 } // create and cache new shape
27320
27321
27322 return renderer.generatePolygon(name, points);
27323 };
27324 };
27325
27326 var BRp$e = {};
27327
27328 BRp$e.timeToRender = function () {
27329 return this.redrawTotalTime / this.redrawCount;
27330 };
27331
27332 BRp$e.redraw = function (options) {
27333 options = options || staticEmptyObject();
27334 var r = this;
27335
27336 if (r.averageRedrawTime === undefined) {
27337 r.averageRedrawTime = 0;
27338 }
27339
27340 if (r.lastRedrawTime === undefined) {
27341 r.lastRedrawTime = 0;
27342 }
27343
27344 if (r.lastDrawTime === undefined) {
27345 r.lastDrawTime = 0;
27346 }
27347
27348 r.requestedFrame = true;
27349 r.renderOptions = options;
27350 };
27351
27352 BRp$e.beforeRender = function (fn, priority) {
27353 // the renderer can't add tick callbacks when destroyed
27354 if (this.destroyed) {
27355 return;
27356 }
27357
27358 if (priority == null) {
27359 error('Priority is not optional for beforeRender');
27360 }
27361
27362 var cbs = this.beforeRenderCallbacks;
27363 cbs.push({
27364 fn: fn,
27365 priority: priority
27366 }); // higher priority callbacks executed first
27367
27368 cbs.sort(function (a, b) {
27369 return b.priority - a.priority;
27370 });
27371 };
27372
27373 var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
27374 var cbs = r.beforeRenderCallbacks;
27375
27376 for (var i = 0; i < cbs.length; i++) {
27377 cbs[i].fn(willDraw, startTime);
27378 }
27379 };
27380
27381 BRp$e.startRenderLoop = function () {
27382 var r = this;
27383 var cy = r.cy;
27384
27385 if (r.renderLoopStarted) {
27386 return;
27387 } else {
27388 r.renderLoopStarted = true;
27389 }
27390
27391 var renderFn = function renderFn(requestTime) {
27392 if (r.destroyed) {
27393 return;
27394 }
27395
27396 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
27397 beforeRenderCallbacks(r, true, requestTime);
27398 var startTime = performanceNow();
27399 r.render(r.renderOptions);
27400 var endTime = r.lastDrawTime = performanceNow();
27401
27402 if (r.averageRedrawTime === undefined) {
27403 r.averageRedrawTime = endTime - startTime;
27404 }
27405
27406 if (r.redrawCount === undefined) {
27407 r.redrawCount = 0;
27408 }
27409
27410 r.redrawCount++;
27411
27412 if (r.redrawTotalTime === undefined) {
27413 r.redrawTotalTime = 0;
27414 }
27415
27416 var duration = endTime - startTime;
27417 r.redrawTotalTime += duration;
27418 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
27419
27420 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
27421 r.requestedFrame = false;
27422 } else {
27423 beforeRenderCallbacks(r, false, requestTime);
27424 }
27425
27426 r.skipFrame = false;
27427 requestAnimationFrame(renderFn);
27428 };
27429
27430 requestAnimationFrame(renderFn);
27431 };
27432
27433 var BaseRenderer = function BaseRenderer(options) {
27434 this.init(options);
27435 };
27436
27437 var BR = BaseRenderer;
27438 var BRp$f = BR.prototype;
27439 BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
27440
27441 BRp$f.init = function (options) {
27442 var r = this;
27443 r.options = options;
27444 r.cy = options.cy;
27445 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
27446
27447 if (window$1) {
27448 var document = window$1.document;
27449 var head = document.head;
27450 var stylesheetId = '__________cytoscape_stylesheet';
27451 var className = '__________cytoscape_container';
27452 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
27453
27454 if (ctr.className.indexOf(className) < 0) {
27455 ctr.className = (ctr.className || '') + ' ' + className;
27456 }
27457
27458 if (!stylesheetAlreadyExists) {
27459 var stylesheet = document.createElement('style');
27460 stylesheet.id = stylesheetId;
27461 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
27462 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
27463 }
27464
27465 var computedStyle = window$1.getComputedStyle(ctr);
27466 var position = computedStyle.getPropertyValue('position');
27467
27468 if (position === 'static') {
27469 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
27470 }
27471 }
27472
27473 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
27474
27475 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
27476
27477 r.hoverData = {
27478 down: null,
27479 last: null,
27480 downTime: null,
27481 triggerMode: null,
27482 dragging: false,
27483 initialPan: [null, null],
27484 capture: false
27485 };
27486 r.dragData = {
27487 possibleDragElements: []
27488 };
27489 r.touchData = {
27490 start: null,
27491 capture: false,
27492 // These 3 fields related to tap, taphold events
27493 startPosition: [null, null, null, null, null, null],
27494 singleTouchStartTime: null,
27495 singleTouchMoved: true,
27496 now: [null, null, null, null, null, null],
27497 earlier: [null, null, null, null, null, null]
27498 };
27499 r.redraws = 0;
27500 r.showFps = options.showFps;
27501 r.debug = options.debug;
27502 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27503 r.textureOnViewport = options.textureOnViewport;
27504 r.wheelSensitivity = options.wheelSensitivity;
27505 r.motionBlurEnabled = options.motionBlur; // on by default
27506
27507 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
27508 r.motionBlur = options.motionBlur; // for initial kick off
27509
27510 r.motionBlurOpacity = options.motionBlurOpacity;
27511 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27512 r.motionBlurPxRatio = 1;
27513 r.mbPxRBlurry = 1; //0.8;
27514
27515 r.minMbLowQualFrames = 4;
27516 r.fullQualityMb = false;
27517 r.clearedForMotionBlur = [];
27518 r.desktopTapThreshold = options.desktopTapThreshold;
27519 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27520 r.touchTapThreshold = options.touchTapThreshold;
27521 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27522 r.tapholdDuration = 500;
27523 r.bindings = [];
27524 r.beforeRenderCallbacks = [];
27525 r.beforeRenderPriorities = {
27526 // higher priority execs before lower one
27527 animations: 400,
27528 eleCalcs: 300,
27529 eleTxrDeq: 200,
27530 lyrTxrDeq: 150,
27531 lyrTxrSkip: 100
27532 };
27533 r.registerNodeShapes();
27534 r.registerArrowShapes();
27535 r.registerCalculationListeners();
27536 };
27537
27538 BRp$f.notify = function (eventName, eles) {
27539 var r = this;
27540 var cy = r.cy; // the renderer can't be notified after it's destroyed
27541
27542 if (this.destroyed) {
27543 return;
27544 }
27545
27546 if (eventName === 'init') {
27547 r.load();
27548 return;
27549 }
27550
27551 if (eventName === 'destroy') {
27552 r.destroy();
27553 return;
27554 }
27555
27556 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27557 r.invalidateCachedZSortedEles();
27558 }
27559
27560 if (eventName === 'viewport') {
27561 r.redrawHint('select', true);
27562 }
27563
27564 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27565 r.invalidateContainerClientCoordsCache();
27566 r.matchCanvasSize(r.container);
27567 }
27568
27569 r.redrawHint('eles', true);
27570 r.redrawHint('drag', true);
27571 this.startRenderLoop();
27572 this.redraw();
27573 };
27574
27575 BRp$f.destroy = function () {
27576 var r = this;
27577 r.destroyed = true;
27578 r.cy.stopAnimationLoop();
27579
27580 for (var i = 0; i < r.bindings.length; i++) {
27581 var binding = r.bindings[i];
27582 var b = binding;
27583 var tgt = b.target;
27584 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27585 }
27586
27587 r.bindings = [];
27588 r.beforeRenderCallbacks = [];
27589 r.onUpdateEleCalcsFns = [];
27590
27591 if (r.removeObserver) {
27592 r.removeObserver.disconnect();
27593 }
27594
27595 if (r.styleObserver) {
27596 r.styleObserver.disconnect();
27597 }
27598
27599 if (r.resizeObserver) {
27600 r.resizeObserver.disconnect();
27601 }
27602
27603 if (r.labelCalcDiv) {
27604 try {
27605 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27606 } catch (e) {// ie10 issue #1014
27607 }
27608 }
27609 };
27610
27611 BRp$f.isHeadless = function () {
27612 return false;
27613 };
27614
27615 [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
27616 extend(BRp$f, props);
27617 });
27618
27619 var fullFpsTime = 1000 / 60; // assume 60 frames per second
27620
27621 var defs = {
27622 setupDequeueing: function setupDequeueing(opts) {
27623 return function setupDequeueingImpl() {
27624 var self = this;
27625 var r = this.renderer;
27626
27627 if (self.dequeueingSetup) {
27628 return;
27629 } else {
27630 self.dequeueingSetup = true;
27631 }
27632
27633 var queueRedraw = lodash_debounce(function () {
27634 r.redrawHint('eles', true);
27635 r.redrawHint('drag', true);
27636 r.redraw();
27637 }, opts.deqRedrawThreshold);
27638
27639 var dequeue = function dequeue(willDraw, frameStartTime) {
27640 var startTime = performanceNow();
27641 var avgRenderTime = r.averageRedrawTime;
27642 var renderTime = r.lastRedrawTime;
27643 var deqd = [];
27644 var extent = r.cy.extent();
27645 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27646 // queue won't automatically be flushed before dequeueing starts
27647
27648 if (!willDraw) {
27649 r.flushRenderedStyleQueue();
27650 }
27651
27652 while (true) {
27653 // eslint-disable-line no-constant-condition
27654 var now = performanceNow();
27655 var duration = now - startTime;
27656 var frameDuration = now - frameStartTime;
27657
27658 if (renderTime < fullFpsTime) {
27659 // if we're rendering faster than the ideal fps, then do dequeueing
27660 // during all of the remaining frame time
27661 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27662
27663 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27664 break;
27665 }
27666 } else {
27667 if (willDraw) {
27668 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27669 break;
27670 }
27671 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27672 break;
27673 }
27674 }
27675
27676 var thisDeqd = opts.deq(self, pixelRatio, extent);
27677
27678 if (thisDeqd.length > 0) {
27679 for (var i = 0; i < thisDeqd.length; i++) {
27680 deqd.push(thisDeqd[i]);
27681 }
27682 } else {
27683 break;
27684 }
27685 } // callbacks on dequeue
27686
27687
27688 if (deqd.length > 0) {
27689 opts.onDeqd(self, deqd);
27690
27691 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27692 queueRedraw();
27693 }
27694 }
27695 };
27696
27697 var priority = opts.priority || noop;
27698 r.beforeRender(dequeue, priority(self));
27699 };
27700 }
27701 };
27702
27703 // Uses keys so elements may share the same cache.
27704
27705 var ElementTextureCacheLookup =
27706 /*#__PURE__*/
27707 function () {
27708 function ElementTextureCacheLookup(getKey) {
27709 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27710
27711 _classCallCheck(this, ElementTextureCacheLookup);
27712
27713 this.idsByKey = new Map$1();
27714 this.keyForId = new Map$1();
27715 this.cachesByLvl = new Map$1();
27716 this.lvls = [];
27717 this.getKey = getKey;
27718 this.doesEleInvalidateKey = doesEleInvalidateKey;
27719 }
27720
27721 _createClass(ElementTextureCacheLookup, [{
27722 key: "getIdsFor",
27723 value: function getIdsFor(key) {
27724 if (key == null) {
27725 error("Can not get id list for null key");
27726 }
27727
27728 var idsByKey = this.idsByKey;
27729 var ids = this.idsByKey.get(key);
27730
27731 if (!ids) {
27732 ids = new Set$1();
27733 idsByKey.set(key, ids);
27734 }
27735
27736 return ids;
27737 }
27738 }, {
27739 key: "addIdForKey",
27740 value: function addIdForKey(key, id) {
27741 if (key != null) {
27742 this.getIdsFor(key).add(id);
27743 }
27744 }
27745 }, {
27746 key: "deleteIdForKey",
27747 value: function deleteIdForKey(key, id) {
27748 if (key != null) {
27749 this.getIdsFor(key)["delete"](id);
27750 }
27751 }
27752 }, {
27753 key: "getNumberOfIdsForKey",
27754 value: function getNumberOfIdsForKey(key) {
27755 if (key == null) {
27756 return 0;
27757 } else {
27758 return this.getIdsFor(key).size;
27759 }
27760 }
27761 }, {
27762 key: "updateKeyMappingFor",
27763 value: function updateKeyMappingFor(ele) {
27764 var id = ele.id();
27765 var prevKey = this.keyForId.get(id);
27766 var currKey = this.getKey(ele);
27767 this.deleteIdForKey(prevKey, id);
27768 this.addIdForKey(currKey, id);
27769 this.keyForId.set(id, currKey);
27770 }
27771 }, {
27772 key: "deleteKeyMappingFor",
27773 value: function deleteKeyMappingFor(ele) {
27774 var id = ele.id();
27775 var prevKey = this.keyForId.get(id);
27776 this.deleteIdForKey(prevKey, id);
27777 this.keyForId["delete"](id);
27778 }
27779 }, {
27780 key: "keyHasChangedFor",
27781 value: function keyHasChangedFor(ele) {
27782 var id = ele.id();
27783 var prevKey = this.keyForId.get(id);
27784 var newKey = this.getKey(ele);
27785 return prevKey !== newKey;
27786 }
27787 }, {
27788 key: "isInvalid",
27789 value: function isInvalid(ele) {
27790 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27791 }
27792 }, {
27793 key: "getCachesAt",
27794 value: function getCachesAt(lvl) {
27795 var cachesByLvl = this.cachesByLvl,
27796 lvls = this.lvls;
27797 var caches = cachesByLvl.get(lvl);
27798
27799 if (!caches) {
27800 caches = new Map$1();
27801 cachesByLvl.set(lvl, caches);
27802 lvls.push(lvl);
27803 }
27804
27805 return caches;
27806 }
27807 }, {
27808 key: "getCache",
27809 value: function getCache(key, lvl) {
27810 return this.getCachesAt(lvl).get(key);
27811 }
27812 }, {
27813 key: "get",
27814 value: function get(ele, lvl) {
27815 var key = this.getKey(ele);
27816 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27817
27818 if (cache != null) {
27819 this.updateKeyMappingFor(ele);
27820 }
27821
27822 return cache;
27823 }
27824 }, {
27825 key: "getForCachedKey",
27826 value: function getForCachedKey(ele, lvl) {
27827 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27828
27829 var cache = this.getCache(key, lvl);
27830 return cache;
27831 }
27832 }, {
27833 key: "hasCache",
27834 value: function hasCache(key, lvl) {
27835 return this.getCachesAt(lvl).has(key);
27836 }
27837 }, {
27838 key: "has",
27839 value: function has(ele, lvl) {
27840 var key = this.getKey(ele);
27841 return this.hasCache(key, lvl);
27842 }
27843 }, {
27844 key: "setCache",
27845 value: function setCache(key, lvl, cache) {
27846 cache.key = key;
27847 this.getCachesAt(lvl).set(key, cache);
27848 }
27849 }, {
27850 key: "set",
27851 value: function set(ele, lvl, cache) {
27852 var key = this.getKey(ele);
27853 this.setCache(key, lvl, cache);
27854 this.updateKeyMappingFor(ele);
27855 }
27856 }, {
27857 key: "deleteCache",
27858 value: function deleteCache(key, lvl) {
27859 this.getCachesAt(lvl)["delete"](key);
27860 }
27861 }, {
27862 key: "delete",
27863 value: function _delete(ele, lvl) {
27864 var key = this.getKey(ele);
27865 this.deleteCache(key, lvl);
27866 }
27867 }, {
27868 key: "invalidateKey",
27869 value: function invalidateKey(key) {
27870 var _this = this;
27871
27872 this.lvls.forEach(function (lvl) {
27873 return _this.deleteCache(key, lvl);
27874 });
27875 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27876
27877 }, {
27878 key: "invalidate",
27879 value: function invalidate(ele) {
27880 var id = ele.id();
27881 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27882
27883 this.deleteKeyMappingFor(ele);
27884 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27885
27886 if (entireKeyInvalidated) {
27887 // clear mapping for current key
27888 this.invalidateKey(key);
27889 }
27890
27891 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27892 }
27893 }]);
27894
27895 return ElementTextureCacheLookup;
27896 }();
27897
27898 var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27899
27900 var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27901
27902 var minLvl = -4; // when scaling smaller than that we don't need to re-render
27903
27904 var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27905
27906 var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27907
27908 var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27909
27910 var defTxrWidth = 1024; // default/minimum texture width
27911
27912 var maxTxrW = 1024; // the maximum width of a texture
27913
27914 var maxTxrH = 1024; // the maximum height of a texture
27915
27916 var minUtility = 0.2; // if usage of texture is less than this, it is retired
27917
27918 var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27919
27920 var maxFullnessChecks = 10; // dequeued after this many checks
27921
27922 var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27923
27924 var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27925
27926 var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27927
27928 var deqFastCost = 0.9; // % of frame time to be used when >60fps
27929
27930 var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27931
27932 var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27933
27934 var getTxrReasons = {
27935 dequeue: 'dequeue',
27936 downscale: 'downscale',
27937 highQuality: 'highQuality'
27938 };
27939 var initDefaults = defaults({
27940 getKey: null,
27941 doesEleInvalidateKey: falsify,
27942 drawElement: null,
27943 getBoundingBox: null,
27944 getRotationPoint: null,
27945 getRotationOffset: null,
27946 isVisible: trueify,
27947 allowEdgeTxrCaching: true,
27948 allowParentTxrCaching: true
27949 });
27950
27951 var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27952 var self = this;
27953 self.renderer = renderer;
27954 self.onDequeues = [];
27955 var opts = initDefaults(initOptions);
27956 extend(self, opts);
27957 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27958 self.setupDequeueing();
27959 };
27960
27961 var ETCp = ElementTextureCache.prototype;
27962 ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27963
27964 ETCp.getTextureQueue = function (txrH) {
27965 var self = this;
27966 self.eleImgCaches = self.eleImgCaches || {};
27967 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27968 }; // the list of usused textures which can be recycled (in use in texture queue)
27969
27970
27971 ETCp.getRetiredTextureQueue = function (txrH) {
27972 var self = this;
27973 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27974 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27975 return rtxtrQ;
27976 }; // queue of element draw requests at different scale levels
27977
27978
27979 ETCp.getElementQueue = function () {
27980 var self = this;
27981 var q = self.eleCacheQueue = self.eleCacheQueue || new heap$1(function (a, b) {
27982 return b.reqs - a.reqs;
27983 });
27984 return q;
27985 }; // queue of element draw requests at different scale levels (element id lookup)
27986
27987
27988 ETCp.getElementKeyToQueue = function () {
27989 var self = this;
27990 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27991 return k2q;
27992 };
27993
27994 ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27995 var self = this;
27996 var r = this.renderer;
27997 var zoom = r.cy.zoom();
27998 var lookup = this.lookup;
27999
28000 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
28001 return null;
28002 }
28003
28004 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
28005 return null;
28006 }
28007
28008 if (lvl == null) {
28009 lvl = Math.ceil(log2(zoom * pxRatio));
28010 }
28011
28012 if (lvl < minLvl) {
28013 lvl = minLvl;
28014 } else if (zoom >= maxZoom || lvl > maxLvl) {
28015 return null;
28016 }
28017
28018 var scale = Math.pow(2, lvl);
28019 var eleScaledH = bb.h * scale;
28020 var eleScaledW = bb.w * scale;
28021 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
28022
28023 if (!this.isVisible(ele, scaledLabelShown)) {
28024 return null;
28025 }
28026
28027 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
28028
28029 if (eleCache && eleCache.invalidated) {
28030 eleCache.invalidated = false;
28031 eleCache.texture.invalidatedWidth -= eleCache.width;
28032 }
28033
28034 if (eleCache) {
28035 return eleCache;
28036 }
28037
28038 var txrH; // which texture height this ele belongs to
28039
28040 if (eleScaledH <= minTxrH) {
28041 txrH = minTxrH;
28042 } else if (eleScaledH <= txrStepH) {
28043 txrH = txrStepH;
28044 } else {
28045 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
28046 }
28047
28048 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
28049 return null; // caching large elements is not efficient
28050 }
28051
28052 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
28053
28054 var txr = txrQ[txrQ.length - 2];
28055
28056 var addNewTxr = function addNewTxr() {
28057 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
28058 }; // try the last one if there is no second last one
28059
28060
28061 if (!txr) {
28062 txr = txrQ[txrQ.length - 1];
28063 } // if the last one doesn't exist, we need a first one
28064
28065
28066 if (!txr) {
28067 txr = addNewTxr();
28068 } // if there's no room in the current texture, we need a new one
28069
28070
28071 if (txr.width - txr.usedWidth < eleScaledW) {
28072 txr = addNewTxr();
28073 }
28074
28075 var scalableFrom = function scalableFrom(otherCache) {
28076 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
28077 };
28078
28079 var deqing = reason && reason === getTxrReasons.dequeue;
28080 var highQualityReq = reason && reason === getTxrReasons.highQuality;
28081 var downscaleReq = reason && reason === getTxrReasons.downscale;
28082 var higherCache; // the nearest cache with a higher level
28083
28084 for (var l = lvl + 1; l <= maxLvl; l++) {
28085 var c = lookup.get(ele, l);
28086
28087 if (c) {
28088 higherCache = c;
28089 break;
28090 }
28091 }
28092
28093 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
28094
28095 var downscale = function downscale() {
28096 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
28097 }; // reset ele area in texture
28098
28099
28100 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28101 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
28102
28103 if (scalableFrom(oneUpCache)) {
28104 // then we can relatively cheaply rescale the existing image w/o rerendering
28105 downscale();
28106 } else if (scalableFrom(higherCache)) {
28107 // then use the higher cache for now and queue the next level down
28108 // to cheaply scale towards the smaller level
28109 if (highQualityReq) {
28110 for (var _l = higherCache.level; _l > lvl; _l--) {
28111 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
28112 }
28113
28114 downscale();
28115 } else {
28116 self.queueElement(ele, higherCache.level - 1);
28117 return higherCache;
28118 }
28119 } else {
28120 var lowerCache; // the nearest cache with a lower level
28121
28122 if (!deqing && !highQualityReq && !downscaleReq) {
28123 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
28124 var _c = lookup.get(ele, _l2);
28125
28126 if (_c) {
28127 lowerCache = _c;
28128 break;
28129 }
28130 }
28131 }
28132
28133 if (scalableFrom(lowerCache)) {
28134 // then use the lower quality cache for now and queue the better one for later
28135 self.queueElement(ele, lvl);
28136 return lowerCache;
28137 }
28138
28139 txr.context.translate(txr.usedWidth, 0);
28140 txr.context.scale(scale, scale);
28141 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
28142 txr.context.scale(1 / scale, 1 / scale);
28143 txr.context.translate(-txr.usedWidth, 0);
28144 }
28145
28146 eleCache = {
28147 x: txr.usedWidth,
28148 texture: txr,
28149 level: lvl,
28150 scale: scale,
28151 width: eleScaledW,
28152 height: eleScaledH,
28153 scaledLabelShown: scaledLabelShown
28154 };
28155 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
28156 txr.eleCaches.push(eleCache);
28157 lookup.set(ele, lvl, eleCache);
28158 self.checkTextureFullness(txr);
28159 return eleCache;
28160 };
28161
28162 ETCp.invalidateElements = function (eles) {
28163 for (var i = 0; i < eles.length; i++) {
28164 this.invalidateElement(eles[i]);
28165 }
28166 };
28167
28168 ETCp.invalidateElement = function (ele) {
28169 var self = this;
28170 var lookup = self.lookup;
28171 var caches = [];
28172 var invalid = lookup.isInvalid(ele);
28173
28174 if (!invalid) {
28175 return; // override the invalidation request if the element key has not changed
28176 }
28177
28178 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
28179 var cache = lookup.getForCachedKey(ele, lvl);
28180
28181 if (cache) {
28182 caches.push(cache);
28183 }
28184 }
28185
28186 var noOtherElesUseCache = lookup.invalidate(ele);
28187
28188 if (noOtherElesUseCache) {
28189 for (var i = 0; i < caches.length; i++) {
28190 var _cache = caches[i];
28191 var txr = _cache.texture; // remove space from the texture it belongs to
28192
28193 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
28194
28195 _cache.invalidated = true; // retire the texture if its utility is low
28196
28197 self.checkTextureUtility(txr);
28198 }
28199 } // remove from queue since the old req was for the old state
28200
28201
28202 self.removeFromQueue(ele);
28203 };
28204
28205 ETCp.checkTextureUtility = function (txr) {
28206 // invalidate all entries in the cache if the cache size is small
28207 if (txr.invalidatedWidth >= minUtility * txr.width) {
28208 this.retireTexture(txr);
28209 }
28210 };
28211
28212 ETCp.checkTextureFullness = function (txr) {
28213 // if texture has been mostly filled and passed over several times, remove
28214 // it from the queue so we don't need to waste time looking at it to put new things
28215 var self = this;
28216 var txrQ = self.getTextureQueue(txr.height);
28217
28218 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
28219 removeFromArray(txrQ, txr);
28220 } else {
28221 txr.fullnessChecks++;
28222 }
28223 };
28224
28225 ETCp.retireTexture = function (txr) {
28226 var self = this;
28227 var txrH = txr.height;
28228 var txrQ = self.getTextureQueue(txrH);
28229 var lookup = this.lookup; // retire the texture from the active / searchable queue:
28230
28231 removeFromArray(txrQ, txr);
28232 txr.retired = true; // remove the refs from the eles to the caches:
28233
28234 var eleCaches = txr.eleCaches;
28235
28236 for (var i = 0; i < eleCaches.length; i++) {
28237 var eleCache = eleCaches[i];
28238 lookup.deleteCache(eleCache.key, eleCache.level);
28239 }
28240
28241 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
28242
28243 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28244 rtxtrQ.push(txr);
28245 };
28246
28247 ETCp.addTexture = function (txrH, minW) {
28248 var self = this;
28249 var txrQ = self.getTextureQueue(txrH);
28250 var txr = {};
28251 txrQ.push(txr);
28252 txr.eleCaches = [];
28253 txr.height = txrH;
28254 txr.width = Math.max(defTxrWidth, minW);
28255 txr.usedWidth = 0;
28256 txr.invalidatedWidth = 0;
28257 txr.fullnessChecks = 0;
28258 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
28259 txr.context = txr.canvas.getContext('2d');
28260 return txr;
28261 };
28262
28263 ETCp.recycleTexture = function (txrH, minW) {
28264 var self = this;
28265 var txrQ = self.getTextureQueue(txrH);
28266 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28267
28268 for (var i = 0; i < rtxtrQ.length; i++) {
28269 var txr = rtxtrQ[i];
28270
28271 if (txr.width >= minW) {
28272 txr.retired = false;
28273 txr.usedWidth = 0;
28274 txr.invalidatedWidth = 0;
28275 txr.fullnessChecks = 0;
28276 clearArray(txr.eleCaches);
28277 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28278 txr.context.clearRect(0, 0, txr.width, txr.height);
28279 removeFromArray(rtxtrQ, txr);
28280 txrQ.push(txr);
28281 return txr;
28282 }
28283 }
28284 };
28285
28286 ETCp.queueElement = function (ele, lvl) {
28287 var self = this;
28288 var q = self.getElementQueue();
28289 var k2q = self.getElementKeyToQueue();
28290 var key = this.getKey(ele);
28291 var existingReq = k2q[key];
28292
28293 if (existingReq) {
28294 // use the max lvl b/c in between lvls are cheap to make
28295 existingReq.level = Math.max(existingReq.level, lvl);
28296 existingReq.eles.merge(ele);
28297 existingReq.reqs++;
28298 q.updateItem(existingReq);
28299 } else {
28300 var req = {
28301 eles: ele.spawn().merge(ele),
28302 level: lvl,
28303 reqs: 1,
28304 key: key
28305 };
28306 q.push(req);
28307 k2q[key] = req;
28308 }
28309 };
28310
28311 ETCp.dequeue = function (pxRatio
28312 /*, extent*/
28313 ) {
28314 var self = this;
28315 var q = self.getElementQueue();
28316 var k2q = self.getElementKeyToQueue();
28317 var dequeued = [];
28318 var lookup = self.lookup;
28319
28320 for (var i = 0; i < maxDeqSize; i++) {
28321 if (q.size() > 0) {
28322 var req = q.pop();
28323 var key = req.key;
28324 var ele = req.eles[0]; // all eles have the same key
28325
28326 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
28327
28328 k2q[key] = null; // dequeueing isn't necessary with an existing cache
28329
28330 if (cacheExists) {
28331 continue;
28332 }
28333
28334 dequeued.push(req);
28335 var bb = self.getBoundingBox(ele);
28336 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
28337 } else {
28338 break;
28339 }
28340 }
28341
28342 return dequeued;
28343 };
28344
28345 ETCp.removeFromQueue = function (ele) {
28346 var self = this;
28347 var q = self.getElementQueue();
28348 var k2q = self.getElementKeyToQueue();
28349 var key = this.getKey(ele);
28350 var req = k2q[key];
28351
28352 if (req != null) {
28353 if (req.eles.length === 1) {
28354 // remove if last ele in the req
28355 // bring to front of queue
28356 req.reqs = MAX_INT;
28357 q.updateItem(req);
28358 q.pop(); // remove from queue
28359
28360 k2q[key] = null; // remove from lookup map
28361 } else {
28362 // otherwise just remove ele from req
28363 req.eles.unmerge(ele);
28364 }
28365 }
28366 };
28367
28368 ETCp.onDequeue = function (fn) {
28369 this.onDequeues.push(fn);
28370 };
28371
28372 ETCp.offDequeue = function (fn) {
28373 removeFromArray(this.onDequeues, fn);
28374 };
28375
28376 ETCp.setupDequeueing = defs.setupDequeueing({
28377 deqRedrawThreshold: deqRedrawThreshold,
28378 deqCost: deqCost,
28379 deqAvgCost: deqAvgCost,
28380 deqNoDrawCost: deqNoDrawCost,
28381 deqFastCost: deqFastCost,
28382 deq: function deq(self, pxRatio, extent) {
28383 return self.dequeue(pxRatio, extent);
28384 },
28385 onDeqd: function onDeqd(self, deqd) {
28386 for (var i = 0; i < self.onDequeues.length; i++) {
28387 var fn = self.onDequeues[i];
28388 fn(deqd);
28389 }
28390 },
28391 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
28392 for (var i = 0; i < deqd.length; i++) {
28393 var eles = deqd[i].eles;
28394
28395 for (var j = 0; j < eles.length; j++) {
28396 var bb = eles[j].boundingBox();
28397
28398 if (boundingBoxesIntersect(bb, extent)) {
28399 return true;
28400 }
28401 }
28402 }
28403
28404 return false;
28405 },
28406 priority: function priority(self) {
28407 return self.renderer.beforeRenderPriorities.eleTxrDeq;
28408 }
28409 });
28410
28411 var defNumLayers = 1; // default number of layers to use
28412
28413 var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
28414
28415 var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
28416
28417 var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
28418
28419 var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
28420
28421 var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
28422
28423 var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
28424
28425 var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
28426
28427 var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
28428
28429 var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
28430
28431 var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
28432
28433 var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
28434
28435 var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
28436
28437 var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
28438 // var log = function(){ console.log.apply( console, arguments ); };
28439
28440 var LayeredTextureCache = function LayeredTextureCache(renderer) {
28441 var self = this;
28442 var r = self.renderer = renderer;
28443 var cy = r.cy;
28444 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
28445
28446 self.firstGet = true;
28447 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
28448 self.skipping = false;
28449 self.eleTxrDeqs = cy.collection();
28450 self.scheduleElementRefinement = lodash_debounce(function () {
28451 self.refineElementTextures(self.eleTxrDeqs);
28452 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
28453 }, refineEleDebounceTime);
28454 r.beforeRender(function (willDraw, now) {
28455 if (now - self.lastInvalidationTime <= invalidThreshold) {
28456 self.skipping = true;
28457 } else {
28458 self.skipping = false;
28459 }
28460 }, r.beforeRenderPriorities.lyrTxrSkip);
28461
28462 var qSort = function qSort(a, b) {
28463 return b.reqs - a.reqs;
28464 };
28465
28466 self.layersQueue = new heap$1(qSort);
28467 self.setupDequeueing();
28468 };
28469
28470 var LTCp = LayeredTextureCache.prototype;
28471 var layerIdPool = 0;
28472 var MAX_INT$1 = Math.pow(2, 53) - 1;
28473
28474 LTCp.makeLayer = function (bb, lvl) {
28475 var scale = Math.pow(2, lvl);
28476 var w = Math.ceil(bb.w * scale);
28477 var h = Math.ceil(bb.h * scale);
28478 var canvas = this.renderer.makeOffscreenCanvas(w, h);
28479 var layer = {
28480 id: layerIdPool = ++layerIdPool % MAX_INT$1,
28481 bb: bb,
28482 level: lvl,
28483 width: w,
28484 height: h,
28485 canvas: canvas,
28486 context: canvas.getContext('2d'),
28487 eles: [],
28488 elesQueue: [],
28489 reqs: 0
28490 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
28491
28492 var cxt = layer.context;
28493 var dx = -layer.bb.x1;
28494 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
28495
28496 cxt.scale(scale, scale);
28497 cxt.translate(dx, dy);
28498 return layer;
28499 };
28500
28501 LTCp.getLayers = function (eles, pxRatio, lvl) {
28502 var self = this;
28503 var r = self.renderer;
28504 var cy = r.cy;
28505 var zoom = cy.zoom();
28506 var firstGet = self.firstGet;
28507 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28508 //log eles.map(function(ele){ return ele.id() }) );
28509
28510 if (lvl == null) {
28511 lvl = Math.ceil(log2(zoom * pxRatio));
28512
28513 if (lvl < minLvl$1) {
28514 lvl = minLvl$1;
28515 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
28516 return null;
28517 }
28518 }
28519
28520 self.validateLayersElesOrdering(lvl, eles);
28521 var layersByLvl = self.layersByLevel;
28522 var scale = Math.pow(2, lvl);
28523 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28524 var bb;
28525 var lvlComplete = self.levelIsComplete(lvl, eles);
28526 var tmpLayers;
28527
28528 var checkTempLevels = function checkTempLevels() {
28529 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28530 self.validateLayersElesOrdering(l, eles);
28531
28532 if (self.levelIsComplete(l, eles)) {
28533 tmpLayers = layersByLvl[l];
28534 return true;
28535 }
28536 };
28537
28538 var checkLvls = function checkLvls(dir) {
28539 if (tmpLayers) {
28540 return;
28541 }
28542
28543 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
28544 if (canUseAsTmpLvl(l)) {
28545 break;
28546 }
28547 }
28548 };
28549
28550 checkLvls(+1);
28551 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28552
28553 for (var i = layers.length - 1; i >= 0; i--) {
28554 var layer = layers[i];
28555
28556 if (layer.invalid) {
28557 removeFromArray(layers, layer);
28558 }
28559 }
28560 };
28561
28562 if (!lvlComplete) {
28563 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28564 // and later queue the current layerset so we can get the proper quality level soon
28565 checkTempLevels();
28566 } else {
28567 // log('level complete, using existing layers\n--');
28568 return layers;
28569 }
28570
28571 var getBb = function getBb() {
28572 if (!bb) {
28573 bb = makeBoundingBox();
28574
28575 for (var i = 0; i < eles.length; i++) {
28576 updateBoundingBox(bb, eles[i].boundingBox());
28577 }
28578 }
28579
28580 return bb;
28581 };
28582
28583 var makeLayer = function makeLayer(opts) {
28584 opts = opts || {};
28585 var after = opts.after;
28586 getBb();
28587 var area = bb.w * scale * (bb.h * scale);
28588
28589 if (area > maxLayerArea) {
28590 return null;
28591 }
28592
28593 var layer = self.makeLayer(bb, lvl);
28594
28595 if (after != null) {
28596 var index = layers.indexOf(after) + 1;
28597 layers.splice(index, 0, layer);
28598 } else if (opts.insert === undefined || opts.insert) {
28599 // no after specified => first layer made so put at start
28600 layers.unshift(layer);
28601 } // if( tmpLayers ){
28602 //self.queueLayer( layer );
28603 // }
28604
28605
28606 return layer;
28607 };
28608
28609 if (self.skipping && !firstGet) {
28610 // log('skip layers');
28611 return null;
28612 } // log('do layers');
28613
28614
28615 var layer = null;
28616 var maxElesPerLayer = eles.length / defNumLayers;
28617 var allowLazyQueueing = !firstGet;
28618
28619 for (var i = 0; i < eles.length; i++) {
28620 var ele = eles[i];
28621 var rs = ele._private.rscratch;
28622 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28623
28624 var existingLayer = caches[lvl];
28625
28626 if (existingLayer) {
28627 // reuse layer for later eles
28628 // log('reuse layer for', ele.id());
28629 layer = existingLayer;
28630 continue;
28631 }
28632
28633 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28634 // log('make new layer for ele %s', ele.id());
28635 layer = makeLayer({
28636 insert: true,
28637 after: layer
28638 }); // if now layer can be built then we can't use layers at this level
28639
28640 if (!layer) {
28641 return null;
28642 } // log('new layer with id %s', layer.id);
28643
28644 }
28645
28646 if (tmpLayers || allowLazyQueueing) {
28647 // log('queue ele %s in layer %s', ele.id(), layer.id);
28648 self.queueLayer(layer, ele);
28649 } else {
28650 // log('draw ele %s in layer %s', ele.id(), layer.id);
28651 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28652 }
28653
28654 layer.eles.push(ele);
28655 caches[lvl] = layer;
28656 } // log('--');
28657
28658
28659 if (tmpLayers) {
28660 // then we only queued the current layerset and can't draw it yet
28661 return tmpLayers;
28662 }
28663
28664 if (allowLazyQueueing) {
28665 // log('lazy queue level', lvl);
28666 return null;
28667 }
28668
28669 return layers;
28670 }; // a layer may want to use an ele cache of a higher level to avoid blurriness
28671 // so the layer level might not equal the ele level
28672
28673
28674 LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28675 return lvl;
28676 };
28677
28678 LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28679 var self = this;
28680 var r = this.renderer;
28681 var context = layer.context;
28682 var bb = ele.boundingBox();
28683
28684 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28685 return;
28686 }
28687
28688 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28689
28690 {
28691 r.setImgSmoothing(context, false);
28692 }
28693
28694 {
28695 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28696 }
28697
28698 {
28699 r.setImgSmoothing(context, true);
28700 }
28701 };
28702
28703 LTCp.levelIsComplete = function (lvl, eles) {
28704 var self = this;
28705 var layers = self.layersByLevel[lvl];
28706
28707 if (!layers || layers.length === 0) {
28708 return false;
28709 }
28710
28711 var numElesInLayers = 0;
28712
28713 for (var i = 0; i < layers.length; i++) {
28714 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28715
28716 if (layer.reqs > 0) {
28717 return false;
28718 } // if the layer is invalid, the level is not complete
28719
28720
28721 if (layer.invalid) {
28722 return false;
28723 }
28724
28725 numElesInLayers += layer.eles.length;
28726 } // we should have exactly the number of eles passed in to be complete
28727
28728
28729 if (numElesInLayers !== eles.length) {
28730 return false;
28731 }
28732
28733 return true;
28734 };
28735
28736 LTCp.validateLayersElesOrdering = function (lvl, eles) {
28737 var layers = this.layersByLevel[lvl];
28738
28739 if (!layers) {
28740 return;
28741 } // if in a layer the eles are not in the same order, then the layer is invalid
28742 // (i.e. there is an ele in between the eles in the layer)
28743
28744
28745 for (var i = 0; i < layers.length; i++) {
28746 var layer = layers[i];
28747 var offset = -1; // find the offset
28748
28749 for (var j = 0; j < eles.length; j++) {
28750 if (layer.eles[0] === eles[j]) {
28751 offset = j;
28752 break;
28753 }
28754 }
28755
28756 if (offset < 0) {
28757 // then the layer has nonexistant elements and is invalid
28758 this.invalidateLayer(layer);
28759 continue;
28760 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28761
28762
28763 var o = offset;
28764
28765 for (var j = 0; j < layer.eles.length; j++) {
28766 if (layer.eles[j] !== eles[o + j]) {
28767 // log('invalidate based on ordering', layer.id);
28768 this.invalidateLayer(layer);
28769 break;
28770 }
28771 }
28772 }
28773 };
28774
28775 LTCp.updateElementsInLayers = function (eles, update) {
28776 var self = this;
28777 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28778 // layer itself along the way
28779
28780 for (var i = 0; i < eles.length; i++) {
28781 var req = isEles ? null : eles[i];
28782 var ele = isEles ? eles[i] : eles[i].ele;
28783 var rs = ele._private.rscratch;
28784 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28785
28786 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28787 var layer = caches[l];
28788
28789 if (!layer) {
28790 continue;
28791 } // if update is a request from the ele cache, then it affects only
28792 // the matching level
28793
28794
28795 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28796 continue;
28797 }
28798
28799 update(layer, ele, req);
28800 }
28801 }
28802 };
28803
28804 LTCp.haveLayers = function () {
28805 var self = this;
28806 var haveLayers = false;
28807
28808 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28809 var layers = self.layersByLevel[l];
28810
28811 if (layers && layers.length > 0) {
28812 haveLayers = true;
28813 break;
28814 }
28815 }
28816
28817 return haveLayers;
28818 };
28819
28820 LTCp.invalidateElements = function (eles) {
28821 var self = this;
28822
28823 if (eles.length === 0) {
28824 return;
28825 }
28826
28827 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28828
28829 if (eles.length === 0 || !self.haveLayers()) {
28830 return;
28831 }
28832
28833 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28834 self.invalidateLayer(layer);
28835 });
28836 };
28837
28838 LTCp.invalidateLayer = function (layer) {
28839 // log('update invalidate layer time');
28840 this.lastInvalidationTime = performanceNow();
28841
28842 if (layer.invalid) {
28843 return;
28844 } // save cycles
28845
28846
28847 var lvl = layer.level;
28848 var eles = layer.eles;
28849 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28850
28851 removeFromArray(layers, layer); // layer.eles = [];
28852
28853 layer.elesQueue = [];
28854 layer.invalid = true;
28855
28856 if (layer.replacement) {
28857 layer.replacement.invalid = true;
28858 }
28859
28860 for (var i = 0; i < eles.length; i++) {
28861 var caches = eles[i]._private.rscratch.imgLayerCaches;
28862
28863 if (caches) {
28864 caches[lvl] = null;
28865 }
28866 }
28867 };
28868
28869 LTCp.refineElementTextures = function (eles) {
28870 var self = this; // log('refine', eles.length);
28871
28872 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28873 var rLyr = layer.replacement;
28874
28875 if (!rLyr) {
28876 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28877 rLyr.replaces = layer;
28878 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28879 }
28880
28881 if (!rLyr.reqs) {
28882 for (var i = 0; i < rLyr.eles.length; i++) {
28883 self.queueLayer(rLyr, rLyr.eles[i]);
28884 } // log('queue replacement layer refinement', rLyr.id);
28885
28886 }
28887 });
28888 };
28889
28890 LTCp.enqueueElementRefinement = function (ele) {
28891
28892 this.eleTxrDeqs.merge(ele);
28893 this.scheduleElementRefinement();
28894 };
28895
28896 LTCp.queueLayer = function (layer, ele) {
28897 var self = this;
28898 var q = self.layersQueue;
28899 var elesQ = layer.elesQueue;
28900 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28901
28902 if (layer.replacement) {
28903 return;
28904 }
28905
28906 if (ele) {
28907 if (hasId[ele.id()]) {
28908 return;
28909 }
28910
28911 elesQ.push(ele);
28912 hasId[ele.id()] = true;
28913 }
28914
28915 if (layer.reqs) {
28916 layer.reqs++;
28917 q.updateItem(layer);
28918 } else {
28919 layer.reqs = 1;
28920 q.push(layer);
28921 }
28922 };
28923
28924 LTCp.dequeue = function (pxRatio) {
28925 var self = this;
28926 var q = self.layersQueue;
28927 var deqd = [];
28928 var eleDeqs = 0;
28929
28930 while (eleDeqs < maxDeqSize$1) {
28931 if (q.size() === 0) {
28932 break;
28933 }
28934
28935 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28936
28937 if (layer.replacement) {
28938 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28939 q.pop();
28940 continue;
28941 } // if this is a replacement layer that has been superceded, then forget it
28942
28943
28944 if (layer.replaces && layer !== layer.replaces.replacement) {
28945 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28946 q.pop();
28947 continue;
28948 }
28949
28950 if (layer.invalid) {
28951 // log('replacement layer %s is invalid; dequeued', layer.id);
28952 q.pop();
28953 continue;
28954 }
28955
28956 var ele = layer.elesQueue.shift();
28957
28958 if (ele) {
28959 // log('dequeue layer %s', layer.id);
28960 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28961 eleDeqs++;
28962 }
28963
28964 if (deqd.length === 0) {
28965 // we need only one entry in deqd to queue redrawing etc
28966 deqd.push(true);
28967 } // if the layer has all its eles done, then remove from the queue
28968
28969
28970 if (layer.elesQueue.length === 0) {
28971 q.pop();
28972 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28973 // when a replacement layer is dequeued, it replaces the old layer in the level
28974
28975 if (layer.replaces) {
28976 self.applyLayerReplacement(layer);
28977 }
28978
28979 self.requestRedraw();
28980 }
28981 }
28982
28983 return deqd;
28984 };
28985
28986 LTCp.applyLayerReplacement = function (layer) {
28987 var self = this;
28988 var layersInLevel = self.layersByLevel[layer.level];
28989 var replaced = layer.replaces;
28990 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28991 // refs would be a mistake (i.e. overwriting the true active layer)
28992
28993 if (index < 0 || replaced.invalid) {
28994 // log('replacement layer would have no effect', layer.id);
28995 return;
28996 }
28997
28998 layersInLevel[index] = layer; // replace level ref
28999 // replace refs in eles
29000
29001 for (var i = 0; i < layer.eles.length; i++) {
29002 var _p = layer.eles[i]._private;
29003 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
29004
29005 if (cache) {
29006 cache[layer.level] = layer;
29007 }
29008 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
29009
29010
29011 self.requestRedraw();
29012 };
29013
29014 LTCp.requestRedraw = lodash_debounce(function () {
29015 var r = this.renderer;
29016 r.redrawHint('eles', true);
29017 r.redrawHint('drag', true);
29018 r.redraw();
29019 }, 100);
29020 LTCp.setupDequeueing = defs.setupDequeueing({
29021 deqRedrawThreshold: deqRedrawThreshold$1,
29022 deqCost: deqCost$1,
29023 deqAvgCost: deqAvgCost$1,
29024 deqNoDrawCost: deqNoDrawCost$1,
29025 deqFastCost: deqFastCost$1,
29026 deq: function deq(self, pxRatio) {
29027 return self.dequeue(pxRatio);
29028 },
29029 onDeqd: noop,
29030 shouldRedraw: trueify,
29031 priority: function priority(self) {
29032 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
29033 }
29034 });
29035
29036 var CRp = {};
29037 var impl;
29038
29039 function polygon(context, points) {
29040 for (var i = 0; i < points.length; i++) {
29041 var pt = points[i];
29042 context.lineTo(pt.x, pt.y);
29043 }
29044 }
29045
29046 function triangleBackcurve(context, points, controlPoint) {
29047 var firstPt;
29048
29049 for (var i = 0; i < points.length; i++) {
29050 var pt = points[i];
29051
29052 if (i === 0) {
29053 firstPt = pt;
29054 }
29055
29056 context.lineTo(pt.x, pt.y);
29057 }
29058
29059 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
29060 }
29061
29062 function triangleTee(context, trianglePoints, teePoints) {
29063 if (context.beginPath) {
29064 context.beginPath();
29065 }
29066
29067 var triPts = trianglePoints;
29068
29069 for (var i = 0; i < triPts.length; i++) {
29070 var pt = triPts[i];
29071 context.lineTo(pt.x, pt.y);
29072 }
29073
29074 var teePts = teePoints;
29075 var firstTeePt = teePoints[0];
29076 context.moveTo(firstTeePt.x, firstTeePt.y);
29077
29078 for (var i = 1; i < teePts.length; i++) {
29079 var pt = teePts[i];
29080 context.lineTo(pt.x, pt.y);
29081 }
29082
29083 if (context.closePath) {
29084 context.closePath();
29085 }
29086 }
29087
29088 function circle(context, rx, ry, r) {
29089 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29090 }
29091
29092 CRp.arrowShapeImpl = function (name) {
29093 return (impl || (impl = {
29094 'polygon': polygon,
29095 'triangle-backcurve': triangleBackcurve,
29096 'triangle-tee': triangleTee,
29097 'triangle-cross': triangleTee,
29098 'circle': circle
29099 }))[name];
29100 };
29101
29102 var CRp$1 = {};
29103
29104 CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
29105 var r = this;
29106
29107 if (ele.isNode()) {
29108 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29109 } else {
29110 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29111 }
29112 };
29113
29114 CRp$1.drawElementOverlay = function (context, ele) {
29115 var r = this;
29116
29117 if (ele.isNode()) {
29118 r.drawNodeOverlay(context, ele);
29119 } else {
29120 r.drawEdgeOverlay(context, ele);
29121 }
29122 };
29123
29124 CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
29125 var r = this;
29126 var bb = eleTxrCache.getBoundingBox(ele);
29127
29128 if (bb.w === 0 || bb.h === 0) {
29129 return;
29130 } // ignore zero size case
29131
29132
29133 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
29134
29135 if (eleCache != null) {
29136 var opacity = getOpacity(r, ele);
29137
29138 if (opacity === 0) {
29139 return;
29140 }
29141
29142 var theta = getRotation(r, ele);
29143 var x1 = bb.x1,
29144 y1 = bb.y1,
29145 w = bb.w,
29146 h = bb.h;
29147 var x, y, sx, sy, smooth;
29148
29149 if (theta !== 0) {
29150 var rotPt = eleTxrCache.getRotationPoint(ele);
29151 sx = rotPt.x;
29152 sy = rotPt.y;
29153 context.translate(sx, sy);
29154 context.rotate(theta);
29155 smooth = r.getImgSmoothing(context);
29156
29157 if (!smooth) {
29158 r.setImgSmoothing(context, true);
29159 }
29160
29161 var off = eleTxrCache.getRotationOffset(ele);
29162 x = off.x;
29163 y = off.y;
29164 } else {
29165 x = x1;
29166 y = y1;
29167 }
29168
29169 var oldGlobalAlpha;
29170
29171 if (opacity !== 1) {
29172 oldGlobalAlpha = context.globalAlpha;
29173 context.globalAlpha = oldGlobalAlpha * opacity;
29174 }
29175
29176 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
29177
29178 if (opacity !== 1) {
29179 context.globalAlpha = oldGlobalAlpha;
29180 }
29181
29182 if (theta !== 0) {
29183 context.rotate(-theta);
29184 context.translate(-sx, -sy);
29185
29186 if (!smooth) {
29187 r.setImgSmoothing(context, false);
29188 }
29189 }
29190 } else {
29191 eleTxrCache.drawElement(context, ele); // direct draw fallback
29192 }
29193 };
29194
29195 var getZeroRotation = function getZeroRotation() {
29196 return 0;
29197 };
29198
29199 var getLabelRotation = function getLabelRotation(r, ele) {
29200 return r.getTextAngle(ele, null);
29201 };
29202
29203 var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
29204 return r.getTextAngle(ele, 'source');
29205 };
29206
29207 var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
29208 return r.getTextAngle(ele, 'target');
29209 };
29210
29211 var getOpacity = function getOpacity(r, ele) {
29212 return ele.effectiveOpacity();
29213 };
29214
29215 var getTextOpacity = function getTextOpacity(e, ele) {
29216 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
29217 };
29218
29219 CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
29220 var r = this;
29221 var _r$data = r.data,
29222 eleTxrCache = _r$data.eleTxrCache,
29223 lblTxrCache = _r$data.lblTxrCache,
29224 slbTxrCache = _r$data.slbTxrCache,
29225 tlbTxrCache = _r$data.tlbTxrCache;
29226 var bb = ele.boundingBox();
29227 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
29228
29229 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
29230 return;
29231 }
29232
29233 if (!extent || boundingBoxesIntersect(bb, extent)) {
29234 var isEdge = ele.isEdge();
29235
29236 var badLine = ele.element()._private.rscratch.badLine;
29237
29238 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
29239
29240 if (!isEdge || !badLine) {
29241 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
29242 }
29243
29244 if (isEdge && !badLine) {
29245 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
29246 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
29247 }
29248
29249 r.drawElementOverlay(context, ele);
29250 }
29251 };
29252
29253 CRp$1.drawElements = function (context, eles) {
29254 var r = this;
29255
29256 for (var i = 0; i < eles.length; i++) {
29257 var ele = eles[i];
29258 r.drawElement(context, ele);
29259 }
29260 };
29261
29262 CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
29263 var r = this;
29264
29265 for (var i = 0; i < eles.length; i++) {
29266 var ele = eles[i];
29267 r.drawCachedElement(context, ele, pxRatio, extent);
29268 }
29269 };
29270
29271 CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
29272 var r = this;
29273
29274 for (var i = 0; i < eles.length; i++) {
29275 var ele = eles[i];
29276
29277 if (!ele.isNode()) {
29278 continue;
29279 }
29280
29281 r.drawCachedElement(context, ele, pxRatio, extent);
29282 }
29283 };
29284
29285 CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
29286 var r = this;
29287 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
29288
29289 if (layers) {
29290 for (var i = 0; i < layers.length; i++) {
29291 var layer = layers[i];
29292 var bb = layer.bb;
29293
29294 if (bb.w === 0 || bb.h === 0) {
29295 continue;
29296 }
29297
29298 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
29299 }
29300 } else {
29301 // fall back on plain caching if no layers
29302 r.drawCachedElements(context, eles, pxRatio, extent);
29303 }
29304 };
29305
29306 /* global Path2D */
29307 var CRp$2 = {};
29308
29309 CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
29310 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29311 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29312 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29313 var r = this;
29314 var rs = edge._private.rscratch;
29315
29316 if (shouldDrawOpacity && !edge.visible()) {
29317 return;
29318 } // if bezier ctrl pts can not be calculated, then die
29319
29320
29321 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
29322 // isNaN in case edge is impossible and browser bugs (e.g. safari)
29323 return;
29324 }
29325
29326 var bb;
29327
29328 if (shiftToOriginWithBb) {
29329 bb = shiftToOriginWithBb;
29330 context.translate(-bb.x1, -bb.y1);
29331 }
29332
29333 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
29334 var lineStyle = edge.pstyle('line-style').value;
29335 var edgeWidth = edge.pstyle('width').pfValue;
29336 var lineCap = edge.pstyle('line-cap').value;
29337
29338 var drawLine = function drawLine() {
29339 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
29340 context.lineWidth = edgeWidth;
29341 context.lineCap = lineCap;
29342 r.eleStrokeStyle(context, edge, strokeOpacity);
29343 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
29344 context.lineCap = 'butt'; // reset for other drawing functions
29345 };
29346
29347 var drawOverlay = function drawOverlay() {
29348 if (!shouldDrawOverlay) {
29349 return;
29350 }
29351
29352 r.drawEdgeOverlay(context, edge);
29353 };
29354
29355 var drawArrows = function drawArrows() {
29356 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
29357 r.drawArrowheads(context, edge, arrowOpacity);
29358 };
29359
29360 var drawText = function drawText() {
29361 r.drawElementText(context, edge, null, drawLabel);
29362 };
29363
29364 context.lineJoin = 'round';
29365 var ghost = edge.pstyle('ghost').value === 'yes';
29366
29367 if (ghost) {
29368 var gx = edge.pstyle('ghost-offset-x').pfValue;
29369 var gy = edge.pstyle('ghost-offset-y').pfValue;
29370 var ghostOpacity = edge.pstyle('ghost-opacity').value;
29371 var effectiveGhostOpacity = opacity * ghostOpacity;
29372 context.translate(gx, gy);
29373 drawLine(effectiveGhostOpacity);
29374 drawArrows(effectiveGhostOpacity);
29375 context.translate(-gx, -gy);
29376 }
29377
29378 drawLine();
29379 drawArrows();
29380 drawOverlay();
29381 drawText();
29382
29383 if (shiftToOriginWithBb) {
29384 context.translate(bb.x1, bb.y1);
29385 }
29386 };
29387
29388 CRp$2.drawEdgeOverlay = function (context, edge) {
29389 if (!edge.visible()) {
29390 return;
29391 }
29392
29393 var overlayOpacity = edge.pstyle('overlay-opacity').value;
29394
29395 if (overlayOpacity === 0) {
29396 return;
29397 }
29398
29399 var r = this;
29400 var usePaths = r.usePaths();
29401 var rs = edge._private.rscratch;
29402 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
29403 var overlayWidth = 2 * overlayPadding;
29404 var overlayColor = edge.pstyle('overlay-color').value;
29405 context.lineWidth = overlayWidth;
29406
29407 if (rs.edgeType === 'self' && !usePaths) {
29408 context.lineCap = 'butt';
29409 } else {
29410 context.lineCap = 'round';
29411 }
29412
29413 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29414 r.drawEdgePath(edge, context, rs.allpts, 'solid');
29415 };
29416
29417 CRp$2.drawEdgePath = function (edge, context, pts, type) {
29418 var rs = edge._private.rscratch;
29419 var canvasCxt = context;
29420 var path;
29421 var pathCacheHit = false;
29422 var usePaths = this.usePaths();
29423 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
29424 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
29425
29426 if (usePaths) {
29427 var pathCacheKey = pts.join('$');
29428 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
29429
29430 if (keyMatches) {
29431 path = context = rs.pathCache;
29432 pathCacheHit = true;
29433 } else {
29434 path = context = new Path2D();
29435 rs.pathCacheKey = pathCacheKey;
29436 rs.pathCache = path;
29437 }
29438 }
29439
29440 if (canvasCxt.setLineDash) {
29441 // for very outofdate browsers
29442 switch (type) {
29443 case 'dotted':
29444 canvasCxt.setLineDash([1, 1]);
29445 break;
29446
29447 case 'dashed':
29448 canvasCxt.setLineDash(lineDashPattern);
29449 canvasCxt.lineDashOffset = lineDashOffset;
29450 break;
29451
29452 case 'solid':
29453 canvasCxt.setLineDash([]);
29454 break;
29455 }
29456 }
29457
29458 if (!pathCacheHit && !rs.badLine) {
29459 if (context.beginPath) {
29460 context.beginPath();
29461 }
29462
29463 context.moveTo(pts[0], pts[1]);
29464
29465 switch (rs.edgeType) {
29466 case 'bezier':
29467 case 'self':
29468 case 'compound':
29469 case 'multibezier':
29470 for (var i = 2; i + 3 < pts.length; i += 4) {
29471 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29472 }
29473
29474 break;
29475
29476 case 'straight':
29477 case 'segments':
29478 case 'haystack':
29479 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29480 context.lineTo(pts[_i], pts[_i + 1]);
29481 }
29482
29483 break;
29484 }
29485 }
29486
29487 context = canvasCxt;
29488
29489 if (usePaths) {
29490 context.stroke(path);
29491 } else {
29492 context.stroke();
29493 } // reset any line dashes
29494
29495
29496 if (context.setLineDash) {
29497 // for very outofdate browsers
29498 context.setLineDash([]);
29499 }
29500 };
29501
29502 CRp$2.drawArrowheads = function (context, edge, opacity) {
29503 var rs = edge._private.rscratch;
29504 var isHaystack = rs.edgeType === 'haystack';
29505
29506 if (!isHaystack) {
29507 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29508 }
29509
29510 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29511 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29512
29513 if (!isHaystack) {
29514 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29515 }
29516 };
29517
29518 CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29519 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29520 return;
29521 }
29522
29523 var self = this;
29524 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29525
29526 if (arrowShape === 'none') {
29527 return;
29528 }
29529
29530 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29531 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29532 var edgeWidth = edge.pstyle('width').pfValue;
29533 var edgeOpacity = edge.pstyle('opacity').value;
29534
29535 if (opacity === undefined) {
29536 opacity = edgeOpacity;
29537 }
29538
29539 var gco = context.globalCompositeOperation;
29540
29541 if (opacity !== 1 || arrowFill === 'hollow') {
29542 // then extra clear is needed
29543 context.globalCompositeOperation = 'destination-out';
29544 self.colorFillStyle(context, 255, 255, 255, 1);
29545 self.colorStrokeStyle(context, 255, 255, 255, 1);
29546 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29547 context.globalCompositeOperation = gco;
29548 } // otherwise, the opaque arrow clears it for free :)
29549
29550
29551 var color = edge.pstyle(prefix + '-arrow-color').value;
29552 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29553 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29554 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29555 };
29556
29557 CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29558 var r = this;
29559 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29560 var pathCacheHit = false;
29561 var path;
29562 var canvasContext = context;
29563 var translation = {
29564 x: x,
29565 y: y
29566 };
29567 var scale = edge.pstyle('arrow-scale').value;
29568 var size = this.getArrowWidth(edgeWidth, scale);
29569 var shapeImpl = r.arrowShapes[shape];
29570
29571 if (usePaths) {
29572 var cache = r.arrowPathCache = r.arrowPathCache || [];
29573 var key = hashString(shape);
29574 var cachedPath = cache[key];
29575
29576 if (cachedPath != null) {
29577 path = context = cachedPath;
29578 pathCacheHit = true;
29579 } else {
29580 path = context = new Path2D();
29581 cache[key] = path;
29582 }
29583 }
29584
29585 if (!pathCacheHit) {
29586 if (context.beginPath) {
29587 context.beginPath();
29588 }
29589
29590 if (usePaths) {
29591 // store in the path cache with values easily manipulated later
29592 shapeImpl.draw(context, 1, 0, {
29593 x: 0,
29594 y: 0
29595 }, 1);
29596 } else {
29597 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29598 }
29599
29600 if (context.closePath) {
29601 context.closePath();
29602 }
29603 }
29604
29605 context = canvasContext;
29606
29607 if (usePaths) {
29608 // set transform to arrow position/orientation
29609 context.translate(x, y);
29610 context.rotate(angle);
29611 context.scale(size, size);
29612 }
29613
29614 if (fill === 'filled' || fill === 'both') {
29615 if (usePaths) {
29616 context.fill(path);
29617 } else {
29618 context.fill();
29619 }
29620 }
29621
29622 if (fill === 'hollow' || fill === 'both') {
29623 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29624 context.lineJoin = 'miter';
29625
29626 if (usePaths) {
29627 context.stroke(path);
29628 } else {
29629 context.stroke();
29630 }
29631 }
29632
29633 if (usePaths) {
29634 // reset transform by applying inverse
29635 context.scale(1 / size, 1 / size);
29636 context.rotate(-angle);
29637 context.translate(-x, -y);
29638 }
29639 };
29640
29641 var CRp$3 = {};
29642
29643 CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29644 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29645 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29646 return;
29647 }
29648
29649 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29650 };
29651
29652 CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29653 var r = this;
29654 var pos = node.position();
29655 var nodeX = pos.x;
29656 var nodeY = pos.y;
29657 var styleObj = node.cy().style();
29658 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29659 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29660 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29661 var nodeW = node.width();
29662 var nodeH = node.height();
29663 var paddingX2 = node.padding() * 2;
29664 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29665 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29666 var rs = node._private.rscratch;
29667 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29668 var shouldClip = clip === 'node';
29669 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29670 var imgW = img.width || img.cachedW;
29671 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29672
29673 if (null == imgW || null == imgH) {
29674 document.body.appendChild(img); // eslint-disable-line no-undef
29675
29676 imgW = img.cachedW = img.width || img.offsetWidth;
29677 imgH = img.cachedH = img.height || img.offsetHeight;
29678 document.body.removeChild(img); // eslint-disable-line no-undef
29679 }
29680
29681 var w = imgW;
29682 var h = imgH;
29683
29684 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29685 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29686 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29687 } else {
29688 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29689 }
29690 }
29691
29692 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29693 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29694 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29695 } else {
29696 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29697 }
29698 }
29699
29700 if (w === 0 || h === 0) {
29701 return; // no point in drawing empty image (and chrome is broken in this case)
29702 }
29703
29704 if (fit === 'contain') {
29705 var scale = Math.min(nodeTW / w, nodeTH / h);
29706 w *= scale;
29707 h *= scale;
29708 } else if (fit === 'cover') {
29709 var scale = Math.max(nodeTW / w, nodeTH / h);
29710 w *= scale;
29711 h *= scale;
29712 }
29713
29714 var x = nodeX - nodeTW / 2; // left
29715
29716 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29717 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29718
29719 if (posXUnits === '%') {
29720 x += (nodeTW - w) * posXPfVal;
29721 } else {
29722 x += posXPfVal;
29723 }
29724
29725 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29726 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29727
29728 if (offXUnits === '%') {
29729 x += (nodeTW - w) * offXPfVal;
29730 } else {
29731 x += offXPfVal;
29732 }
29733
29734 var y = nodeY - nodeTH / 2; // top
29735
29736 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29737 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29738
29739 if (posYUnits === '%') {
29740 y += (nodeTH - h) * posYPfVal;
29741 } else {
29742 y += posYPfVal;
29743 }
29744
29745 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29746 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29747
29748 if (offYUnits === '%') {
29749 y += (nodeTH - h) * offYPfVal;
29750 } else {
29751 y += offYPfVal;
29752 }
29753
29754 if (rs.pathCache) {
29755 x -= nodeX;
29756 y -= nodeY;
29757 nodeX = 0;
29758 nodeY = 0;
29759 }
29760
29761 var gAlpha = context.globalAlpha;
29762 context.globalAlpha = imgOpacity;
29763
29764 if (repeat === 'no-repeat') {
29765 if (shouldClip) {
29766 context.save();
29767
29768 if (rs.pathCache) {
29769 context.clip(rs.pathCache);
29770 } else {
29771 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29772 context.clip();
29773 }
29774 }
29775
29776 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29777
29778 if (shouldClip) {
29779 context.restore();
29780 }
29781 } else {
29782 var pattern = context.createPattern(img, repeat);
29783 context.fillStyle = pattern;
29784 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29785 context.translate(x, y);
29786 context.fill();
29787 context.translate(-x, -y);
29788 }
29789
29790 context.globalAlpha = gAlpha;
29791 };
29792
29793 var CRp$4 = {};
29794
29795 CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29796 if (!scale) {
29797 var zoom = ele.cy().zoom();
29798 var pxRatio = this.getPixelRatio();
29799 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29800
29801 scale = Math.pow(2, lvl);
29802 }
29803
29804 var computedSize = ele.pstyle('font-size').pfValue * scale;
29805 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29806
29807 if (computedSize < minSize) {
29808 return false;
29809 }
29810
29811 return true;
29812 };
29813
29814 CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29815 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29816 var r = this;
29817
29818 if (force == null) {
29819 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29820 return;
29821 }
29822 } else if (force === false) {
29823 return;
29824 }
29825
29826 if (ele.isNode()) {
29827 var label = ele.pstyle('label');
29828
29829 if (!label || !label.value) {
29830 return;
29831 }
29832
29833 var justification = r.getLabelJustification(ele);
29834 context.textAlign = justification;
29835 context.textBaseline = 'bottom';
29836 } else {
29837 var badLine = ele.element()._private.rscratch.badLine;
29838
29839 var _label = ele.pstyle('label');
29840
29841 var srcLabel = ele.pstyle('source-label');
29842 var tgtLabel = ele.pstyle('target-label');
29843
29844 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29845 return;
29846 }
29847
29848 context.textAlign = 'center';
29849 context.textBaseline = 'bottom';
29850 }
29851
29852 var applyRotation = !shiftToOriginWithBb;
29853 var bb;
29854
29855 if (shiftToOriginWithBb) {
29856 bb = shiftToOriginWithBb;
29857 context.translate(-bb.x1, -bb.y1);
29858 }
29859
29860 if (prefix == null) {
29861 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29862
29863 if (ele.isEdge()) {
29864 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29865 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29866 }
29867 } else {
29868 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29869 }
29870
29871 if (shiftToOriginWithBb) {
29872 context.translate(bb.x1, bb.y1);
29873 }
29874 };
29875
29876 CRp$4.getFontCache = function (context) {
29877 var cache;
29878 this.fontCaches = this.fontCaches || [];
29879
29880 for (var i = 0; i < this.fontCaches.length; i++) {
29881 cache = this.fontCaches[i];
29882
29883 if (cache.context === context) {
29884 return cache;
29885 }
29886 }
29887
29888 cache = {
29889 context: context
29890 };
29891 this.fontCaches.push(cache);
29892 return cache;
29893 }; // set up canvas context with font
29894 // returns transformed text string
29895
29896
29897 CRp$4.setupTextStyle = function (context, ele) {
29898 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29899 // Font style
29900 var labelStyle = ele.pstyle('font-style').strValue;
29901 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29902 var labelFamily = ele.pstyle('font-family').strValue;
29903 var labelWeight = ele.pstyle('font-weight').strValue;
29904 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29905 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29906 var color = ele.pstyle('color').value;
29907 var outlineColor = ele.pstyle('text-outline-color').value;
29908 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29909 context.lineJoin = 'round'; // so text outlines aren't jagged
29910
29911 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29912 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29913 }; // TODO ensure re-used
29914
29915
29916 function roundRect(ctx, x, y, width, height) {
29917 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29918 ctx.beginPath();
29919 ctx.moveTo(x + radius, y);
29920 ctx.lineTo(x + width - radius, y);
29921 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29922 ctx.lineTo(x + width, y + height - radius);
29923 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29924 ctx.lineTo(x + radius, y + height);
29925 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29926 ctx.lineTo(x, y + radius);
29927 ctx.quadraticCurveTo(x, y, x + radius, y);
29928 ctx.closePath();
29929 ctx.fill();
29930 }
29931
29932 CRp$4.getTextAngle = function (ele, prefix) {
29933 var theta;
29934 var _p = ele._private;
29935 var rscratch = _p.rscratch;
29936 var pdash = prefix ? prefix + '-' : '';
29937 var rotation = ele.pstyle(pdash + 'text-rotation');
29938 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29939
29940 if (rotation.strValue === 'autorotate') {
29941 theta = ele.isEdge() ? textAngle : 0;
29942 } else if (rotation.strValue === 'none') {
29943 theta = 0;
29944 } else {
29945 theta = rotation.pfValue;
29946 }
29947
29948 return theta;
29949 };
29950
29951 CRp$4.drawText = function (context, ele, prefix) {
29952 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29953 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29954 var _p = ele._private;
29955 var rscratch = _p.rscratch;
29956 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29957
29958 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29959 return;
29960 } // use 'main' as an alias for the main label (i.e. null prefix)
29961
29962
29963 if (prefix === 'main') {
29964 prefix = null;
29965 }
29966
29967 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29968 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29969 var orgTextX, orgTextY; // used for rotation
29970
29971 var text = this.getLabelText(ele, prefix);
29972
29973 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29974 this.setupTextStyle(context, ele, useEleOpacity);
29975 var pdash = prefix ? prefix + '-' : '';
29976 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29977 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29978 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29979 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29980 var isEdge = ele.isEdge();
29981 var halign = ele.pstyle('text-halign').value;
29982 var valign = ele.pstyle('text-valign').value;
29983
29984 if (isEdge) {
29985 halign = 'center';
29986 valign = 'center';
29987 }
29988
29989 textX += marginX;
29990 textY += marginY;
29991 var theta;
29992
29993 if (!applyRotation) {
29994 theta = 0;
29995 } else {
29996 theta = this.getTextAngle(ele, prefix);
29997 }
29998
29999 if (theta !== 0) {
30000 orgTextX = textX;
30001 orgTextY = textY;
30002 context.translate(orgTextX, orgTextY);
30003 context.rotate(theta);
30004 textX = 0;
30005 textY = 0;
30006 }
30007
30008 switch (valign) {
30009 case 'top':
30010 break;
30011
30012 case 'center':
30013 textY += textH / 2;
30014 break;
30015
30016 case 'bottom':
30017 textY += textH;
30018 break;
30019 }
30020
30021 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
30022 var borderOpacity = ele.pstyle('text-border-opacity').value;
30023 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
30024 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
30025
30026 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
30027 var bgX = textX - backgroundPadding;
30028
30029 switch (halign) {
30030 case 'left':
30031 bgX -= textW;
30032 break;
30033
30034 case 'center':
30035 bgX -= textW / 2;
30036 break;
30037 }
30038
30039 var bgY = textY - textH - backgroundPadding;
30040 var bgW = textW + 2 * backgroundPadding;
30041 var bgH = textH + 2 * backgroundPadding;
30042
30043 if (backgroundOpacity > 0) {
30044 var textFill = context.fillStyle;
30045 var textBackgroundColor = ele.pstyle('text-background-color').value;
30046 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
30047 var styleShape = ele.pstyle('text-background-shape').strValue;
30048
30049 if (styleShape.indexOf('round') === 0) {
30050 roundRect(context, bgX, bgY, bgW, bgH, 2);
30051 } else {
30052 context.fillRect(bgX, bgY, bgW, bgH);
30053 }
30054
30055 context.fillStyle = textFill;
30056 }
30057
30058 if (textBorderWidth > 0 && borderOpacity > 0) {
30059 var textStroke = context.strokeStyle;
30060 var textLineWidth = context.lineWidth;
30061 var textBorderColor = ele.pstyle('text-border-color').value;
30062 var textBorderStyle = ele.pstyle('text-border-style').value;
30063 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
30064 context.lineWidth = textBorderWidth;
30065
30066 if (context.setLineDash) {
30067 // for very outofdate browsers
30068 switch (textBorderStyle) {
30069 case 'dotted':
30070 context.setLineDash([1, 1]);
30071 break;
30072
30073 case 'dashed':
30074 context.setLineDash([4, 2]);
30075 break;
30076
30077 case 'double':
30078 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
30079
30080 context.setLineDash([]);
30081 break;
30082
30083 case 'solid':
30084 context.setLineDash([]);
30085 break;
30086 }
30087 }
30088
30089 context.strokeRect(bgX, bgY, bgW, bgH);
30090
30091 if (textBorderStyle === 'double') {
30092 var whiteWidth = textBorderWidth / 2;
30093 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
30094 }
30095
30096 if (context.setLineDash) {
30097 // for very outofdate browsers
30098 context.setLineDash([]);
30099 }
30100
30101 context.lineWidth = textLineWidth;
30102 context.strokeStyle = textStroke;
30103 }
30104 }
30105
30106 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
30107
30108 if (lineWidth > 0) {
30109 context.lineWidth = lineWidth;
30110 }
30111
30112 if (ele.pstyle('text-wrap').value === 'wrap') {
30113 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
30114 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
30115 var halfTextW = textW / 2;
30116 var justification = this.getLabelJustification(ele);
30117
30118 if (justification === 'auto') ; else if (halign === 'left') {
30119 // auto justification : right
30120 if (justification === 'left') {
30121 textX += -textW;
30122 } else if (justification === 'center') {
30123 textX += -halfTextW;
30124 } // else same as auto
30125
30126 } else if (halign === 'center') {
30127 // auto justfication : center
30128 if (justification === 'left') {
30129 textX += -halfTextW;
30130 } else if (justification === 'right') {
30131 textX += halfTextW;
30132 } // else same as auto
30133
30134 } else if (halign === 'right') {
30135 // auto justification : left
30136 if (justification === 'center') {
30137 textX += halfTextW;
30138 } else if (justification === 'right') {
30139 textX += textW;
30140 } // else same as auto
30141
30142 }
30143
30144 switch (valign) {
30145 case 'top':
30146 textY -= (lines.length - 1) * lineHeight;
30147 break;
30148
30149 case 'center':
30150 case 'bottom':
30151 textY -= (lines.length - 1) * lineHeight;
30152 break;
30153 }
30154
30155 for (var l = 0; l < lines.length; l++) {
30156 if (lineWidth > 0) {
30157 context.strokeText(lines[l], textX, textY);
30158 }
30159
30160 context.fillText(lines[l], textX, textY);
30161 textY += lineHeight;
30162 }
30163 } else {
30164 if (lineWidth > 0) {
30165 context.strokeText(text, textX, textY);
30166 }
30167
30168 context.fillText(text, textX, textY);
30169 }
30170
30171 if (theta !== 0) {
30172 context.rotate(-theta);
30173 context.translate(-orgTextX, -orgTextY);
30174 }
30175 }
30176 };
30177
30178 /* global Path2D */
30179 var CRp$5 = {};
30180
30181 CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
30182 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30183 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30184 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
30185 var r = this;
30186 var nodeWidth, nodeHeight;
30187 var _p = node._private;
30188 var rs = _p.rscratch;
30189 var pos = node.position();
30190
30191 if (!number(pos.x) || !number(pos.y)) {
30192 return; // can't draw node with undefined position
30193 }
30194
30195 if (shouldDrawOpacity && !node.visible()) {
30196 return;
30197 }
30198
30199 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
30200 var usePaths = r.usePaths();
30201 var path;
30202 var pathCacheHit = false;
30203 var padding = node.padding();
30204 nodeWidth = node.width() + 2 * padding;
30205 nodeHeight = node.height() + 2 * padding; //
30206 // setup shift
30207
30208 var bb;
30209
30210 if (shiftToOriginWithBb) {
30211 bb = shiftToOriginWithBb;
30212 context.translate(-bb.x1, -bb.y1);
30213 } //
30214 // load bg image
30215
30216
30217 var bgImgProp = node.pstyle('background-image');
30218 var urls = bgImgProp.value;
30219 var urlDefined = new Array(urls.length);
30220 var image = new Array(urls.length);
30221 var numImages = 0;
30222
30223 for (var i = 0; i < urls.length; i++) {
30224 var url = urls[i];
30225 var defd = urlDefined[i] = url != null && url !== 'none';
30226
30227 if (defd) {
30228 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
30229 numImages++; // get image, and if not loaded then ask to redraw when later loaded
30230
30231 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
30232 _p.backgroundTimestamp = Date.now();
30233 node.emitAndNotify('background');
30234 });
30235 }
30236 } //
30237 // setup styles
30238
30239
30240 var darkness = node.pstyle('background-blacken').value;
30241 var borderWidth = node.pstyle('border-width').pfValue;
30242 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
30243 var borderColor = node.pstyle('border-color').value;
30244 var borderStyle = node.pstyle('border-style').value;
30245 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
30246 context.lineJoin = 'miter'; // so borders are square with the node shape
30247
30248 var setupShapeColor = function setupShapeColor() {
30249 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
30250 r.eleFillStyle(context, node, bgOpy);
30251 };
30252
30253 var setupBorderColor = function setupBorderColor() {
30254 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
30255 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
30256 }; //
30257 // setup shape
30258
30259
30260 var styleShape = node.pstyle('shape').strValue;
30261 var shapePts = node.pstyle('shape-polygon-points').pfValue;
30262
30263 if (usePaths) {
30264 context.translate(pos.x, pos.y);
30265 var pathCache = r.nodePathCache = r.nodePathCache || [];
30266 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
30267 var cachedPath = pathCache[key];
30268
30269 if (cachedPath != null) {
30270 path = cachedPath;
30271 pathCacheHit = true;
30272 rs.pathCache = path;
30273 } else {
30274 path = new Path2D();
30275 pathCache[key] = rs.pathCache = path;
30276 }
30277 }
30278
30279 var drawShape = function drawShape() {
30280 if (!pathCacheHit) {
30281 var npos = pos;
30282
30283 if (usePaths) {
30284 npos = {
30285 x: 0,
30286 y: 0
30287 };
30288 }
30289
30290 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
30291 }
30292
30293 if (usePaths) {
30294 context.fill(path);
30295 } else {
30296 context.fill();
30297 }
30298 };
30299
30300 var drawImages = function drawImages() {
30301 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30302 var prevBging = _p.backgrounding;
30303 var totalCompleted = 0;
30304
30305 for (var _i = 0; _i < image.length; _i++) {
30306 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
30307 totalCompleted++;
30308 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
30309 }
30310 }
30311
30312 _p.backgrounding = !(totalCompleted === numImages);
30313
30314 if (prevBging !== _p.backgrounding) {
30315 // update style b/c :backgrounding state changed
30316 node.updateStyle(false);
30317 }
30318 };
30319
30320 var drawPie = function drawPie() {
30321 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
30322 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
30323
30324 if (r.hasPie(node)) {
30325 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
30326
30327 if (redrawShape) {
30328 if (!usePaths) {
30329 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
30330 }
30331 }
30332 }
30333 };
30334
30335 var darken = function darken() {
30336 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30337 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
30338 var c = darkness > 0 ? 0 : 255;
30339
30340 if (darkness !== 0) {
30341 r.colorFillStyle(context, c, c, c, opacity);
30342
30343 if (usePaths) {
30344 context.fill(path);
30345 } else {
30346 context.fill();
30347 }
30348 }
30349 };
30350
30351 var drawBorder = function drawBorder() {
30352 if (borderWidth > 0) {
30353 context.lineWidth = borderWidth;
30354 context.lineCap = 'butt';
30355
30356 if (context.setLineDash) {
30357 // for very outofdate browsers
30358 switch (borderStyle) {
30359 case 'dotted':
30360 context.setLineDash([1, 1]);
30361 break;
30362
30363 case 'dashed':
30364 context.setLineDash([4, 2]);
30365 break;
30366
30367 case 'solid':
30368 case 'double':
30369 context.setLineDash([]);
30370 break;
30371 }
30372 }
30373
30374 if (usePaths) {
30375 context.stroke(path);
30376 } else {
30377 context.stroke();
30378 }
30379
30380 if (borderStyle === 'double') {
30381 context.lineWidth = borderWidth / 3;
30382 var gco = context.globalCompositeOperation;
30383 context.globalCompositeOperation = 'destination-out';
30384
30385 if (usePaths) {
30386 context.stroke(path);
30387 } else {
30388 context.stroke();
30389 }
30390
30391 context.globalCompositeOperation = gco;
30392 } // reset in case we changed the border style
30393
30394
30395 if (context.setLineDash) {
30396 // for very outofdate browsers
30397 context.setLineDash([]);
30398 }
30399 }
30400 };
30401
30402 var drawOverlay = function drawOverlay() {
30403 if (shouldDrawOverlay) {
30404 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30405 }
30406 };
30407
30408 var drawText = function drawText() {
30409 r.drawElementText(context, node, null, drawLabel);
30410 };
30411
30412 var ghost = node.pstyle('ghost').value === 'yes';
30413
30414 if (ghost) {
30415 var gx = node.pstyle('ghost-offset-x').pfValue;
30416 var gy = node.pstyle('ghost-offset-y').pfValue;
30417 var ghostOpacity = node.pstyle('ghost-opacity').value;
30418 var effGhostOpacity = ghostOpacity * eleOpacity;
30419 context.translate(gx, gy);
30420 setupShapeColor(ghostOpacity * bgOpacity);
30421 drawShape();
30422 drawImages(effGhostOpacity);
30423 drawPie(darkness !== 0 || borderWidth !== 0);
30424 darken(effGhostOpacity);
30425 setupBorderColor(ghostOpacity * borderOpacity);
30426 drawBorder();
30427 context.translate(-gx, -gy);
30428 }
30429
30430 setupShapeColor();
30431 drawShape();
30432 drawImages();
30433 drawPie(darkness !== 0 || borderWidth !== 0);
30434 darken();
30435 setupBorderColor();
30436 drawBorder();
30437
30438 if (usePaths) {
30439 context.translate(-pos.x, -pos.y);
30440 }
30441
30442 drawText();
30443 drawOverlay(); //
30444 // clean up shift
30445
30446 if (shiftToOriginWithBb) {
30447 context.translate(bb.x1, bb.y1);
30448 }
30449 };
30450
30451 CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
30452 var r = this;
30453
30454 if (!node.visible()) {
30455 return;
30456 }
30457
30458 var overlayPadding = node.pstyle('overlay-padding').pfValue;
30459 var overlayOpacity = node.pstyle('overlay-opacity').value;
30460 var overlayColor = node.pstyle('overlay-color').value;
30461
30462 if (overlayOpacity > 0) {
30463 pos = pos || node.position();
30464
30465 if (nodeWidth == null || nodeHeight == null) {
30466 var padding = node.padding();
30467 nodeWidth = node.width() + 2 * padding;
30468 nodeHeight = node.height() + 2 * padding;
30469 }
30470
30471 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
30472 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
30473 context.fill();
30474 }
30475 }; // does the node have at least one pie piece?
30476
30477
30478 CRp$5.hasPie = function (node) {
30479 node = node[0]; // ensure ele ref
30480
30481 return node._private.hasPie;
30482 };
30483
30484 CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30485 node = node[0]; // ensure ele ref
30486
30487 pos = pos || node.position();
30488 var cyStyle = node.cy().style();
30489 var pieSize = node.pstyle('pie-size');
30490 var x = pos.x;
30491 var y = pos.y;
30492 var nodeW = node.width();
30493 var nodeH = node.height();
30494 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30495
30496 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30497
30498 var usePaths = this.usePaths();
30499
30500 if (usePaths) {
30501 x = 0;
30502 y = 0;
30503 }
30504
30505 if (pieSize.units === '%') {
30506 radius = radius * pieSize.pfValue;
30507 } else if (pieSize.pfValue !== undefined) {
30508 radius = pieSize.pfValue / 2;
30509 }
30510
30511 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30512 // 1..N
30513 var size = node.pstyle('pie-' + i + '-background-size').value;
30514 var color = node.pstyle('pie-' + i + '-background-color').value;
30515 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30516 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30517 // percent can't push beyond 1
30518
30519 if (percent + lastPercent > 1) {
30520 percent = 1 - lastPercent;
30521 }
30522
30523 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30524
30525 var angleDelta = 2 * Math.PI * percent;
30526 var angleEnd = angleStart + angleDelta; // ignore if
30527 // - zero size
30528 // - we're already beyond the full circle
30529 // - adding the current slice would go beyond the full circle
30530
30531 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30532 continue;
30533 }
30534
30535 context.beginPath();
30536 context.moveTo(x, y);
30537 context.arc(x, y, radius, angleStart, angleEnd);
30538 context.closePath();
30539 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30540 context.fill();
30541 lastPercent += percent;
30542 }
30543 };
30544
30545 var CRp$6 = {};
30546 var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30547
30548 CRp$6.getPixelRatio = function () {
30549 var context = this.data.contexts[0];
30550
30551 if (this.forcedPixelRatio != null) {
30552 return this.forcedPixelRatio;
30553 }
30554
30555 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30556 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30557 };
30558
30559 CRp$6.paintCache = function (context) {
30560 var caches = this.paintCaches = this.paintCaches || [];
30561 var needToCreateCache = true;
30562 var cache;
30563
30564 for (var i = 0; i < caches.length; i++) {
30565 cache = caches[i];
30566
30567 if (cache.context === context) {
30568 needToCreateCache = false;
30569 break;
30570 }
30571 }
30572
30573 if (needToCreateCache) {
30574 cache = {
30575 context: context
30576 };
30577 caches.push(cache);
30578 }
30579
30580 return cache;
30581 };
30582
30583 CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30584 var gradientStyle;
30585 var usePaths = this.usePaths();
30586 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30587 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30588
30589 if (fill === 'radial-gradient') {
30590 if (ele.isEdge()) {
30591 var start = ele.sourceEndpoint(),
30592 end = ele.targetEndpoint(),
30593 mid = ele.midpoint();
30594 var d1 = dist(start, mid);
30595 var d2 = dist(end, mid);
30596 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30597 } else {
30598 var pos = usePaths ? {
30599 x: 0,
30600 y: 0
30601 } : ele.position(),
30602 width = ele.paddedWidth(),
30603 height = ele.paddedHeight();
30604 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30605 }
30606 } else {
30607 if (ele.isEdge()) {
30608 var _start = ele.sourceEndpoint(),
30609 _end = ele.targetEndpoint();
30610
30611 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30612 } else {
30613 var _pos = usePaths ? {
30614 x: 0,
30615 y: 0
30616 } : ele.position(),
30617 _width = ele.paddedWidth(),
30618 _height = ele.paddedHeight(),
30619 halfWidth = _width / 2,
30620 halfHeight = _height / 2;
30621
30622 var direction = ele.pstyle('background-gradient-direction').value;
30623
30624 switch (direction) {
30625 case 'to-bottom':
30626 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30627 break;
30628
30629 case 'to-top':
30630 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30631 break;
30632
30633 case 'to-left':
30634 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30635 break;
30636
30637 case 'to-right':
30638 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30639 break;
30640
30641 case 'to-bottom-right':
30642 case 'to-right-bottom':
30643 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30644 break;
30645
30646 case 'to-top-right':
30647 case 'to-right-top':
30648 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30649 break;
30650
30651 case 'to-bottom-left':
30652 case 'to-left-bottom':
30653 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30654 break;
30655
30656 case 'to-top-left':
30657 case 'to-left-top':
30658 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30659 break;
30660 }
30661 }
30662 }
30663
30664 if (!gradientStyle) return null; // invalid gradient style
30665
30666 var hasPositions = positions.length === colors.length;
30667 var length = colors.length;
30668
30669 for (var i = 0; i < length; i++) {
30670 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30671 }
30672
30673 return gradientStyle;
30674 };
30675
30676 CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30677 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30678 if (!gradientStyle) return null; // error
30679
30680 context.fillStyle = gradientStyle;
30681 };
30682
30683 CRp$6.colorFillStyle = function (context, r, g, b, a) {
30684 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30685 // var cache = this.paintCache(context);
30686 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30687 // if( cache.fillStyle !== fillStyle ){
30688 // context.fillStyle = cache.fillStyle = fillStyle;
30689 // }
30690 };
30691
30692 CRp$6.eleFillStyle = function (context, ele, opacity) {
30693 var backgroundFill = ele.pstyle('background-fill').value;
30694
30695 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30696 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30697 } else {
30698 var backgroundColor = ele.pstyle('background-color').value;
30699 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30700 }
30701 };
30702
30703 CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30704 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30705 if (!gradientStyle) return null; // error
30706
30707 context.strokeStyle = gradientStyle;
30708 };
30709
30710 CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30711 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30712 // var cache = this.paintCache(context);
30713 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30714 // if( cache.strokeStyle !== strokeStyle ){
30715 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30716 // }
30717 };
30718
30719 CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30720 var lineFill = ele.pstyle('line-fill').value;
30721
30722 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30723 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30724 } else {
30725 var lineColor = ele.pstyle('line-color').value;
30726 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30727 }
30728 }; // Resize canvas
30729
30730
30731 CRp$6.matchCanvasSize = function (container) {
30732 var r = this;
30733 var data = r.data;
30734 var bb = r.findContainerClientCoords();
30735 var width = bb[2];
30736 var height = bb[3];
30737 var pixelRatio = r.getPixelRatio();
30738 var mbPxRatio = r.motionBlurPxRatio;
30739
30740 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30741 pixelRatio = mbPxRatio;
30742 }
30743
30744 var canvasWidth = width * pixelRatio;
30745 var canvasHeight = height * pixelRatio;
30746 var canvas;
30747
30748 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30749 return; // save cycles if same
30750 }
30751
30752 r.fontCaches = null; // resizing resets the style
30753
30754 var canvasContainer = data.canvasContainer;
30755 canvasContainer.style.width = width + 'px';
30756 canvasContainer.style.height = height + 'px';
30757
30758 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30759 canvas = data.canvases[i];
30760 canvas.width = canvasWidth;
30761 canvas.height = canvasHeight;
30762 canvas.style.width = width + 'px';
30763 canvas.style.height = height + 'px';
30764 }
30765
30766 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30767 canvas = data.bufferCanvases[i];
30768 canvas.width = canvasWidth;
30769 canvas.height = canvasHeight;
30770 canvas.style.width = width + 'px';
30771 canvas.style.height = height + 'px';
30772 }
30773
30774 r.textureMult = 1;
30775
30776 if (pixelRatio <= 1) {
30777 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30778 r.textureMult = 2;
30779 canvas.width = canvasWidth * r.textureMult;
30780 canvas.height = canvasHeight * r.textureMult;
30781 }
30782
30783 r.canvasWidth = canvasWidth;
30784 r.canvasHeight = canvasHeight;
30785 };
30786
30787 CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30788 this.render({
30789 forcedContext: cxt,
30790 forcedZoom: zoom,
30791 forcedPan: pan,
30792 drawAllLayers: true,
30793 forcedPxRatio: pxRatio
30794 });
30795 };
30796
30797 CRp$6.render = function (options) {
30798 options = options || staticEmptyObject();
30799 var forcedContext = options.forcedContext;
30800 var drawAllLayers = options.drawAllLayers;
30801 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30802 var forcedZoom = options.forcedZoom;
30803 var forcedPan = options.forcedPan;
30804 var r = this;
30805 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30806 var cy = r.cy;
30807 var data = r.data;
30808 var needDraw = data.canvasNeedsRedraw;
30809 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30810 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30811 var mbPxRatio = r.motionBlurPxRatio;
30812 var hasCompoundNodes = cy.hasCompoundNodes();
30813 var inNodeDragGesture = r.hoverData.draggingEles;
30814 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30815 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30816 var motionBlurFadeEffect = motionBlur;
30817
30818 if (!forcedContext) {
30819 if (r.prevPxRatio !== pixelRatio) {
30820 r.invalidateContainerClientCoordsCache();
30821 r.matchCanvasSize(r.container);
30822 r.redrawHint('eles', true);
30823 r.redrawHint('drag', true);
30824 }
30825
30826 r.prevPxRatio = pixelRatio;
30827 }
30828
30829 if (!forcedContext && r.motionBlurTimeout) {
30830 clearTimeout(r.motionBlurTimeout);
30831 }
30832
30833 if (motionBlur) {
30834 if (r.mbFrames == null) {
30835 r.mbFrames = 0;
30836 }
30837
30838 r.mbFrames++;
30839
30840 if (r.mbFrames < 3) {
30841 // need several frames before even high quality motionblur
30842 motionBlurFadeEffect = false;
30843 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30844
30845
30846 if (r.mbFrames > r.minMbLowQualFrames) {
30847 //r.fullQualityMb = false;
30848 r.motionBlurPxRatio = r.mbPxRBlurry;
30849 }
30850 }
30851
30852 if (r.clearingMotionBlur) {
30853 r.motionBlurPxRatio = 1;
30854 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30855 // because a rogue async texture frame would clear needDraw
30856
30857
30858 if (r.textureDrawLastFrame && !textureDraw) {
30859 needDraw[r.NODE] = true;
30860 needDraw[r.SELECT_BOX] = true;
30861 }
30862
30863 var style = cy.style();
30864 var zoom = cy.zoom();
30865 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30866 var pan = cy.pan();
30867 var effectivePan = {
30868 x: pan.x,
30869 y: pan.y
30870 };
30871 var vp = {
30872 zoom: zoom,
30873 pan: {
30874 x: pan.x,
30875 y: pan.y
30876 }
30877 };
30878 var prevVp = r.prevViewport;
30879 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)
30880
30881 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30882 r.motionBlurPxRatio = 1;
30883 }
30884
30885 if (forcedPan) {
30886 effectivePan = forcedPan;
30887 } // apply pixel ratio
30888
30889
30890 effectiveZoom *= pixelRatio;
30891 effectivePan.x *= pixelRatio;
30892 effectivePan.y *= pixelRatio;
30893 var eles = r.getCachedZSortedEles();
30894
30895 function mbclear(context, x, y, w, h) {
30896 var gco = context.globalCompositeOperation;
30897 context.globalCompositeOperation = 'destination-out';
30898 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30899 context.fillRect(x, y, w, h);
30900 context.globalCompositeOperation = gco;
30901 }
30902
30903 function setContextTransform(context, clear) {
30904 var ePan, eZoom, w, h;
30905
30906 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30907 ePan = {
30908 x: pan.x * mbPxRatio,
30909 y: pan.y * mbPxRatio
30910 };
30911 eZoom = zoom * mbPxRatio;
30912 w = r.canvasWidth * mbPxRatio;
30913 h = r.canvasHeight * mbPxRatio;
30914 } else {
30915 ePan = effectivePan;
30916 eZoom = effectiveZoom;
30917 w = r.canvasWidth;
30918 h = r.canvasHeight;
30919 }
30920
30921 context.setTransform(1, 0, 0, 1, 0, 0);
30922
30923 if (clear === 'motionBlur') {
30924 mbclear(context, 0, 0, w, h);
30925 } else if (!forcedContext && (clear === undefined || clear)) {
30926 context.clearRect(0, 0, w, h);
30927 }
30928
30929 if (!drawAllLayers) {
30930 context.translate(ePan.x, ePan.y);
30931 context.scale(eZoom, eZoom);
30932 }
30933
30934 if (forcedPan) {
30935 context.translate(forcedPan.x, forcedPan.y);
30936 }
30937
30938 if (forcedZoom) {
30939 context.scale(forcedZoom, forcedZoom);
30940 }
30941 }
30942
30943 if (!textureDraw) {
30944 r.textureDrawLastFrame = false;
30945 }
30946
30947 if (textureDraw) {
30948 r.textureDrawLastFrame = true;
30949
30950 if (!r.textureCache) {
30951 r.textureCache = {};
30952 r.textureCache.bb = cy.mutableElements().boundingBox();
30953 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30954 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30955 cxt.setTransform(1, 0, 0, 1, 0, 0);
30956 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30957 r.render({
30958 forcedContext: cxt,
30959 drawOnlyNodeLayer: true,
30960 forcedPxRatio: pixelRatio * r.textureMult
30961 });
30962 var vp = r.textureCache.viewport = {
30963 zoom: cy.zoom(),
30964 pan: cy.pan(),
30965 width: r.canvasWidth,
30966 height: r.canvasHeight
30967 };
30968 vp.mpan = {
30969 x: (0 - vp.pan.x) / vp.zoom,
30970 y: (0 - vp.pan.y) / vp.zoom
30971 };
30972 }
30973
30974 needDraw[r.DRAG] = false;
30975 needDraw[r.NODE] = false;
30976 var context = data.contexts[r.NODE];
30977 var texture = r.textureCache.texture;
30978 var vp = r.textureCache.viewport;
30979 context.setTransform(1, 0, 0, 1, 0, 0);
30980
30981 if (motionBlur) {
30982 mbclear(context, 0, 0, vp.width, vp.height);
30983 } else {
30984 context.clearRect(0, 0, vp.width, vp.height);
30985 }
30986
30987 var outsideBgColor = style.core('outside-texture-bg-color').value;
30988 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30989 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30990 context.fillRect(0, 0, vp.width, vp.height);
30991 var zoom = cy.zoom();
30992 setContextTransform(context, false);
30993 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30994 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30995 } else if (r.textureOnViewport && !forcedContext) {
30996 // clear the cache since we don't need it
30997 r.textureCache = null;
30998 }
30999
31000 var extent = cy.extent();
31001 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
31002 var hideEdges = r.hideEdgesOnViewport && vpManip;
31003 var needMbClear = [];
31004 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
31005
31006 if (needMbClear[r.NODE]) {
31007 r.clearedForMotionBlur[r.NODE] = true;
31008 }
31009
31010 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
31011
31012 if (needMbClear[r.DRAG]) {
31013 r.clearedForMotionBlur[r.DRAG] = true;
31014 }
31015
31016 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
31017 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
31018 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
31019 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
31020 setContextTransform(context, clear);
31021
31022 if (hideEdges) {
31023 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
31024 } else {
31025 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
31026 }
31027
31028 if (r.debug) {
31029 r.drawDebugPoints(context, eles.nondrag);
31030 }
31031
31032 if (!drawAllLayers && !motionBlur) {
31033 needDraw[r.NODE] = false;
31034 }
31035 }
31036
31037 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
31038 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
31039 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
31040 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
31041
31042 if (hideEdges) {
31043 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
31044 } else {
31045 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
31046 }
31047
31048 if (r.debug) {
31049 r.drawDebugPoints(context, eles.drag);
31050 }
31051
31052 if (!drawAllLayers && !motionBlur) {
31053 needDraw[r.DRAG] = false;
31054 }
31055 }
31056
31057 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
31058 var context = forcedContext || data.contexts[r.SELECT_BOX];
31059 setContextTransform(context);
31060
31061 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
31062 var zoom = r.cy.zoom();
31063 var borderWidth = style.core('selection-box-border-width').value / zoom;
31064 context.lineWidth = borderWidth;
31065 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 + ')';
31066 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31067
31068 if (borderWidth > 0) {
31069 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 + ')';
31070 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31071 }
31072 }
31073
31074 if (data.bgActivePosistion && !r.hoverData.selecting) {
31075 var zoom = r.cy.zoom();
31076 var pos = data.bgActivePosistion;
31077 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 + ')';
31078 context.beginPath();
31079 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
31080 context.fill();
31081 }
31082
31083 var timeToRender = r.lastRedrawTime;
31084
31085 if (r.showFps && timeToRender) {
31086 timeToRender = Math.round(timeToRender);
31087 var fps = Math.round(1000 / timeToRender);
31088 context.setTransform(1, 0, 0, 1, 0, 0);
31089 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
31090 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
31091 context.lineWidth = 1;
31092 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
31093 var maxFps = 60;
31094 context.strokeRect(0, 30, 250, 20);
31095 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
31096 }
31097
31098 if (!drawAllLayers) {
31099 needDraw[r.SELECT_BOX] = false;
31100 }
31101 } // motionblur: blit rendered blurry frames
31102
31103
31104 if (motionBlur && mbPxRatio !== 1) {
31105 var cxtNode = data.contexts[r.NODE];
31106 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
31107 var cxtDrag = data.contexts[r.DRAG];
31108 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
31109
31110 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
31111 cxt.setTransform(1, 0, 0, 1, 0, 0);
31112
31113 if (needClear || !motionBlurFadeEffect) {
31114 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
31115 } else {
31116 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
31117 }
31118
31119 var pxr = mbPxRatio;
31120 cxt.drawImage(txt, // img
31121 0, 0, // sx, sy
31122 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
31123 0, 0, // x, y
31124 r.canvasWidth, r.canvasHeight // w, h
31125 );
31126 };
31127
31128 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
31129 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
31130 needDraw[r.NODE] = false;
31131 }
31132
31133 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
31134 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
31135 needDraw[r.DRAG] = false;
31136 }
31137 }
31138
31139 r.prevViewport = vp;
31140
31141 if (r.clearingMotionBlur) {
31142 r.clearingMotionBlur = false;
31143 r.motionBlurCleared = true;
31144 r.motionBlur = true;
31145 }
31146
31147 if (motionBlur) {
31148 r.motionBlurTimeout = setTimeout(function () {
31149 r.motionBlurTimeout = null;
31150 r.clearedForMotionBlur[r.NODE] = false;
31151 r.clearedForMotionBlur[r.DRAG] = false;
31152 r.motionBlur = false;
31153 r.clearingMotionBlur = !textureDraw;
31154 r.mbFrames = 0;
31155 needDraw[r.NODE] = true;
31156 needDraw[r.DRAG] = true;
31157 r.redraw();
31158 }, motionBlurDelay);
31159 }
31160
31161 if (!forcedContext) {
31162 cy.emit('render');
31163 }
31164 };
31165
31166 var CRp$7 = {}; // @O Polygon drawing
31167
31168 CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
31169 var halfW = width / 2;
31170 var halfH = height / 2;
31171
31172 if (context.beginPath) {
31173 context.beginPath();
31174 }
31175
31176 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
31177
31178 for (var i = 1; i < points.length / 2; i++) {
31179 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
31180 }
31181
31182 context.closePath();
31183 };
31184
31185 CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
31186 var halfW = width / 2;
31187 var halfH = height / 2;
31188 var cornerRadius = getRoundPolygonRadius(width, height);
31189
31190 if (context.beginPath) {
31191 context.beginPath();
31192 }
31193
31194 for (var _i = 0; _i < points.length / 4; _i++) {
31195 var sourceUv = void 0,
31196 destUv = void 0;
31197
31198 if (_i === 0) {
31199 sourceUv = points.length - 2;
31200 } else {
31201 sourceUv = _i * 4 - 2;
31202 }
31203
31204 destUv = _i * 4 + 2;
31205 var px = x + halfW * points[_i * 4];
31206 var py = y + halfH * points[_i * 4 + 1];
31207 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
31208 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
31209 var cp0x = px - offset * points[sourceUv];
31210 var cp0y = py - offset * points[sourceUv + 1];
31211 var cp1x = px + offset * points[destUv];
31212 var cp1y = py + offset * points[destUv + 1];
31213
31214 if (_i === 0) {
31215 context.moveTo(cp0x, cp0y);
31216 } else {
31217 context.lineTo(cp0x, cp0y);
31218 }
31219
31220 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
31221 }
31222
31223 context.closePath();
31224 }; // Round rectangle drawing
31225
31226
31227 CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
31228 var halfWidth = width / 2;
31229 var halfHeight = height / 2;
31230 var cornerRadius = getRoundRectangleRadius(width, height);
31231
31232 if (context.beginPath) {
31233 context.beginPath();
31234 } // Start at top middle
31235
31236
31237 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
31238
31239 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
31240
31241 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
31242
31243 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
31244
31245 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
31246
31247 context.lineTo(x, y - halfHeight);
31248 context.closePath();
31249 };
31250
31251 CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
31252 var halfWidth = width / 2;
31253 var halfHeight = height / 2;
31254 var cornerRadius = getRoundRectangleRadius(width, height);
31255
31256 if (context.beginPath) {
31257 context.beginPath();
31258 } // Start at top middle
31259
31260
31261 context.moveTo(x, y - halfHeight);
31262 context.lineTo(x + halfWidth, y - halfHeight);
31263 context.lineTo(x + halfWidth, y);
31264 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
31265 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
31266 context.lineTo(x - halfWidth, y - halfHeight);
31267 context.lineTo(x, y - halfHeight);
31268 context.closePath();
31269 };
31270
31271 CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
31272 var halfWidth = width / 2;
31273 var halfHeight = height / 2;
31274 var cornerLength = getCutRectangleCornerLength();
31275
31276 if (context.beginPath) {
31277 context.beginPath();
31278 }
31279
31280 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
31281 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
31282 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
31283 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
31284 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
31285 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
31286 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
31287 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
31288 context.closePath();
31289 };
31290
31291 CRp$7.drawBarrelPath = function (context, x, y, width, height) {
31292 var halfWidth = width / 2;
31293 var halfHeight = height / 2;
31294 var xBegin = x - halfWidth;
31295 var xEnd = x + halfWidth;
31296 var yBegin = y - halfHeight;
31297 var yEnd = y + halfHeight;
31298 var barrelCurveConstants = getBarrelCurveConstants(width, height);
31299 var wOffset = barrelCurveConstants.widthOffset;
31300 var hOffset = barrelCurveConstants.heightOffset;
31301 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
31302
31303 if (context.beginPath) {
31304 context.beginPath();
31305 }
31306
31307 context.moveTo(xBegin, yBegin + hOffset);
31308 context.lineTo(xBegin, yEnd - hOffset);
31309 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
31310 context.lineTo(xEnd - wOffset, yEnd);
31311 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
31312 context.lineTo(xEnd, yBegin + hOffset);
31313 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
31314 context.lineTo(xBegin + wOffset, yBegin);
31315 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
31316 context.closePath();
31317 };
31318
31319 var sin0 = Math.sin(0);
31320 var cos0 = Math.cos(0);
31321 var sin = {};
31322 var cos = {};
31323 var ellipseStepSize = Math.PI / 40;
31324
31325 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31326 sin[i] = Math.sin(i);
31327 cos[i] = Math.cos(i);
31328 }
31329
31330 CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
31331 if (context.beginPath) {
31332 context.beginPath();
31333 }
31334
31335 if (context.ellipse) {
31336 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
31337 } else {
31338 var xPos, yPos;
31339 var rw = width / 2;
31340 var rh = height / 2;
31341
31342 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31343 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
31344 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
31345
31346 if (i === 0) {
31347 context.moveTo(xPos, yPos);
31348 } else {
31349 context.lineTo(xPos, yPos);
31350 }
31351 }
31352 }
31353
31354 context.closePath();
31355 };
31356
31357 /* global atob, ArrayBuffer, Uint8Array, Blob */
31358 var CRp$8 = {};
31359
31360 CRp$8.createBuffer = function (w, h) {
31361 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
31362
31363 buffer.width = w;
31364 buffer.height = h;
31365 return [buffer, buffer.getContext('2d')];
31366 };
31367
31368 CRp$8.bufferCanvasImage = function (options) {
31369 var cy = this.cy;
31370 var eles = cy.mutableElements();
31371 var bb = eles.boundingBox();
31372 var ctrRect = this.findContainerClientCoords();
31373 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31374 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31375 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
31376 var pxRatio = this.getPixelRatio();
31377 var scale = 1;
31378
31379 if (options.scale !== undefined) {
31380 width *= options.scale;
31381 height *= options.scale;
31382 scale = options.scale;
31383 } else if (specdMaxDims) {
31384 var maxScaleW = Infinity;
31385 var maxScaleH = Infinity;
31386
31387 if (number(options.maxWidth)) {
31388 maxScaleW = scale * options.maxWidth / width;
31389 }
31390
31391 if (number(options.maxHeight)) {
31392 maxScaleH = scale * options.maxHeight / height;
31393 }
31394
31395 scale = Math.min(maxScaleW, maxScaleH);
31396 width *= scale;
31397 height *= scale;
31398 }
31399
31400 if (!specdMaxDims) {
31401 width *= pxRatio;
31402 height *= pxRatio;
31403 scale *= pxRatio;
31404 }
31405
31406 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31407
31408 buffCanvas.width = width;
31409 buffCanvas.height = height;
31410 buffCanvas.style.width = width + 'px';
31411 buffCanvas.style.height = height + 'px';
31412 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31413
31414 if (width > 0 && height > 0) {
31415 buffCxt.clearRect(0, 0, width, height);
31416 buffCxt.globalCompositeOperation = 'source-over';
31417 var zsortedEles = this.getCachedZSortedEles();
31418
31419 if (options.full) {
31420 // draw the full bounds of the graph
31421 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31422 buffCxt.scale(scale, scale);
31423 this.drawElements(buffCxt, zsortedEles);
31424 buffCxt.scale(1 / scale, 1 / scale);
31425 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31426 } else {
31427 // draw the current view
31428 var pan = cy.pan();
31429 var translation = {
31430 x: pan.x * scale,
31431 y: pan.y * scale
31432 };
31433 scale *= cy.zoom();
31434 buffCxt.translate(translation.x, translation.y);
31435 buffCxt.scale(scale, scale);
31436 this.drawElements(buffCxt, zsortedEles);
31437 buffCxt.scale(1 / scale, 1 / scale);
31438 buffCxt.translate(-translation.x, -translation.y);
31439 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31440
31441
31442 if (options.bg) {
31443 buffCxt.globalCompositeOperation = 'destination-over';
31444 buffCxt.fillStyle = options.bg;
31445 buffCxt.rect(0, 0, width, height);
31446 buffCxt.fill();
31447 }
31448 }
31449
31450 return buffCanvas;
31451 };
31452
31453 function b64ToBlob(b64, mimeType) {
31454 var bytes = atob(b64);
31455 var buff = new ArrayBuffer(bytes.length);
31456 var buffUint8 = new Uint8Array(buff);
31457
31458 for (var i = 0; i < bytes.length; i++) {
31459 buffUint8[i] = bytes.charCodeAt(i);
31460 }
31461
31462 return new Blob([buff], {
31463 type: mimeType
31464 });
31465 }
31466
31467 function b64UriToB64(b64uri) {
31468 var i = b64uri.indexOf(',');
31469 return b64uri.substr(i + 1);
31470 }
31471
31472 function output(options, canvas, mimeType) {
31473 var getB64Uri = function getB64Uri() {
31474 return canvas.toDataURL(mimeType, options.quality);
31475 };
31476
31477 switch (options.output) {
31478 case 'blob-promise':
31479 return new Promise$1(function (resolve, reject) {
31480 try {
31481 canvas.toBlob(function (blob) {
31482 if (blob != null) {
31483 resolve(blob);
31484 } else {
31485 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31486 }
31487 }, mimeType, options.quality);
31488 } catch (err) {
31489 reject(err);
31490 }
31491 });
31492
31493 case 'blob':
31494 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31495
31496 case 'base64':
31497 return b64UriToB64(getB64Uri());
31498
31499 case 'base64uri':
31500 default:
31501 return getB64Uri();
31502 }
31503 }
31504
31505 CRp$8.png = function (options) {
31506 return output(options, this.bufferCanvasImage(options), 'image/png');
31507 };
31508
31509 CRp$8.jpg = function (options) {
31510 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31511 };
31512
31513 var CRp$9 = {};
31514
31515 CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31516 switch (name) {
31517 case 'ellipse':
31518 return this.drawEllipsePath(context, centerX, centerY, width, height);
31519
31520 case 'polygon':
31521 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31522
31523 case 'round-polygon':
31524 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31525
31526 case 'roundrectangle':
31527 case 'round-rectangle':
31528 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31529
31530 case 'cutrectangle':
31531 case 'cut-rectangle':
31532 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31533
31534 case 'bottomroundrectangle':
31535 case 'bottom-round-rectangle':
31536 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31537
31538 case 'barrel':
31539 return this.drawBarrelPath(context, centerX, centerY, width, height);
31540 }
31541 };
31542
31543 var CR = CanvasRenderer;
31544 var CRp$a = CanvasRenderer.prototype;
31545 CRp$a.CANVAS_LAYERS = 3; //
31546
31547 CRp$a.SELECT_BOX = 0;
31548 CRp$a.DRAG = 1;
31549 CRp$a.NODE = 2;
31550 CRp$a.BUFFER_COUNT = 3; //
31551
31552 CRp$a.TEXTURE_BUFFER = 0;
31553 CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
31554 CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
31555
31556 function CanvasRenderer(options) {
31557 var r = this;
31558 r.data = {
31559 canvases: new Array(CRp$a.CANVAS_LAYERS),
31560 contexts: new Array(CRp$a.CANVAS_LAYERS),
31561 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
31562 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
31563 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
31564 };
31565 var tapHlOffAttr = '-webkit-tap-highlight-color';
31566 var tapHlOffStyle = 'rgba(0,0,0,0)';
31567 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31568
31569 var containerStyle = r.data.canvasContainer.style;
31570 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31571 containerStyle.position = 'relative';
31572 containerStyle.zIndex = '0';
31573 containerStyle.overflow = 'hidden';
31574 var container = options.cy.container();
31575 container.appendChild(r.data.canvasContainer);
31576 container.style[tapHlOffAttr] = tapHlOffStyle;
31577 var styleMap = {
31578 '-webkit-user-select': 'none',
31579 '-moz-user-select': '-moz-none',
31580 'user-select': 'none',
31581 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31582 'outline-style': 'none'
31583 };
31584
31585 if (ms()) {
31586 styleMap['-ms-touch-action'] = 'none';
31587 styleMap['touch-action'] = 'none';
31588 }
31589
31590 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
31591 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31592
31593 r.data.contexts[i] = canvas.getContext('2d');
31594 Object.keys(styleMap).forEach(function (k) {
31595 canvas.style[k] = styleMap[k];
31596 });
31597 canvas.style.position = 'absolute';
31598 canvas.setAttribute('data-id', 'layer' + i);
31599 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
31600 r.data.canvasContainer.appendChild(canvas);
31601 r.data.canvasNeedsRedraw[i] = false;
31602 }
31603
31604 r.data.topCanvas = r.data.canvases[0];
31605 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
31606 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
31607 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
31608
31609 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
31610 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31611
31612 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31613 r.data.bufferCanvases[i].style.position = 'absolute';
31614 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31615 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31616 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31617 }
31618
31619 r.pathsEnabled = true;
31620 var emptyBb = makeBoundingBox();
31621
31622 var getBoxCenter = function getBoxCenter(bb) {
31623 return {
31624 x: (bb.x1 + bb.x2) / 2,
31625 y: (bb.y1 + bb.y2) / 2
31626 };
31627 };
31628
31629 var getCenterOffset = function getCenterOffset(bb) {
31630 return {
31631 x: -bb.w / 2,
31632 y: -bb.h / 2
31633 };
31634 };
31635
31636 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31637 var _p = ele[0]._private;
31638 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31639 return !same;
31640 };
31641
31642 var getStyleKey = function getStyleKey(ele) {
31643 return ele[0]._private.nodeKey;
31644 };
31645
31646 var getLabelKey = function getLabelKey(ele) {
31647 return ele[0]._private.labelStyleKey;
31648 };
31649
31650 var getSourceLabelKey = function getSourceLabelKey(ele) {
31651 return ele[0]._private.sourceLabelStyleKey;
31652 };
31653
31654 var getTargetLabelKey = function getTargetLabelKey(ele) {
31655 return ele[0]._private.targetLabelStyleKey;
31656 };
31657
31658 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31659 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31660 };
31661
31662 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31663 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31664 };
31665
31666 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31667 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31668 };
31669
31670 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31671 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31672 };
31673
31674 var getElementBox = function getElementBox(ele) {
31675 ele.boundingBox();
31676 return ele[0]._private.bodyBounds;
31677 };
31678
31679 var getLabelBox = function getLabelBox(ele) {
31680 ele.boundingBox();
31681 return ele[0]._private.labelBounds.main || emptyBb;
31682 };
31683
31684 var getSourceLabelBox = function getSourceLabelBox(ele) {
31685 ele.boundingBox();
31686 return ele[0]._private.labelBounds.source || emptyBb;
31687 };
31688
31689 var getTargetLabelBox = function getTargetLabelBox(ele) {
31690 ele.boundingBox();
31691 return ele[0]._private.labelBounds.target || emptyBb;
31692 };
31693
31694 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31695 return scaledLabelShown;
31696 };
31697
31698 var getElementRotationPoint = function getElementRotationPoint(ele) {
31699 return getBoxCenter(getElementBox(ele));
31700 };
31701
31702 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31703 var pre = prefix ? prefix + '-' : '';
31704 return {
31705 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31706 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31707 };
31708 };
31709
31710 var getRsPt = function getRsPt(ele, x, y) {
31711 var rs = ele[0]._private.rscratch;
31712 return {
31713 x: rs[x],
31714 y: rs[y]
31715 };
31716 };
31717
31718 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31719 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31720 };
31721
31722 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31723 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31724 };
31725
31726 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31727 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31728 };
31729
31730 var getElementRotationOffset = function getElementRotationOffset(ele) {
31731 return getCenterOffset(getElementBox(ele));
31732 };
31733
31734 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31735 return getCenterOffset(getSourceLabelBox(ele));
31736 };
31737
31738 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31739 return getCenterOffset(getTargetLabelBox(ele));
31740 };
31741
31742 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31743 var bb = getLabelBox(ele);
31744 var p = getCenterOffset(getLabelBox(ele));
31745
31746 if (ele.isNode()) {
31747 switch (ele.pstyle('text-halign').value) {
31748 case 'left':
31749 p.x = -bb.w;
31750 break;
31751
31752 case 'right':
31753 p.x = 0;
31754 break;
31755 }
31756
31757 switch (ele.pstyle('text-valign').value) {
31758 case 'top':
31759 p.y = -bb.h;
31760 break;
31761
31762 case 'bottom':
31763 p.y = 0;
31764 break;
31765 }
31766 }
31767
31768 return p;
31769 };
31770
31771 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31772 getKey: getStyleKey,
31773 doesEleInvalidateKey: backgroundTimestampHasChanged,
31774 drawElement: drawElement,
31775 getBoundingBox: getElementBox,
31776 getRotationPoint: getElementRotationPoint,
31777 getRotationOffset: getElementRotationOffset,
31778 allowEdgeTxrCaching: false,
31779 allowParentTxrCaching: false
31780 });
31781 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31782 getKey: getLabelKey,
31783 drawElement: drawLabel,
31784 getBoundingBox: getLabelBox,
31785 getRotationPoint: getLabelRotationPoint,
31786 getRotationOffset: getLabelRotationOffset,
31787 isVisible: isLabelVisibleAtScale
31788 });
31789 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31790 getKey: getSourceLabelKey,
31791 drawElement: drawSourceLabel,
31792 getBoundingBox: getSourceLabelBox,
31793 getRotationPoint: getSourceLabelRotationPoint,
31794 getRotationOffset: getSourceLabelRotationOffset,
31795 isVisible: isLabelVisibleAtScale
31796 });
31797 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31798 getKey: getTargetLabelKey,
31799 drawElement: drawTargetLabel,
31800 getBoundingBox: getTargetLabelBox,
31801 getRotationPoint: getTargetLabelRotationPoint,
31802 getRotationOffset: getTargetLabelRotationOffset,
31803 isVisible: isLabelVisibleAtScale
31804 });
31805 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31806 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31807 // each cache should check for sub-key diff to see that the update affects that cache particularly
31808 eleTxrCache.invalidateElements(eles);
31809 lblTxrCache.invalidateElements(eles);
31810 slbTxrCache.invalidateElements(eles);
31811 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31812
31813 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31814
31815 for (var _i = 0; _i < eles.length; _i++) {
31816 var _p = eles[_i]._private;
31817 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31818 }
31819 });
31820
31821 var refineInLayers = function refineInLayers(reqs) {
31822 for (var i = 0; i < reqs.length; i++) {
31823 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31824 }
31825 };
31826
31827 eleTxrCache.onDequeue(refineInLayers);
31828 lblTxrCache.onDequeue(refineInLayers);
31829 slbTxrCache.onDequeue(refineInLayers);
31830 tlbTxrCache.onDequeue(refineInLayers);
31831 }
31832
31833 CRp$a.redrawHint = function (group, bool) {
31834 var r = this;
31835
31836 switch (group) {
31837 case 'eles':
31838 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31839 break;
31840
31841 case 'drag':
31842 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31843 break;
31844
31845 case 'select':
31846 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31847 break;
31848 }
31849 }; // whether to use Path2D caching for drawing
31850
31851
31852 var pathsImpld = typeof Path2D !== 'undefined';
31853
31854 CRp$a.path2dEnabled = function (on) {
31855 if (on === undefined) {
31856 return this.pathsEnabled;
31857 }
31858
31859 this.pathsEnabled = on ? true : false;
31860 };
31861
31862 CRp$a.usePaths = function () {
31863 return pathsImpld && this.pathsEnabled;
31864 };
31865
31866 CRp$a.setImgSmoothing = function (context, bool) {
31867 if (context.imageSmoothingEnabled != null) {
31868 context.imageSmoothingEnabled = bool;
31869 } else {
31870 context.webkitImageSmoothingEnabled = bool;
31871 context.mozImageSmoothingEnabled = bool;
31872 context.msImageSmoothingEnabled = bool;
31873 }
31874 };
31875
31876 CRp$a.getImgSmoothing = function (context) {
31877 if (context.imageSmoothingEnabled != null) {
31878 return context.imageSmoothingEnabled;
31879 } else {
31880 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31881 }
31882 };
31883
31884 CRp$a.makeOffscreenCanvas = function (width, height) {
31885 var canvas;
31886
31887 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31888 canvas = new OffscreenCanvas(width, height);
31889 } else {
31890 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31891
31892 canvas.width = width;
31893 canvas.height = height;
31894 }
31895
31896 return canvas;
31897 };
31898
31899 [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31900 extend(CRp$a, props);
31901 });
31902
31903 var renderer = [{
31904 name: 'null',
31905 impl: NullRenderer
31906 }, {
31907 name: 'base',
31908 impl: BR
31909 }, {
31910 name: 'canvas',
31911 impl: CR
31912 }];
31913
31914 var incExts = [{
31915 type: 'layout',
31916 extensions: layout
31917 }, {
31918 type: 'renderer',
31919 extensions: renderer
31920 }];
31921
31922 var extensions = {}; // registered modules for extensions, indexed by name
31923
31924 var modules = {};
31925
31926 function setExtension(type, name, registrant) {
31927 var ext = registrant;
31928
31929 var overrideErr = function overrideErr(field) {
31930 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31931 };
31932
31933 if (type === 'core') {
31934 if (Core.prototype[name]) {
31935 return overrideErr(name);
31936 } else {
31937 Core.prototype[name] = registrant;
31938 }
31939 } else if (type === 'collection') {
31940 if (Collection.prototype[name]) {
31941 return overrideErr(name);
31942 } else {
31943 Collection.prototype[name] = registrant;
31944 }
31945 } else if (type === 'layout') {
31946 // fill in missing layout functions in the prototype
31947 var Layout = function Layout(options) {
31948 this.options = options;
31949 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31950
31951 if (!plainObject(this._private)) {
31952 this._private = {};
31953 }
31954
31955 this._private.cy = options.cy;
31956 this._private.listeners = [];
31957 this.createEmitter();
31958 };
31959
31960 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31961 var optLayoutFns = [];
31962
31963 for (var i = 0; i < optLayoutFns.length; i++) {
31964 var fnName = optLayoutFns[i];
31965
31966 layoutProto[fnName] = layoutProto[fnName] || function () {
31967 return this;
31968 };
31969 } // either .start() or .run() is defined, so autogen the other
31970
31971
31972 if (layoutProto.start && !layoutProto.run) {
31973 layoutProto.run = function () {
31974 this.start();
31975 return this;
31976 };
31977 } else if (!layoutProto.start && layoutProto.run) {
31978 layoutProto.start = function () {
31979 this.run();
31980 return this;
31981 };
31982 }
31983
31984 var regStop = registrant.prototype.stop;
31985
31986 layoutProto.stop = function () {
31987 var opts = this.options;
31988
31989 if (opts && opts.animate) {
31990 var anis = this.animations;
31991
31992 if (anis) {
31993 for (var _i = 0; _i < anis.length; _i++) {
31994 anis[_i].stop();
31995 }
31996 }
31997 }
31998
31999 if (regStop) {
32000 regStop.call(this);
32001 } else {
32002 this.emit('layoutstop');
32003 }
32004
32005 return this;
32006 };
32007
32008 if (!layoutProto.destroy) {
32009 layoutProto.destroy = function () {
32010 return this;
32011 };
32012 }
32013
32014 layoutProto.cy = function () {
32015 return this._private.cy;
32016 };
32017
32018 var getCy = function getCy(layout) {
32019 return layout._private.cy;
32020 };
32021
32022 var emitterOpts = {
32023 addEventFields: function addEventFields(layout, evt) {
32024 evt.layout = layout;
32025 evt.cy = getCy(layout);
32026 evt.target = layout;
32027 },
32028 bubble: function bubble() {
32029 return true;
32030 },
32031 parent: function parent(layout) {
32032 return getCy(layout);
32033 }
32034 };
32035 extend(layoutProto, {
32036 createEmitter: function createEmitter() {
32037 this._private.emitter = new Emitter(emitterOpts, this);
32038 return this;
32039 },
32040 emitter: function emitter() {
32041 return this._private.emitter;
32042 },
32043 on: function on(evt, cb) {
32044 this.emitter().on(evt, cb);
32045 return this;
32046 },
32047 one: function one(evt, cb) {
32048 this.emitter().one(evt, cb);
32049 return this;
32050 },
32051 once: function once(evt, cb) {
32052 this.emitter().one(evt, cb);
32053 return this;
32054 },
32055 removeListener: function removeListener(evt, cb) {
32056 this.emitter().removeListener(evt, cb);
32057 return this;
32058 },
32059 removeAllListeners: function removeAllListeners() {
32060 this.emitter().removeAllListeners();
32061 return this;
32062 },
32063 emit: function emit(evt, params) {
32064 this.emitter().emit(evt, params);
32065 return this;
32066 }
32067 });
32068 define$3.eventAliasesOn(layoutProto);
32069 ext = Layout; // replace with our wrapped layout
32070 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
32071 // user registered renderers inherit from base
32072 var BaseRenderer = getExtension('renderer', 'base');
32073 var bProto = BaseRenderer.prototype;
32074 var RegistrantRenderer = registrant;
32075 var rProto = registrant.prototype;
32076
32077 var Renderer = function Renderer() {
32078 BaseRenderer.apply(this, arguments);
32079 RegistrantRenderer.apply(this, arguments);
32080 };
32081
32082 var proto = Renderer.prototype;
32083
32084 for (var pName in bProto) {
32085 var pVal = bProto[pName];
32086 var existsInR = rProto[pName] != null;
32087
32088 if (existsInR) {
32089 return overrideErr(pName);
32090 }
32091
32092 proto[pName] = pVal; // take impl from base
32093 }
32094
32095 for (var _pName in rProto) {
32096 proto[_pName] = rProto[_pName]; // take impl from registrant
32097 }
32098
32099 bProto.clientFunctions.forEach(function (name) {
32100 proto[name] = proto[name] || function () {
32101 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
32102 };
32103 });
32104 ext = Renderer;
32105 }
32106
32107 return setMap({
32108 map: extensions,
32109 keys: [type, name],
32110 value: ext
32111 });
32112 }
32113
32114 function getExtension(type, name) {
32115 return getMap({
32116 map: extensions,
32117 keys: [type, name]
32118 });
32119 }
32120
32121 function setModule(type, name, moduleType, moduleName, registrant) {
32122 return setMap({
32123 map: modules,
32124 keys: [type, name, moduleType, moduleName],
32125 value: registrant
32126 });
32127 }
32128
32129 function getModule(type, name, moduleType, moduleName) {
32130 return getMap({
32131 map: modules,
32132 keys: [type, name, moduleType, moduleName]
32133 });
32134 }
32135
32136 var extension = function extension() {
32137 // e.g. extension('renderer', 'svg')
32138 if (arguments.length === 2) {
32139 return getExtension.apply(null, arguments);
32140 } // e.g. extension('renderer', 'svg', { ... })
32141 else if (arguments.length === 3) {
32142 return setExtension.apply(null, arguments);
32143 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
32144 else if (arguments.length === 4) {
32145 return getModule.apply(null, arguments);
32146 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
32147 else if (arguments.length === 5) {
32148 return setModule.apply(null, arguments);
32149 } else {
32150 error('Invalid extension access syntax');
32151 }
32152 }; // allows a core instance to access extensions internally
32153
32154
32155 Core.prototype.extension = extension; // included extensions
32156
32157 incExts.forEach(function (group) {
32158 group.extensions.forEach(function (ext) {
32159 setExtension(group.type, ext.name, ext.impl);
32160 });
32161 });
32162
32163 // (useful for init)
32164
32165 var Stylesheet = function Stylesheet() {
32166 if (!(this instanceof Stylesheet)) {
32167 return new Stylesheet();
32168 }
32169
32170 this.length = 0;
32171 };
32172
32173 var sheetfn = Stylesheet.prototype;
32174
32175 sheetfn.instanceString = function () {
32176 return 'stylesheet';
32177 }; // just store the selector to be parsed later
32178
32179
32180 sheetfn.selector = function (selector) {
32181 var i = this.length++;
32182 this[i] = {
32183 selector: selector,
32184 properties: []
32185 };
32186 return this; // chaining
32187 }; // just store the property to be parsed later
32188
32189
32190 sheetfn.css = function (name, value) {
32191 var i = this.length - 1;
32192
32193 if (string(name)) {
32194 this[i].properties.push({
32195 name: name,
32196 value: value
32197 });
32198 } else if (plainObject(name)) {
32199 var map = name;
32200 var propNames = Object.keys(map);
32201
32202 for (var j = 0; j < propNames.length; j++) {
32203 var key = propNames[j];
32204 var mapVal = map[key];
32205
32206 if (mapVal == null) {
32207 continue;
32208 }
32209
32210 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
32211
32212 if (prop == null) {
32213 continue;
32214 }
32215
32216 var _name = prop.name;
32217 var _value = mapVal;
32218 this[i].properties.push({
32219 name: _name,
32220 value: _value
32221 });
32222 }
32223 }
32224
32225 return this; // chaining
32226 };
32227
32228 sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
32229
32230 sheetfn.generateStyle = function (cy) {
32231 var style = new Style(cy);
32232 return this.appendToStyle(style);
32233 }; // append a dummy stylesheet object on a real style object
32234
32235
32236 sheetfn.appendToStyle = function (style) {
32237 for (var i = 0; i < this.length; i++) {
32238 var context = this[i];
32239 var selector = context.selector;
32240 var props = context.properties;
32241 style.selector(selector); // apply selector
32242
32243 for (var j = 0; j < props.length; j++) {
32244 var prop = props[j];
32245 style.css(prop.name, prop.value); // apply property
32246 }
32247 }
32248
32249 return style;
32250 };
32251
32252 var version = "3.14.1";
32253
32254 var cytoscape = function cytoscape(options) {
32255 // if no options specified, use default
32256 if (options === undefined) {
32257 options = {};
32258 } // create instance
32259
32260
32261 if (plainObject(options)) {
32262 return new Core(options);
32263 } // allow for registration of extensions
32264 else if (string(options)) {
32265 return extension.apply(extension, arguments);
32266 }
32267 }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
32268
32269
32270 cytoscape.use = function (ext) {
32271 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
32272
32273 args.unshift(cytoscape); // cytoscape is first arg to ext
32274
32275 ext.apply(null, args);
32276 return this;
32277 };
32278
32279 cytoscape.warnings = function (bool) {
32280 return warnings(bool);
32281 }; // replaced by build system
32282
32283
32284 cytoscape.version = version; // expose public apis (mostly for extensions)
32285
32286 cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
32287
32288 return cytoscape;
32289
32290})));