UNPKG

933 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 !elementOrCollection(obj) && (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_HASH_SEED = 9261;
1094 var K = 65599; // 37 also works pretty well
1095
1096 var DEFAULT_HASH_SEED_ALT = 5381;
1097 var hashIterableInts = function hashIterableInts(iterator) {
1098 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1099 // sdbm/string-hash
1100 var hash = seed;
1101 var entry;
1102
1103 for (;;) {
1104 entry = iterator.next();
1105
1106 if (entry.done) {
1107 break;
1108 }
1109
1110 hash = hash * K + entry.value | 0;
1111 }
1112
1113 return hash;
1114 };
1115 var hashInt = function hashInt(num) {
1116 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1117 // sdbm/string-hash
1118 return seed * K + num | 0;
1119 };
1120 var hashIntAlt = function hashIntAlt(num) {
1121 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
1122 // djb2/string-hash
1123 return (seed << 5) + seed + num | 0;
1124 };
1125 var combineHashes = function combineHashes(hash1, hash2) {
1126 return hash1 * 0x200000 + hash2;
1127 };
1128 var combineHashesArray = function combineHashesArray(hashes) {
1129 return hashes[0] * 0x200000 + hashes[1];
1130 };
1131 var hashArrays = function hashArrays(hashes1, hashes2) {
1132 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
1133 };
1134 var hashIntsArray = function hashIntsArray(ints, seed) {
1135 var entry = {
1136 value: 0,
1137 done: false
1138 };
1139 var i = 0;
1140 var length = ints.length;
1141 var iterator = {
1142 next: function next() {
1143 if (i < length) {
1144 entry.value = ints[i++];
1145 } else {
1146 entry.done = true;
1147 }
1148
1149 return entry;
1150 }
1151 };
1152 return hashIterableInts(iterator, seed);
1153 };
1154 var hashString = function hashString(str, seed) {
1155 var entry = {
1156 value: 0,
1157 done: false
1158 };
1159 var i = 0;
1160 var length = str.length;
1161 var iterator = {
1162 next: function next() {
1163 if (i < length) {
1164 entry.value = str.charCodeAt(i++);
1165 } else {
1166 entry.done = true;
1167 }
1168
1169 return entry;
1170 }
1171 };
1172 return hashIterableInts(iterator, seed);
1173 };
1174 var hashStrings = function hashStrings() {
1175 return hashStringsArray(arguments);
1176 };
1177 var hashStringsArray = function hashStringsArray(strs) {
1178 var hash;
1179
1180 for (var i = 0; i < strs.length; i++) {
1181 var str = strs[i];
1182
1183 if (i === 0) {
1184 hash = hashString(str);
1185 } else {
1186 hash = hashString(str, hash);
1187 }
1188 }
1189
1190 return hash;
1191 };
1192
1193 /*global console */
1194 var warningsEnabled = true;
1195 var warnSupported = console.warn != null; // eslint-disable-line no-console
1196
1197 var traceSupported = console.trace != null; // eslint-disable-line no-console
1198
1199 var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
1200 var trueify = function trueify() {
1201 return true;
1202 };
1203 var falsify = function falsify() {
1204 return false;
1205 };
1206 var zeroify = function zeroify() {
1207 return 0;
1208 };
1209 var noop = function noop() {};
1210 var error = function error(msg) {
1211 throw new Error(msg);
1212 };
1213 var warnings = function warnings(enabled) {
1214 if (enabled !== undefined) {
1215 warningsEnabled = !!enabled;
1216 } else {
1217 return warningsEnabled;
1218 }
1219 };
1220 var warn = function warn(msg) {
1221 /* eslint-disable no-console */
1222 if (!warnings()) {
1223 return;
1224 }
1225
1226 if (warnSupported) {
1227 console.warn(msg);
1228 } else {
1229 console.log(msg);
1230
1231 if (traceSupported) {
1232 console.trace();
1233 }
1234 }
1235 };
1236 /* eslint-enable */
1237
1238 var clone = function clone(obj) {
1239 return extend({}, obj);
1240 }; // gets a shallow copy of the argument
1241
1242 var copy = function copy(obj) {
1243 if (obj == null) {
1244 return obj;
1245 }
1246
1247 if (array(obj)) {
1248 return obj.slice();
1249 } else if (plainObject(obj)) {
1250 return clone(obj);
1251 } else {
1252 return obj;
1253 }
1254 };
1255 var copyArray = function copyArray(arr) {
1256 return arr.slice();
1257 };
1258 var uuid = function uuid(a, b
1259 /* placeholders */
1260 ) {
1261 for ( // loop :)
1262 b = a = ''; // b - result , a - numeric letiable
1263 a++ < 36; //
1264 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
1265 ? // return a random number or 4
1266 (a ^ 15 // if "a" is not 15
1267 ? // genetate a random number from 0 to 15
1268 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
1269 : 4 // otherwise 4
1270 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
1271 ) {
1272 }
1273
1274 return b;
1275 };
1276 var _staticEmptyObject = {};
1277 var staticEmptyObject = function staticEmptyObject() {
1278 return _staticEmptyObject;
1279 };
1280 var defaults = function defaults(_defaults) {
1281 var keys = Object.keys(_defaults);
1282 return function (opts) {
1283 var filledOpts = {};
1284
1285 for (var i = 0; i < keys.length; i++) {
1286 var key = keys[i];
1287 var optVal = opts == null ? undefined : opts[key];
1288 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
1289 }
1290
1291 return filledOpts;
1292 };
1293 };
1294 var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
1295 for (var i = arr.length; i >= 0; i--) {
1296 if (arr[i] === ele) {
1297 arr.splice(i, 1);
1298
1299 if (!manyCopies) {
1300 break;
1301 }
1302 }
1303 }
1304 };
1305 var clearArray = function clearArray(arr) {
1306 arr.splice(0, arr.length);
1307 };
1308 var push = function push(arr, otherArr) {
1309 for (var i = 0; i < otherArr.length; i++) {
1310 var el = otherArr[i];
1311 arr.push(el);
1312 }
1313 };
1314 var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
1315 if (prefix) {
1316 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1317 }
1318
1319 return obj[propName];
1320 };
1321 var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
1322 if (prefix) {
1323 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1324 }
1325
1326 obj[propName] = value;
1327 };
1328
1329 /* global Map */
1330 var ObjectMap =
1331 /*#__PURE__*/
1332 function () {
1333 function ObjectMap() {
1334 _classCallCheck(this, ObjectMap);
1335
1336 this._obj = {};
1337 }
1338
1339 _createClass(ObjectMap, [{
1340 key: "set",
1341 value: function set(key, val) {
1342 this._obj[key] = val;
1343 return this;
1344 }
1345 }, {
1346 key: "delete",
1347 value: function _delete(key) {
1348 this._obj[key] = undefined;
1349 return this;
1350 }
1351 }, {
1352 key: "clear",
1353 value: function clear() {
1354 this._obj = {};
1355 }
1356 }, {
1357 key: "has",
1358 value: function has(key) {
1359 return this._obj[key] !== undefined;
1360 }
1361 }, {
1362 key: "get",
1363 value: function get(key) {
1364 return this._obj[key];
1365 }
1366 }]);
1367
1368 return ObjectMap;
1369 }();
1370
1371 var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
1372
1373 /* global Set */
1374 var undef = "undefined" ;
1375
1376 var ObjectSet =
1377 /*#__PURE__*/
1378 function () {
1379 function ObjectSet(arrayOrObjectSet) {
1380 _classCallCheck(this, ObjectSet);
1381
1382 this._obj = Object.create(null);
1383 this.size = 0;
1384
1385 if (arrayOrObjectSet != null) {
1386 var arr;
1387
1388 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1389 arr = arrayOrObjectSet.toArray();
1390 } else {
1391 arr = arrayOrObjectSet;
1392 }
1393
1394 for (var i = 0; i < arr.length; i++) {
1395 this.add(arr[i]);
1396 }
1397 }
1398 }
1399
1400 _createClass(ObjectSet, [{
1401 key: "instanceString",
1402 value: function instanceString() {
1403 return 'set';
1404 }
1405 }, {
1406 key: "add",
1407 value: function add(val) {
1408 var o = this._obj;
1409
1410 if (o[val] !== 1) {
1411 o[val] = 1;
1412 this.size++;
1413 }
1414 }
1415 }, {
1416 key: "delete",
1417 value: function _delete(val) {
1418 var o = this._obj;
1419
1420 if (o[val] === 1) {
1421 o[val] = 0;
1422 this.size--;
1423 }
1424 }
1425 }, {
1426 key: "clear",
1427 value: function clear() {
1428 this._obj = Object.create(null);
1429 }
1430 }, {
1431 key: "has",
1432 value: function has(val) {
1433 return this._obj[val] === 1;
1434 }
1435 }, {
1436 key: "toArray",
1437 value: function toArray() {
1438 var _this = this;
1439
1440 return Object.keys(this._obj).filter(function (key) {
1441 return _this.has(key);
1442 });
1443 }
1444 }, {
1445 key: "forEach",
1446 value: function forEach(callback, thisArg) {
1447 return this.toArray().forEach(callback, thisArg);
1448 }
1449 }]);
1450
1451 return ObjectSet;
1452 }();
1453
1454 var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1455
1456 var Element = function Element(cy, params) {
1457 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1458
1459 if (cy === undefined || params === undefined || !core(cy)) {
1460 error('An element must have a core reference and parameters set');
1461 return;
1462 }
1463
1464 var group = params.group; // try to automatically infer the group if unspecified
1465
1466 if (group == null) {
1467 if (params.data && params.data.source != null && params.data.target != null) {
1468 group = 'edges';
1469 } else {
1470 group = 'nodes';
1471 }
1472 } // validate group
1473
1474
1475 if (group !== 'nodes' && group !== 'edges') {
1476 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1477 return;
1478 } // make the element array-like, just like a collection
1479
1480
1481 this.length = 1;
1482 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1483
1484 var _p = this._private = {
1485 cy: cy,
1486 single: true,
1487 // indicates this is an element
1488 data: params.data || {},
1489 // data object
1490 position: params.position || {
1491 x: 0,
1492 y: 0
1493 },
1494 // (x, y) position pair
1495 autoWidth: undefined,
1496 // width and height of nodes calculated by the renderer when set to special 'auto' value
1497 autoHeight: undefined,
1498 autoPadding: undefined,
1499 compoundBoundsClean: false,
1500 // whether the compound dimensions need to be recalculated the next time dimensions are read
1501 listeners: [],
1502 // array of bound listeners
1503 group: group,
1504 // string; 'nodes' or 'edges'
1505 style: {},
1506 // properties as set by the style
1507 rstyle: {},
1508 // properties for style sent from the renderer to the core
1509 styleCxts: [],
1510 // applied style contexts from the styler
1511 styleKeys: {},
1512 // per-group keys of style property values
1513 removed: true,
1514 // whether it's inside the vis; true if removed (set true here since we call restore)
1515 selected: params.selected ? true : false,
1516 // whether it's selected
1517 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1518 // whether it's selectable
1519 locked: params.locked ? true : false,
1520 // whether the element is locked (cannot be moved)
1521 grabbed: false,
1522 // whether the element is grabbed by the mouse; renderer sets this privately
1523 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1524 // whether the element can be grabbed
1525 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1526 // whether the element has passthrough panning enabled
1527 active: false,
1528 // whether the element is active from user interaction
1529 classes: new Set$1(),
1530 // map ( className => true )
1531 animation: {
1532 // object for currently-running animations
1533 current: [],
1534 queue: []
1535 },
1536 rscratch: {},
1537 // object in which the renderer can store information
1538 scratch: params.scratch || {},
1539 // scratch objects
1540 edges: [],
1541 // array of connected edges
1542 children: [],
1543 // array of children
1544 parent: null,
1545 // parent ref
1546 traversalCache: {},
1547 // cache of output of traversal functions
1548 backgrounding: false,
1549 // whether background images are loading
1550 bbCache: null,
1551 // cache of the current bounding box
1552 bbCacheShift: {
1553 x: 0,
1554 y: 0
1555 },
1556 // shift applied to cached bb to be applied on next get
1557 bodyBounds: null,
1558 // bounds cache of element body, w/o overlay
1559 overlayBounds: null,
1560 // bounds cache of element body, including overlay
1561 labelBounds: {
1562 // bounds cache of labels
1563 all: null,
1564 source: null,
1565 target: null,
1566 main: null
1567 },
1568 arrowBounds: {
1569 // bounds cache of edge arrows
1570 source: null,
1571 target: null,
1572 'mid-source': null,
1573 'mid-target': null
1574 }
1575 };
1576
1577 if (_p.position.x == null) {
1578 _p.position.x = 0;
1579 }
1580
1581 if (_p.position.y == null) {
1582 _p.position.y = 0;
1583 } // renderedPosition overrides if specified
1584
1585
1586 if (params.renderedPosition) {
1587 var rpos = params.renderedPosition;
1588 var pan = cy.pan();
1589 var zoom = cy.zoom();
1590 _p.position = {
1591 x: (rpos.x - pan.x) / zoom,
1592 y: (rpos.y - pan.y) / zoom
1593 };
1594 }
1595
1596 var classes = [];
1597
1598 if (array(params.classes)) {
1599 classes = params.classes;
1600 } else if (string(params.classes)) {
1601 classes = params.classes.split(/\s+/);
1602 }
1603
1604 for (var i = 0, l = classes.length; i < l; i++) {
1605 var cls = classes[i];
1606
1607 if (!cls || cls === '') {
1608 continue;
1609 }
1610
1611 _p.classes.add(cls);
1612 }
1613
1614 this.createEmitter();
1615 var bypass = params.style || params.css;
1616
1617 if (bypass) {
1618 warn('Setting a `style` bypass at element creation is deprecated');
1619 this.style(bypass);
1620 }
1621
1622 if (restore === undefined || restore) {
1623 this.restore();
1624 }
1625 };
1626
1627 var defineSearch = function defineSearch(params) {
1628 params = {
1629 bfs: params.bfs || !params.dfs,
1630 dfs: params.dfs || !params.bfs
1631 }; // from pseudocode on wikipedia
1632
1633 return function searchFn(roots, fn$1, directed) {
1634 var options;
1635
1636 if (plainObject(roots) && !elementOrCollection(roots)) {
1637 options = roots;
1638 roots = options.roots || options.root;
1639 fn$1 = options.visit;
1640 directed = options.directed;
1641 }
1642
1643 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1644 fn$1 = fn(fn$1) ? fn$1 : function () {};
1645 var cy = this._private.cy;
1646 var v = roots = string(roots) ? this.filter(roots) : roots;
1647 var Q = [];
1648 var connectedNodes = [];
1649 var connectedBy = {};
1650 var id2depth = {};
1651 var V = {};
1652 var j = 0;
1653 var found;
1654
1655 var _this$byGroup = this.byGroup(),
1656 nodes = _this$byGroup.nodes,
1657 edges = _this$byGroup.edges; // enqueue v
1658
1659
1660 for (var i = 0; i < v.length; i++) {
1661 var vi = v[i];
1662 var viId = vi.id();
1663
1664 if (vi.isNode()) {
1665 Q.unshift(vi);
1666
1667 if (params.bfs) {
1668 V[viId] = true;
1669 connectedNodes.push(vi);
1670 }
1671
1672 id2depth[viId] = 0;
1673 }
1674 }
1675
1676 var _loop2 = function _loop2() {
1677 var v = params.bfs ? Q.shift() : Q.pop();
1678 var vId = v.id();
1679
1680 if (params.dfs) {
1681 if (V[vId]) {
1682 return "continue";
1683 }
1684
1685 V[vId] = true;
1686 connectedNodes.push(v);
1687 }
1688
1689 var depth = id2depth[vId];
1690 var prevEdge = connectedBy[vId];
1691 var src = prevEdge != null ? prevEdge.source() : null;
1692 var tgt = prevEdge != null ? prevEdge.target() : null;
1693 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1694 var ret = void 0;
1695 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1696
1697 if (ret === true) {
1698 found = v;
1699 return "break";
1700 }
1701
1702 if (ret === false) {
1703 return "break";
1704 }
1705
1706 var vwEdges = v.connectedEdges().filter(function (e) {
1707 return (!directed || e.source().same(v)) && edges.has(e);
1708 });
1709
1710 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1711 var e = vwEdges[_i2];
1712 var w = e.connectedNodes().filter(function (n) {
1713 return !n.same(v) && nodes.has(n);
1714 });
1715 var wId = w.id();
1716
1717 if (w.length !== 0 && !V[wId]) {
1718 w = w[0];
1719 Q.push(w);
1720
1721 if (params.bfs) {
1722 V[wId] = true;
1723 connectedNodes.push(w);
1724 }
1725
1726 connectedBy[wId] = e;
1727 id2depth[wId] = id2depth[vId] + 1;
1728 }
1729 }
1730 };
1731
1732 _loop: while (Q.length !== 0) {
1733 var _ret = _loop2();
1734
1735 switch (_ret) {
1736 case "continue":
1737 continue;
1738
1739 case "break":
1740 break _loop;
1741 }
1742 }
1743
1744 var connectedEles = cy.collection();
1745
1746 for (var _i = 0; _i < connectedNodes.length; _i++) {
1747 var node = connectedNodes[_i];
1748 var edge = connectedBy[node.id()];
1749
1750 if (edge != null) {
1751 connectedEles.push(edge);
1752 }
1753
1754 connectedEles.push(node);
1755 }
1756
1757 return {
1758 path: cy.collection(connectedEles),
1759 found: cy.collection(found)
1760 };
1761 };
1762 }; // search, spanning trees, etc
1763
1764
1765 var elesfn = {
1766 breadthFirstSearch: defineSearch({
1767 bfs: true
1768 }),
1769 depthFirstSearch: defineSearch({
1770 dfs: true
1771 })
1772 }; // nice, short mathemathical alias
1773
1774 elesfn.bfs = elesfn.breadthFirstSearch;
1775 elesfn.dfs = elesfn.depthFirstSearch;
1776
1777 var heap = createCommonjsModule(function (module, exports) {
1778 // Generated by CoffeeScript 1.8.0
1779 (function() {
1780 var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
1781
1782 floor = Math.floor, min = Math.min;
1783
1784
1785 /*
1786 Default comparison function to be used
1787 */
1788
1789 defaultCmp = function(x, y) {
1790 if (x < y) {
1791 return -1;
1792 }
1793 if (x > y) {
1794 return 1;
1795 }
1796 return 0;
1797 };
1798
1799
1800 /*
1801 Insert item x in list a, and keep it sorted assuming a is sorted.
1802
1803 If x is already in a, insert it to the right of the rightmost x.
1804
1805 Optional args lo (default 0) and hi (default a.length) bound the slice
1806 of a to be searched.
1807 */
1808
1809 insort = function(a, x, lo, hi, cmp) {
1810 var mid;
1811 if (lo == null) {
1812 lo = 0;
1813 }
1814 if (cmp == null) {
1815 cmp = defaultCmp;
1816 }
1817 if (lo < 0) {
1818 throw new Error('lo must be non-negative');
1819 }
1820 if (hi == null) {
1821 hi = a.length;
1822 }
1823 while (lo < hi) {
1824 mid = floor((lo + hi) / 2);
1825 if (cmp(x, a[mid]) < 0) {
1826 hi = mid;
1827 } else {
1828 lo = mid + 1;
1829 }
1830 }
1831 return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
1832 };
1833
1834
1835 /*
1836 Push item onto heap, maintaining the heap invariant.
1837 */
1838
1839 heappush = function(array, item, cmp) {
1840 if (cmp == null) {
1841 cmp = defaultCmp;
1842 }
1843 array.push(item);
1844 return _siftdown(array, 0, array.length - 1, cmp);
1845 };
1846
1847
1848 /*
1849 Pop the smallest item off the heap, maintaining the heap invariant.
1850 */
1851
1852 heappop = function(array, cmp) {
1853 var lastelt, returnitem;
1854 if (cmp == null) {
1855 cmp = defaultCmp;
1856 }
1857 lastelt = array.pop();
1858 if (array.length) {
1859 returnitem = array[0];
1860 array[0] = lastelt;
1861 _siftup(array, 0, cmp);
1862 } else {
1863 returnitem = lastelt;
1864 }
1865 return returnitem;
1866 };
1867
1868
1869 /*
1870 Pop and return the current smallest value, and add the new item.
1871
1872 This is more efficient than heappop() followed by heappush(), and can be
1873 more appropriate when using a fixed size heap. Note that the value
1874 returned may be larger than item! That constrains reasonable use of
1875 this routine unless written as part of a conditional replacement:
1876 if item > array[0]
1877 item = heapreplace(array, item)
1878 */
1879
1880 heapreplace = function(array, item, cmp) {
1881 var returnitem;
1882 if (cmp == null) {
1883 cmp = defaultCmp;
1884 }
1885 returnitem = array[0];
1886 array[0] = item;
1887 _siftup(array, 0, cmp);
1888 return returnitem;
1889 };
1890
1891
1892 /*
1893 Fast version of a heappush followed by a heappop.
1894 */
1895
1896 heappushpop = function(array, item, cmp) {
1897 var _ref;
1898 if (cmp == null) {
1899 cmp = defaultCmp;
1900 }
1901 if (array.length && cmp(array[0], item) < 0) {
1902 _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
1903 _siftup(array, 0, cmp);
1904 }
1905 return item;
1906 };
1907
1908
1909 /*
1910 Transform list into a heap, in-place, in O(array.length) time.
1911 */
1912
1913 heapify = function(array, cmp) {
1914 var i, _i, _len, _ref1, _results, _results1;
1915 if (cmp == null) {
1916 cmp = defaultCmp;
1917 }
1918 _ref1 = (function() {
1919 _results1 = [];
1920 for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
1921 return _results1;
1922 }).apply(this).reverse();
1923 _results = [];
1924 for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
1925 i = _ref1[_i];
1926 _results.push(_siftup(array, i, cmp));
1927 }
1928 return _results;
1929 };
1930
1931
1932 /*
1933 Update the position of the given item in the heap.
1934 This function should be called every time the item is being modified.
1935 */
1936
1937 updateItem = function(array, item, cmp) {
1938 var pos;
1939 if (cmp == null) {
1940 cmp = defaultCmp;
1941 }
1942 pos = array.indexOf(item);
1943 if (pos === -1) {
1944 return;
1945 }
1946 _siftdown(array, 0, pos, cmp);
1947 return _siftup(array, pos, cmp);
1948 };
1949
1950
1951 /*
1952 Find the n largest elements in a dataset.
1953 */
1954
1955 nlargest = function(array, n, cmp) {
1956 var elem, result, _i, _len, _ref;
1957 if (cmp == null) {
1958 cmp = defaultCmp;
1959 }
1960 result = array.slice(0, n);
1961 if (!result.length) {
1962 return result;
1963 }
1964 heapify(result, cmp);
1965 _ref = array.slice(n);
1966 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1967 elem = _ref[_i];
1968 heappushpop(result, elem, cmp);
1969 }
1970 return result.sort(cmp).reverse();
1971 };
1972
1973
1974 /*
1975 Find the n smallest elements in a dataset.
1976 */
1977
1978 nsmallest = function(array, n, cmp) {
1979 var elem, i, los, result, _i, _j, _len, _ref, _ref1, _results;
1980 if (cmp == null) {
1981 cmp = defaultCmp;
1982 }
1983 if (n * 10 <= array.length) {
1984 result = array.slice(0, n).sort(cmp);
1985 if (!result.length) {
1986 return result;
1987 }
1988 los = result[result.length - 1];
1989 _ref = array.slice(n);
1990 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
1991 elem = _ref[_i];
1992 if (cmp(elem, los) < 0) {
1993 insort(result, elem, 0, null, cmp);
1994 result.pop();
1995 los = result[result.length - 1];
1996 }
1997 }
1998 return result;
1999 }
2000 heapify(array, cmp);
2001 _results = [];
2002 for (i = _j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; i = 0 <= _ref1 ? ++_j : --_j) {
2003 _results.push(heappop(array, cmp));
2004 }
2005 return _results;
2006 };
2007
2008 _siftdown = function(array, startpos, pos, cmp) {
2009 var newitem, parent, parentpos;
2010 if (cmp == null) {
2011 cmp = defaultCmp;
2012 }
2013 newitem = array[pos];
2014 while (pos > startpos) {
2015 parentpos = (pos - 1) >> 1;
2016 parent = array[parentpos];
2017 if (cmp(newitem, parent) < 0) {
2018 array[pos] = parent;
2019 pos = parentpos;
2020 continue;
2021 }
2022 break;
2023 }
2024 return array[pos] = newitem;
2025 };
2026
2027 _siftup = function(array, pos, cmp) {
2028 var childpos, endpos, newitem, rightpos, startpos;
2029 if (cmp == null) {
2030 cmp = defaultCmp;
2031 }
2032 endpos = array.length;
2033 startpos = pos;
2034 newitem = array[pos];
2035 childpos = 2 * pos + 1;
2036 while (childpos < endpos) {
2037 rightpos = childpos + 1;
2038 if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
2039 childpos = rightpos;
2040 }
2041 array[pos] = array[childpos];
2042 pos = childpos;
2043 childpos = 2 * pos + 1;
2044 }
2045 array[pos] = newitem;
2046 return _siftdown(array, startpos, pos, cmp);
2047 };
2048
2049 Heap = (function() {
2050 Heap.push = heappush;
2051
2052 Heap.pop = heappop;
2053
2054 Heap.replace = heapreplace;
2055
2056 Heap.pushpop = heappushpop;
2057
2058 Heap.heapify = heapify;
2059
2060 Heap.updateItem = updateItem;
2061
2062 Heap.nlargest = nlargest;
2063
2064 Heap.nsmallest = nsmallest;
2065
2066 function Heap(cmp) {
2067 this.cmp = cmp != null ? cmp : defaultCmp;
2068 this.nodes = [];
2069 }
2070
2071 Heap.prototype.push = function(x) {
2072 return heappush(this.nodes, x, this.cmp);
2073 };
2074
2075 Heap.prototype.pop = function() {
2076 return heappop(this.nodes, this.cmp);
2077 };
2078
2079 Heap.prototype.peek = function() {
2080 return this.nodes[0];
2081 };
2082
2083 Heap.prototype.contains = function(x) {
2084 return this.nodes.indexOf(x) !== -1;
2085 };
2086
2087 Heap.prototype.replace = function(x) {
2088 return heapreplace(this.nodes, x, this.cmp);
2089 };
2090
2091 Heap.prototype.pushpop = function(x) {
2092 return heappushpop(this.nodes, x, this.cmp);
2093 };
2094
2095 Heap.prototype.heapify = function() {
2096 return heapify(this.nodes, this.cmp);
2097 };
2098
2099 Heap.prototype.updateItem = function(x) {
2100 return updateItem(this.nodes, x, this.cmp);
2101 };
2102
2103 Heap.prototype.clear = function() {
2104 return this.nodes = [];
2105 };
2106
2107 Heap.prototype.empty = function() {
2108 return this.nodes.length === 0;
2109 };
2110
2111 Heap.prototype.size = function() {
2112 return this.nodes.length;
2113 };
2114
2115 Heap.prototype.clone = function() {
2116 var heap;
2117 heap = new Heap();
2118 heap.nodes = this.nodes.slice(0);
2119 return heap;
2120 };
2121
2122 Heap.prototype.toArray = function() {
2123 return this.nodes.slice(0);
2124 };
2125
2126 Heap.prototype.insert = Heap.prototype.push;
2127
2128 Heap.prototype.top = Heap.prototype.peek;
2129
2130 Heap.prototype.front = Heap.prototype.peek;
2131
2132 Heap.prototype.has = Heap.prototype.contains;
2133
2134 Heap.prototype.copy = Heap.prototype.clone;
2135
2136 return Heap;
2137
2138 })();
2139
2140 (function(root, factory) {
2141 {
2142 return module.exports = factory();
2143 }
2144 })(this, function() {
2145 return Heap;
2146 });
2147
2148 }).call(commonjsGlobal);
2149 });
2150
2151 var heap$1 = heap;
2152
2153 var dijkstraDefaults = defaults({
2154 root: null,
2155 weight: function weight(edge) {
2156 return 1;
2157 },
2158 directed: false
2159 });
2160 var elesfn$1 = {
2161 dijkstra: function dijkstra(options) {
2162 if (!plainObject(options)) {
2163 var args = arguments;
2164 options = {
2165 root: args[0],
2166 weight: args[1],
2167 directed: args[2]
2168 };
2169 }
2170
2171 var _dijkstraDefaults = dijkstraDefaults(options),
2172 root = _dijkstraDefaults.root,
2173 weight = _dijkstraDefaults.weight,
2174 directed = _dijkstraDefaults.directed;
2175
2176 var eles = this;
2177 var weightFn = weight;
2178 var source = string(root) ? this.filter(root)[0] : root[0];
2179 var dist = {};
2180 var prev = {};
2181 var knownDist = {};
2182
2183 var _this$byGroup = this.byGroup(),
2184 nodes = _this$byGroup.nodes,
2185 edges = _this$byGroup.edges;
2186
2187 edges.unmergeBy(function (ele) {
2188 return ele.isLoop();
2189 });
2190
2191 var getDist = function getDist(node) {
2192 return dist[node.id()];
2193 };
2194
2195 var setDist = function setDist(node, d) {
2196 dist[node.id()] = d;
2197 Q.updateItem(node);
2198 };
2199
2200 var Q = new heap$1(function (a, b) {
2201 return getDist(a) - getDist(b);
2202 });
2203
2204 for (var i = 0; i < nodes.length; i++) {
2205 var node = nodes[i];
2206 dist[node.id()] = node.same(source) ? 0 : Infinity;
2207 Q.push(node);
2208 }
2209
2210 var distBetween = function distBetween(u, v) {
2211 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
2212 var smallestDistance = Infinity;
2213 var smallestEdge;
2214
2215 for (var _i = 0; _i < uvs.length; _i++) {
2216 var edge = uvs[_i];
2217
2218 var _weight = weightFn(edge);
2219
2220 if (_weight < smallestDistance || !smallestEdge) {
2221 smallestDistance = _weight;
2222 smallestEdge = edge;
2223 }
2224 }
2225
2226 return {
2227 edge: smallestEdge,
2228 dist: smallestDistance
2229 };
2230 };
2231
2232 while (Q.size() > 0) {
2233 var u = Q.pop();
2234 var smalletsDist = getDist(u);
2235 var uid = u.id();
2236 knownDist[uid] = smalletsDist;
2237
2238 if (smalletsDist === Infinity) {
2239 continue;
2240 }
2241
2242 var neighbors = u.neighborhood().intersect(nodes);
2243
2244 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
2245 var v = neighbors[_i2];
2246 var vid = v.id();
2247 var vDist = distBetween(u, v);
2248 var alt = smalletsDist + vDist.dist;
2249
2250 if (alt < getDist(v)) {
2251 setDist(v, alt);
2252 prev[vid] = {
2253 node: u,
2254 edge: vDist.edge
2255 };
2256 }
2257 } // for
2258
2259 } // while
2260
2261
2262 return {
2263 distanceTo: function distanceTo(node) {
2264 var target = string(node) ? nodes.filter(node)[0] : node[0];
2265 return knownDist[target.id()];
2266 },
2267 pathTo: function pathTo(node) {
2268 var target = string(node) ? nodes.filter(node)[0] : node[0];
2269 var S = [];
2270 var u = target;
2271 var uid = u.id();
2272
2273 if (target.length > 0) {
2274 S.unshift(target);
2275
2276 while (prev[uid]) {
2277 var p = prev[uid];
2278 S.unshift(p.edge);
2279 S.unshift(p.node);
2280 u = p.node;
2281 uid = u.id();
2282 }
2283 }
2284
2285 return eles.spawn(S);
2286 }
2287 };
2288 }
2289 };
2290
2291 var elesfn$2 = {
2292 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
2293 // implemented from pseudocode from wikipedia
2294 kruskal: function kruskal(weightFn) {
2295 weightFn = weightFn || function (edge) {
2296 return 1;
2297 };
2298
2299 var _this$byGroup = this.byGroup(),
2300 nodes = _this$byGroup.nodes,
2301 edges = _this$byGroup.edges;
2302
2303 var numNodes = nodes.length;
2304 var forest = new Array(numNodes);
2305 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
2306
2307 var findSetIndex = function findSetIndex(ele) {
2308 for (var i = 0; i < forest.length; i++) {
2309 var eles = forest[i];
2310
2311 if (eles.has(ele)) {
2312 return i;
2313 }
2314 }
2315 }; // start with one forest per node
2316
2317
2318 for (var i = 0; i < numNodes; i++) {
2319 forest[i] = this.spawn(nodes[i]);
2320 }
2321
2322 var S = edges.sort(function (a, b) {
2323 return weightFn(a) - weightFn(b);
2324 });
2325
2326 for (var _i = 0; _i < S.length; _i++) {
2327 var edge = S[_i];
2328 var u = edge.source()[0];
2329 var v = edge.target()[0];
2330 var setUIndex = findSetIndex(u);
2331 var setVIndex = findSetIndex(v);
2332 var setU = forest[setUIndex];
2333 var setV = forest[setVIndex];
2334
2335 if (setUIndex !== setVIndex) {
2336 A.merge(edge); // combine forests for u and v
2337
2338 setU.merge(setV);
2339 forest.splice(setVIndex, 1);
2340 }
2341 }
2342
2343 return A;
2344 }
2345 };
2346
2347 var aStarDefaults = defaults({
2348 root: null,
2349 goal: null,
2350 weight: function weight(edge) {
2351 return 1;
2352 },
2353 heuristic: function heuristic(edge) {
2354 return 0;
2355 },
2356 directed: false
2357 });
2358 var elesfn$3 = {
2359 // Implemented from pseudocode from wikipedia
2360 aStar: function aStar(options) {
2361 var cy = this.cy();
2362
2363 var _aStarDefaults = aStarDefaults(options),
2364 root = _aStarDefaults.root,
2365 goal = _aStarDefaults.goal,
2366 heuristic = _aStarDefaults.heuristic,
2367 directed = _aStarDefaults.directed,
2368 weight = _aStarDefaults.weight;
2369
2370 root = cy.collection(root)[0];
2371 goal = cy.collection(goal)[0];
2372 var sid = root.id();
2373 var tid = goal.id();
2374 var gScore = {};
2375 var fScore = {};
2376 var closedSetIds = {};
2377 var openSet = new heap$1(function (a, b) {
2378 return fScore[a.id()] - fScore[b.id()];
2379 });
2380 var openSetIds = new Set$1();
2381 var cameFrom = {};
2382 var cameFromEdge = {};
2383
2384 var addToOpenSet = function addToOpenSet(ele, id) {
2385 openSet.push(ele);
2386 openSetIds.add(id);
2387 };
2388
2389 var cMin, cMinId;
2390
2391 var popFromOpenSet = function popFromOpenSet() {
2392 cMin = openSet.pop();
2393 cMinId = cMin.id();
2394 openSetIds["delete"](cMinId);
2395 };
2396
2397 var isInOpenSet = function isInOpenSet(id) {
2398 return openSetIds.has(id);
2399 };
2400
2401 addToOpenSet(root, sid);
2402 gScore[sid] = 0;
2403 fScore[sid] = heuristic(root); // Counter
2404
2405 var steps = 0; // Main loop
2406
2407 while (openSet.size() > 0) {
2408 popFromOpenSet();
2409 steps++; // If we've found our goal, then we are done
2410
2411 if (cMinId === tid) {
2412 var path = [];
2413 var pathNode = goal;
2414 var pathNodeId = tid;
2415 var pathEdge = cameFromEdge[pathNodeId];
2416
2417 for (;;) {
2418 path.unshift(pathNode);
2419
2420 if (pathEdge != null) {
2421 path.unshift(pathEdge);
2422 }
2423
2424 pathNode = cameFrom[pathNodeId];
2425
2426 if (pathNode == null) {
2427 break;
2428 }
2429
2430 pathNodeId = pathNode.id();
2431 pathEdge = cameFromEdge[pathNodeId];
2432 }
2433
2434 return {
2435 found: true,
2436 distance: gScore[cMinId],
2437 path: this.spawn(path),
2438 steps: steps
2439 };
2440 } // Add cMin to processed nodes
2441
2442
2443 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
2444 // Take into account if graph is directed or not
2445
2446 var vwEdges = cMin._private.edges;
2447
2448 for (var i = 0; i < vwEdges.length; i++) {
2449 var e = vwEdges[i]; // edge must be in set of calling eles
2450
2451 if (!this.hasElementWithId(e.id())) {
2452 continue;
2453 } // cMin must be the source of edge if directed
2454
2455
2456 if (directed && e.data('source') !== cMinId) {
2457 continue;
2458 }
2459
2460 var wSrc = e.source();
2461 var wTgt = e.target();
2462 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
2463 var wid = w.id(); // node must be in set of calling eles
2464
2465 if (!this.hasElementWithId(wid)) {
2466 continue;
2467 } // if node is in closedSet, ignore it
2468
2469
2470 if (closedSetIds[wid]) {
2471 continue;
2472 } // New tentative score for node w
2473
2474
2475 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
2476 // w not present in openSet
2477 // OR
2478 // tentative gScore is less than previous value
2479 // w not in openSet
2480
2481 if (!isInOpenSet(wid)) {
2482 gScore[wid] = tempScore;
2483 fScore[wid] = tempScore + heuristic(w);
2484 addToOpenSet(w, wid);
2485 cameFrom[wid] = cMin;
2486 cameFromEdge[wid] = e;
2487 continue;
2488 } // w already in openSet, but with greater gScore
2489
2490
2491 if (tempScore < gScore[wid]) {
2492 gScore[wid] = tempScore;
2493 fScore[wid] = tempScore + heuristic(w);
2494 cameFrom[wid] = cMin;
2495 }
2496 } // End of neighbors update
2497
2498 } // End of main loop
2499 // If we've reached here, then we've not reached our goal
2500
2501
2502 return {
2503 found: false,
2504 distance: undefined,
2505 path: undefined,
2506 steps: steps
2507 };
2508 }
2509 }; // elesfn
2510
2511 var floydWarshallDefaults = defaults({
2512 weight: function weight(edge) {
2513 return 1;
2514 },
2515 directed: false
2516 });
2517 var elesfn$4 = {
2518 // Implemented from pseudocode from wikipedia
2519 floydWarshall: function floydWarshall(options) {
2520 var cy = this.cy();
2521
2522 var _floydWarshallDefault = floydWarshallDefaults(options),
2523 weight = _floydWarshallDefault.weight,
2524 directed = _floydWarshallDefault.directed;
2525
2526 var weightFn = weight;
2527
2528 var _this$byGroup = this.byGroup(),
2529 nodes = _this$byGroup.nodes,
2530 edges = _this$byGroup.edges;
2531
2532 var N = nodes.length;
2533 var Nsq = N * N;
2534
2535 var indexOf = function indexOf(node) {
2536 return nodes.indexOf(node);
2537 };
2538
2539 var atIndex = function atIndex(i) {
2540 return nodes[i];
2541 }; // Initialize distance matrix
2542
2543
2544 var dist = new Array(Nsq);
2545
2546 for (var n = 0; n < Nsq; n++) {
2547 var j = n % N;
2548 var i = (n - j) / N;
2549
2550 if (i === j) {
2551 dist[n] = 0;
2552 } else {
2553 dist[n] = Infinity;
2554 }
2555 } // Initialize matrix used for path reconstruction
2556 // Initialize distance matrix
2557
2558
2559 var next = new Array(Nsq);
2560 var edgeNext = new Array(Nsq); // Process edges
2561
2562 for (var _i = 0; _i < edges.length; _i++) {
2563 var edge = edges[_i];
2564 var src = edge.source()[0];
2565 var tgt = edge.target()[0];
2566
2567 if (src === tgt) {
2568 continue;
2569 } // exclude loops
2570
2571
2572 var s = indexOf(src);
2573 var t = indexOf(tgt);
2574 var st = s * N + t; // source to target index
2575
2576 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
2577
2578
2579 if (dist[st] > _weight) {
2580 dist[st] = _weight;
2581 next[st] = t;
2582 edgeNext[st] = edge;
2583 } // If undirected graph, process 'reversed' edge
2584
2585
2586 if (!directed) {
2587 var ts = t * N + s; // target to source index
2588
2589 if (!directed && dist[ts] > _weight) {
2590 dist[ts] = _weight;
2591 next[ts] = s;
2592 edgeNext[ts] = edge;
2593 }
2594 }
2595 } // Main loop
2596
2597
2598 for (var k = 0; k < N; k++) {
2599 for (var _i2 = 0; _i2 < N; _i2++) {
2600 var ik = _i2 * N + k;
2601
2602 for (var _j = 0; _j < N; _j++) {
2603 var ij = _i2 * N + _j;
2604 var kj = k * N + _j;
2605
2606 if (dist[ik] + dist[kj] < dist[ij]) {
2607 dist[ij] = dist[ik] + dist[kj];
2608 next[ij] = next[ik];
2609 }
2610 }
2611 }
2612 }
2613
2614 var getArgEle = function getArgEle(ele) {
2615 return (string(ele) ? cy.filter(ele) : ele)[0];
2616 };
2617
2618 var indexOfArgEle = function indexOfArgEle(ele) {
2619 return indexOf(getArgEle(ele));
2620 };
2621
2622 var res = {
2623 distance: function distance(from, to) {
2624 var i = indexOfArgEle(from);
2625 var j = indexOfArgEle(to);
2626 return dist[i * N + j];
2627 },
2628 path: function path(from, to) {
2629 var i = indexOfArgEle(from);
2630 var j = indexOfArgEle(to);
2631 var fromNode = atIndex(i);
2632
2633 if (i === j) {
2634 return fromNode.collection();
2635 }
2636
2637 if (next[i * N + j] == null) {
2638 return cy.collection();
2639 }
2640
2641 var path = cy.collection();
2642 var prev = i;
2643 var edge;
2644 path.merge(fromNode);
2645
2646 while (i !== j) {
2647 prev = i;
2648 i = next[i * N + j];
2649 edge = edgeNext[prev * N + i];
2650 path.merge(edge);
2651 path.merge(atIndex(i));
2652 }
2653
2654 return path;
2655 }
2656 };
2657 return res;
2658 } // floydWarshall
2659
2660 }; // elesfn
2661
2662 var bellmanFordDefaults = defaults({
2663 weight: function weight(edge) {
2664 return 1;
2665 },
2666 directed: false,
2667 root: null
2668 });
2669 var elesfn$5 = {
2670 // Implemented from pseudocode from wikipedia
2671 bellmanFord: function bellmanFord(options) {
2672 var _this = this;
2673
2674 var _bellmanFordDefaults = bellmanFordDefaults(options),
2675 weight = _bellmanFordDefaults.weight,
2676 directed = _bellmanFordDefaults.directed,
2677 root = _bellmanFordDefaults.root;
2678
2679 var weightFn = weight;
2680 var eles = this;
2681 var cy = this.cy();
2682
2683 var _this$byGroup = this.byGroup(),
2684 edges = _this$byGroup.edges,
2685 nodes = _this$byGroup.nodes;
2686
2687 var numNodes = nodes.length;
2688 var infoMap = new Map$1();
2689 var hasNegativeWeightCycle = false;
2690 var negativeWeightCycles = [];
2691 root = cy.collection(root)[0]; // in case selector passed
2692
2693 edges.unmergeBy(function (edge) {
2694 return edge.isLoop();
2695 });
2696 var numEdges = edges.length;
2697
2698 var getInfo = function getInfo(node) {
2699 var obj = infoMap.get(node.id());
2700
2701 if (!obj) {
2702 obj = {};
2703 infoMap.set(node.id(), obj);
2704 }
2705
2706 return obj;
2707 };
2708
2709 var getNodeFromTo = function getNodeFromTo(to) {
2710 return (string(to) ? cy.$(to) : to)[0];
2711 };
2712
2713 var distanceTo = function distanceTo(to) {
2714 return getInfo(getNodeFromTo(to)).dist;
2715 };
2716
2717 var pathTo = function pathTo(to) {
2718 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2719 var end = getNodeFromTo(to);
2720 var path = [];
2721 var node = end;
2722
2723 for (;;) {
2724 if (node == null) {
2725 return _this.spawn();
2726 }
2727
2728 var _getInfo = getInfo(node),
2729 edge = _getInfo.edge,
2730 pred = _getInfo.pred;
2731
2732 path.unshift(node[0]);
2733
2734 if (node.same(thisStart) && path.length > 0) {
2735 break;
2736 }
2737
2738 if (edge != null) {
2739 path.unshift(edge);
2740 }
2741
2742 node = pred;
2743 }
2744
2745 return eles.spawn(path);
2746 }; // Initializations { dist, pred, edge }
2747
2748
2749 for (var i = 0; i < numNodes; i++) {
2750 var node = nodes[i];
2751 var info = getInfo(node);
2752
2753 if (node.same(root)) {
2754 info.dist = 0;
2755 } else {
2756 info.dist = Infinity;
2757 }
2758
2759 info.pred = null;
2760 info.edge = null;
2761 } // Edges relaxation
2762
2763
2764 var replacedEdge = false;
2765
2766 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2767 var dist = info1.dist + weight;
2768
2769 if (dist < info2.dist && !edge.same(info1.edge)) {
2770 info2.dist = dist;
2771 info2.pred = node1;
2772 info2.edge = edge;
2773 replacedEdge = true;
2774 }
2775 };
2776
2777 for (var _i = 1; _i < numNodes; _i++) {
2778 replacedEdge = false;
2779
2780 for (var e = 0; e < numEdges; e++) {
2781 var edge = edges[e];
2782 var src = edge.source();
2783 var tgt = edge.target();
2784
2785 var _weight = weightFn(edge);
2786
2787 var srcInfo = getInfo(src);
2788 var tgtInfo = getInfo(tgt);
2789 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2790
2791 if (!directed) {
2792 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2793 }
2794 }
2795
2796 if (!replacedEdge) {
2797 break;
2798 }
2799 }
2800
2801 if (replacedEdge) {
2802 // Check for negative weight cycles
2803 for (var _e = 0; _e < numEdges; _e++) {
2804 var _edge = edges[_e];
2805
2806 var _src = _edge.source();
2807
2808 var _tgt = _edge.target();
2809
2810 var _weight2 = weightFn(_edge);
2811
2812 var srcDist = getInfo(_src).dist;
2813 var tgtDist = getInfo(_tgt).dist;
2814
2815 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2816 warn('Graph contains a negative weight cycle for Bellman-Ford');
2817 hasNegativeWeightCycle = true;
2818 break;
2819 }
2820 }
2821 }
2822
2823 return {
2824 distanceTo: distanceTo,
2825 pathTo: pathTo,
2826 hasNegativeWeightCycle: hasNegativeWeightCycle,
2827 negativeWeightCycles: negativeWeightCycles
2828 };
2829 } // bellmanFord
2830
2831 }; // elesfn
2832
2833 var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2834 // Updates the remaining edge lists
2835 // Receives as a paramater the edge which causes the collapse
2836
2837 var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2838 if (remainingEdges.length === 0) {
2839 error("Karger-Stein must be run on a connected (sub)graph");
2840 }
2841
2842 var edgeInfo = remainingEdges[edgeIndex];
2843 var sourceIn = edgeInfo[1];
2844 var targetIn = edgeInfo[2];
2845 var partition1 = nodeMap[sourceIn];
2846 var partition2 = nodeMap[targetIn];
2847 var newEdges = remainingEdges; // re-use array
2848 // Delete all edges between partition1 and partition2
2849
2850 for (var i = newEdges.length - 1; i >= 0; i--) {
2851 var edge = newEdges[i];
2852 var src = edge[1];
2853 var tgt = edge[2];
2854
2855 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2856 newEdges.splice(i, 1);
2857 }
2858 } // All edges pointing to partition2 should now point to partition1
2859
2860
2861 for (var _i = 0; _i < newEdges.length; _i++) {
2862 var _edge = newEdges[_i];
2863
2864 if (_edge[1] === partition2) {
2865 // Check source
2866 newEdges[_i] = _edge.slice(); // copy
2867
2868 newEdges[_i][1] = partition1;
2869 } else if (_edge[2] === partition2) {
2870 // Check target
2871 newEdges[_i] = _edge.slice(); // copy
2872
2873 newEdges[_i][2] = partition1;
2874 }
2875 } // Move all nodes from partition2 to partition1
2876
2877
2878 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2879 if (nodeMap[_i2] === partition2) {
2880 nodeMap[_i2] = partition1;
2881 }
2882 }
2883
2884 return newEdges;
2885 }; // Contracts a graph until we reach a certain number of meta nodes
2886
2887
2888 var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2889 while (size > sizeLimit) {
2890 // Choose an edge randomly
2891 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2892
2893 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2894 size--;
2895 }
2896
2897 return remainingEdges;
2898 };
2899
2900 var elesfn$6 = {
2901 // Computes the minimum cut of an undirected graph
2902 // Returns the correct answer with high probability
2903 kargerStein: function kargerStein() {
2904 var _this = this;
2905
2906 var _this$byGroup = this.byGroup(),
2907 nodes = _this$byGroup.nodes,
2908 edges = _this$byGroup.edges;
2909
2910 edges.unmergeBy(function (edge) {
2911 return edge.isLoop();
2912 });
2913 var numNodes = nodes.length;
2914 var numEdges = edges.length;
2915 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2916 var stopSize = Math.floor(numNodes / sqrt2);
2917
2918 if (numNodes < 2) {
2919 error('At least 2 nodes are required for Karger-Stein algorithm');
2920 return undefined;
2921 } // Now store edge destination as indexes
2922 // Format for each edge (edge index, source node index, target node index)
2923
2924
2925 var edgeIndexes = [];
2926
2927 for (var i = 0; i < numEdges; i++) {
2928 var e = edges[i];
2929 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2930 } // We will store the best cut found here
2931
2932
2933 var minCutSize = Infinity;
2934 var minCutEdgeIndexes = [];
2935 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2936
2937 var metaNodeMap = new Array(numNodes);
2938 var metaNodeMap2 = new Array(numNodes);
2939
2940 var copyNodesMap = function copyNodesMap(from, to) {
2941 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2942 to[_i3] = from[_i3];
2943 }
2944 }; // Main loop
2945
2946
2947 for (var iter = 0; iter <= numIter; iter++) {
2948 // Reset meta node partition
2949 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2950 metaNodeMap[_i4] = _i4;
2951 } // Contract until stop point (stopSize nodes)
2952
2953
2954 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2955 var edgesState2 = edgesState.slice(); // copy
2956 // Create a copy of the colapsed nodes state
2957
2958 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2959
2960 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2961 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2962
2963 if (res1.length <= res2.length && res1.length < minCutSize) {
2964 minCutSize = res1.length;
2965 minCutEdgeIndexes = res1;
2966 copyNodesMap(metaNodeMap, minCutNodeMap);
2967 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2968 minCutSize = res2.length;
2969 minCutEdgeIndexes = res2;
2970 copyNodesMap(metaNodeMap2, minCutNodeMap);
2971 }
2972 } // end of main loop
2973 // Construct result
2974
2975
2976 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2977 return edges[e[0]];
2978 }));
2979 var partition1 = this.spawn();
2980 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2981
2982 var witnessNodePartition = minCutNodeMap[0];
2983
2984 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2985 var partitionId = minCutNodeMap[_i5];
2986 var node = nodes[_i5];
2987
2988 if (partitionId === witnessNodePartition) {
2989 partition1.merge(node);
2990 } else {
2991 partition2.merge(node);
2992 }
2993 } // construct components corresponding to each disjoint subset of nodes
2994
2995
2996 var constructComponent = function constructComponent(subset) {
2997 var component = _this.spawn();
2998
2999 subset.forEach(function (node) {
3000 component.merge(node);
3001 node.connectedEdges().forEach(function (edge) {
3002 // ensure edge is within calling collection and edge is not in cut
3003 if (_this.contains(edge) && !cut.contains(edge)) {
3004 component.merge(edge);
3005 }
3006 });
3007 });
3008 return component;
3009 };
3010
3011 var components = [constructComponent(partition1), constructComponent(partition2)];
3012 var ret = {
3013 cut: cut,
3014 components: components,
3015 // n.b. partitions are included to be compatible with the old api spec
3016 // (could be removed in a future major version)
3017 partition1: partition1,
3018 partition2: partition2
3019 };
3020 return ret;
3021 }
3022 }; // elesfn
3023
3024 var copyPosition = function copyPosition(p) {
3025 return {
3026 x: p.x,
3027 y: p.y
3028 };
3029 };
3030 var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3031 return {
3032 x: p.x * zoom + pan.x,
3033 y: p.y * zoom + pan.y
3034 };
3035 };
3036 var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3037 return {
3038 x: (p.x - pan.x) / zoom,
3039 y: (p.y - pan.y) / zoom
3040 };
3041 };
3042 var array2point = function array2point(arr) {
3043 return {
3044 x: arr[0],
3045 y: arr[1]
3046 };
3047 };
3048 var min = function min(arr) {
3049 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3050 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3051 var min = Infinity;
3052
3053 for (var i = begin; i < end; i++) {
3054 var val = arr[i];
3055
3056 if (isFinite(val)) {
3057 min = Math.min(val, min);
3058 }
3059 }
3060
3061 return min;
3062 };
3063 var max = function max(arr) {
3064 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3065 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3066 var max = -Infinity;
3067
3068 for (var i = begin; i < end; i++) {
3069 var val = arr[i];
3070
3071 if (isFinite(val)) {
3072 max = Math.max(val, max);
3073 }
3074 }
3075
3076 return max;
3077 };
3078 var mean = function mean(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 total = 0;
3082 var n = 0;
3083
3084 for (var i = begin; i < end; i++) {
3085 var val = arr[i];
3086
3087 if (isFinite(val)) {
3088 total += val;
3089 n++;
3090 }
3091 }
3092
3093 return total / n;
3094 };
3095 var median = function median(arr) {
3096 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3097 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3098 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3099 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3100 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3101
3102 if (copy) {
3103 arr = arr.slice(begin, end);
3104 } else {
3105 if (end < arr.length) {
3106 arr.splice(end, arr.length - end);
3107 }
3108
3109 if (begin > 0) {
3110 arr.splice(0, begin);
3111 }
3112 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3113
3114
3115 var off = 0; // offset from non-finite values
3116
3117 for (var i = arr.length - 1; i >= 0; i--) {
3118 var v = arr[i];
3119
3120 if (includeHoles) {
3121 if (!isFinite(v)) {
3122 arr[i] = -Infinity;
3123 off++;
3124 }
3125 } else {
3126 // just remove it if we don't want to consider holes
3127 arr.splice(i, 1);
3128 }
3129 }
3130
3131 if (sort) {
3132 arr.sort(function (a, b) {
3133 return a - b;
3134 }); // requires copy = true if you don't want to change the orig
3135 }
3136
3137 var len = arr.length;
3138 var mid = Math.floor(len / 2);
3139
3140 if (len % 2 !== 0) {
3141 return arr[mid + 1 + off];
3142 } else {
3143 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3144 }
3145 };
3146 var deg2rad = function deg2rad(deg) {
3147 return Math.PI * deg / 180;
3148 };
3149 var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3150 return Math.atan2(dispY, dispX) - Math.PI / 2;
3151 };
3152 var log2 = Math.log2 || function (n) {
3153 return Math.log(n) / Math.log(2);
3154 };
3155 var signum = function signum(x) {
3156 if (x > 0) {
3157 return 1;
3158 } else if (x < 0) {
3159 return -1;
3160 } else {
3161 return 0;
3162 }
3163 };
3164 var dist = function dist(p1, p2) {
3165 return Math.sqrt(sqdist(p1, p2));
3166 };
3167 var sqdist = function sqdist(p1, p2) {
3168 var dx = p2.x - p1.x;
3169 var dy = p2.y - p1.y;
3170 return dx * dx + dy * dy;
3171 };
3172 var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3173 var length = v.length; // First, get sum of all elements
3174
3175 var total = 0;
3176
3177 for (var i = 0; i < length; i++) {
3178 total += v[i];
3179 } // Now, divide each by the sum of all elements
3180
3181
3182 for (var _i = 0; _i < length; _i++) {
3183 v[_i] = v[_i] / total;
3184 }
3185
3186 return v;
3187 };
3188
3189 var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3190 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3191 };
3192 var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3193 return {
3194 x: qbezierAt(p0.x, p1.x, p2.x, t),
3195 y: qbezierAt(p0.y, p1.y, p2.y, t)
3196 };
3197 };
3198 var lineAt = function lineAt(p0, p1, t, d) {
3199 var vec = {
3200 x: p1.x - p0.x,
3201 y: p1.y - p0.y
3202 };
3203 var vecDist = dist(p0, p1);
3204 var normVec = {
3205 x: vec.x / vecDist,
3206 y: vec.y / vecDist
3207 };
3208 t = t == null ? 0 : t;
3209 d = d != null ? d : t * vecDist;
3210 return {
3211 x: p0.x + normVec.x * d,
3212 y: p0.y + normVec.y * d
3213 };
3214 };
3215 var bound = function bound(min, val, max) {
3216 return Math.max(min, Math.min(max, val));
3217 }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3218
3219 var makeBoundingBox = function makeBoundingBox(bb) {
3220 if (bb == null) {
3221 return {
3222 x1: Infinity,
3223 y1: Infinity,
3224 x2: -Infinity,
3225 y2: -Infinity,
3226 w: 0,
3227 h: 0
3228 };
3229 } else if (bb.x1 != null && bb.y1 != null) {
3230 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3231 return {
3232 x1: bb.x1,
3233 y1: bb.y1,
3234 x2: bb.x2,
3235 y2: bb.y2,
3236 w: bb.x2 - bb.x1,
3237 h: bb.y2 - bb.y1
3238 };
3239 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3240 return {
3241 x1: bb.x1,
3242 y1: bb.y1,
3243 x2: bb.x1 + bb.w,
3244 y2: bb.y1 + bb.h,
3245 w: bb.w,
3246 h: bb.h
3247 };
3248 }
3249 }
3250 };
3251 var copyBoundingBox = function copyBoundingBox(bb) {
3252 return {
3253 x1: bb.x1,
3254 x2: bb.x2,
3255 w: bb.w,
3256 y1: bb.y1,
3257 y2: bb.y2,
3258 h: bb.h
3259 };
3260 };
3261 var clearBoundingBox = function clearBoundingBox(bb) {
3262 bb.x1 = Infinity;
3263 bb.y1 = Infinity;
3264 bb.x2 = -Infinity;
3265 bb.y2 = -Infinity;
3266 bb.w = 0;
3267 bb.h = 0;
3268 };
3269 var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3270 // update bb1 with bb2 bounds
3271 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3272 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3273 bb1.w = bb1.x2 - bb1.x1;
3274 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3275 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3276 bb1.h = bb1.y2 - bb1.y1;
3277 };
3278 var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3279 bb.x1 = Math.min(bb.x1, x);
3280 bb.x2 = Math.max(bb.x2, x);
3281 bb.w = bb.x2 - bb.x1;
3282 bb.y1 = Math.min(bb.y1, y);
3283 bb.y2 = Math.max(bb.y2, y);
3284 bb.h = bb.y2 - bb.y1;
3285 };
3286 var expandBoundingBox = function expandBoundingBox(bb) {
3287 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3288 bb.x1 -= padding;
3289 bb.x2 += padding;
3290 bb.y1 -= padding;
3291 bb.y2 += padding;
3292 bb.w = bb.x2 - bb.x1;
3293 bb.h = bb.y2 - bb.y1;
3294 return bb;
3295 };
3296 var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3297 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3298 var top, right, bottom, left;
3299
3300 if (padding.length === 1) {
3301 top = right = bottom = left = padding[0];
3302 } else if (padding.length === 2) {
3303 top = bottom = padding[0];
3304 left = right = padding[1];
3305 } else if (padding.length === 4) {
3306 var _padding = _slicedToArray(padding, 4);
3307
3308 top = _padding[0];
3309 right = _padding[1];
3310 bottom = _padding[2];
3311 left = _padding[3];
3312 }
3313
3314 bb.x1 -= left;
3315 bb.x2 += right;
3316 bb.y1 -= top;
3317 bb.y2 += bottom;
3318 bb.w = bb.x2 - bb.x1;
3319 bb.h = bb.y2 - bb.y1;
3320 return bb;
3321 };
3322
3323 var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3324 bb1.x1 = bb2.x1;
3325 bb1.y1 = bb2.y1;
3326 bb1.x2 = bb2.x2;
3327 bb1.y2 = bb2.y2;
3328 bb1.w = bb1.x2 - bb1.x1;
3329 bb1.h = bb1.y2 - bb1.y1;
3330 };
3331 var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
3332 bb.x1 += delta.x;
3333 bb.x2 += delta.x;
3334 bb.y1 += delta.y;
3335 bb.y2 += delta.y;
3336 };
3337 var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3338 // case: one bb to right of other
3339 if (bb1.x1 > bb2.x2) {
3340 return false;
3341 }
3342
3343 if (bb2.x1 > bb1.x2) {
3344 return false;
3345 } // case: one bb to left of other
3346
3347
3348 if (bb1.x2 < bb2.x1) {
3349 return false;
3350 }
3351
3352 if (bb2.x2 < bb1.x1) {
3353 return false;
3354 } // case: one bb above other
3355
3356
3357 if (bb1.y2 < bb2.y1) {
3358 return false;
3359 }
3360
3361 if (bb2.y2 < bb1.y1) {
3362 return false;
3363 } // case: one bb below other
3364
3365
3366 if (bb1.y1 > bb2.y2) {
3367 return false;
3368 }
3369
3370 if (bb2.y1 > bb1.y2) {
3371 return false;
3372 } // otherwise, must have some overlap
3373
3374
3375 return true;
3376 };
3377 var inBoundingBox = function inBoundingBox(bb, x, y) {
3378 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3379 };
3380 var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3381 return inBoundingBox(bb, pt.x, pt.y);
3382 };
3383 var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3384 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3385 };
3386 var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3387 var cornerRadius = getRoundRectangleRadius(width, height);
3388 var halfWidth = width / 2;
3389 var halfHeight = height / 2; // Check intersections with straight line segments
3390
3391 var straightLineIntersections; // Top segment, left to right
3392
3393 {
3394 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3395 var topStartY = nodeY - halfHeight - padding;
3396 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3397 var topEndY = topStartY;
3398 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3399
3400 if (straightLineIntersections.length > 0) {
3401 return straightLineIntersections;
3402 }
3403 } // Right segment, top to bottom
3404
3405 {
3406 var rightStartX = nodeX + halfWidth + padding;
3407 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3408 var rightEndX = rightStartX;
3409 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3410 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3411
3412 if (straightLineIntersections.length > 0) {
3413 return straightLineIntersections;
3414 }
3415 } // Bottom segment, left to right
3416
3417 {
3418 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3419 var bottomStartY = nodeY + halfHeight + padding;
3420 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3421 var bottomEndY = bottomStartY;
3422 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3423
3424 if (straightLineIntersections.length > 0) {
3425 return straightLineIntersections;
3426 }
3427 } // Left segment, top to bottom
3428
3429 {
3430 var leftStartX = nodeX - halfWidth - padding;
3431 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3432 var leftEndX = leftStartX;
3433 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3434 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3435
3436 if (straightLineIntersections.length > 0) {
3437 return straightLineIntersections;
3438 }
3439 } // Check intersections with arc segments
3440
3441 var arcIntersections; // Top Left
3442
3443 {
3444 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3445 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3446 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3447
3448 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3449 return [arcIntersections[0], arcIntersections[1]];
3450 }
3451 } // Top Right
3452
3453 {
3454 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3455 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3456 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3457
3458 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3459 return [arcIntersections[0], arcIntersections[1]];
3460 }
3461 } // Bottom Right
3462
3463 {
3464 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3465 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3466 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3467
3468 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3469 return [arcIntersections[0], arcIntersections[1]];
3470 }
3471 } // Bottom Left
3472
3473 {
3474 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3475 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3476 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3477
3478 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3479 return [arcIntersections[0], arcIntersections[1]];
3480 }
3481 }
3482 return []; // if nothing
3483 };
3484 var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3485 var t = tolerance;
3486 var x1 = Math.min(lx1, lx2);
3487 var x2 = Math.max(lx1, lx2);
3488 var y1 = Math.min(ly1, ly2);
3489 var y2 = Math.max(ly1, ly2);
3490 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3491 };
3492 var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3493 var bb = {
3494 x1: Math.min(x1, x3, x2) - tolerance,
3495 x2: Math.max(x1, x3, x2) + tolerance,
3496 y1: Math.min(y1, y3, y2) - tolerance,
3497 y2: Math.max(y1, y3, y2) + tolerance
3498 }; // if outside the rough bounding box for the bezier, then it can't be a hit
3499
3500 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3501 // console.log('bezier out of rough bb')
3502 return false;
3503 } else {
3504 // console.log('do more expensive check');
3505 return true;
3506 }
3507 };
3508 var solveQuadratic = function solveQuadratic(a, b, c, val) {
3509 c -= val;
3510 var r = b * b - 4 * a * c;
3511
3512 if (r < 0) {
3513 return [];
3514 }
3515
3516 var sqrtR = Math.sqrt(r);
3517 var denom = 2 * a;
3518 var root1 = (-b + sqrtR) / denom;
3519 var root2 = (-b - sqrtR) / denom;
3520 return [root1, root2];
3521 };
3522 var solveCubic = function solveCubic(a, b, c, d, result) {
3523 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3524 // r is the real component, i is the imaginary component
3525 // An implementation of the Cardano method from the year 1545
3526 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3527 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
3528
3529 if (a === 0) {
3530 a = epsilon;
3531 }
3532
3533 b /= a;
3534 c /= a;
3535 d /= a;
3536 var discriminant, q, r, dum1, s, t, term1, r13;
3537 q = (3.0 * c - b * b) / 9.0;
3538 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3539 r /= 54.0;
3540 discriminant = q * q * q + r * r;
3541 result[1] = 0;
3542 term1 = b / 3.0;
3543
3544 if (discriminant > 0) {
3545 s = r + Math.sqrt(discriminant);
3546 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3547 t = r - Math.sqrt(discriminant);
3548 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3549 result[0] = -term1 + s + t;
3550 term1 += (s + t) / 2.0;
3551 result[4] = result[2] = -term1;
3552 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3553 result[3] = term1;
3554 result[5] = -term1;
3555 return;
3556 }
3557
3558 result[5] = result[3] = 0;
3559
3560 if (discriminant === 0) {
3561 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3562 result[0] = -term1 + 2.0 * r13;
3563 result[4] = result[2] = -(r13 + term1);
3564 return;
3565 }
3566
3567 q = -q;
3568 dum1 = q * q * q;
3569 dum1 = Math.acos(r / Math.sqrt(dum1));
3570 r13 = 2.0 * Math.sqrt(q);
3571 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3572 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3573 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3574 return;
3575 };
3576 var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3577 // Find minimum distance by using the minimum of the distance
3578 // function between the given point and the curve
3579 // This gives the coefficients of the resulting cubic equation
3580 // whose roots tell us where a possible minimum is
3581 // (Coefficients are divided by 4)
3582 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;
3583 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;
3584 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;
3585 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);
3586
3587 var roots = []; // Use the cubic solving algorithm
3588
3589 solveCubic(a, b, c, d, roots);
3590 var zeroThreshold = 0.0000001;
3591 var params = [];
3592
3593 for (var index = 0; index < 6; index += 2) {
3594 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3595 params.push(roots[index]);
3596 }
3597 }
3598
3599 params.push(1.0);
3600 params.push(0.0);
3601 var minDistanceSquared = -1;
3602 var curX, curY, distSquared;
3603
3604 for (var i = 0; i < params.length; i++) {
3605 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3606 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3607 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3608
3609 if (minDistanceSquared >= 0) {
3610 if (distSquared < minDistanceSquared) {
3611 minDistanceSquared = distSquared;
3612 }
3613 } else {
3614 minDistanceSquared = distSquared;
3615 }
3616 }
3617
3618 return minDistanceSquared;
3619 };
3620 var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3621 var offset = [x - x1, y - y1];
3622 var line = [x2 - x1, y2 - y1];
3623 var lineSq = line[0] * line[0] + line[1] * line[1];
3624 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3625 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3626 var adjSq = dotProduct * dotProduct / lineSq;
3627
3628 if (dotProduct < 0) {
3629 return hypSq;
3630 }
3631
3632 if (adjSq > lineSq) {
3633 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3634 }
3635
3636 return hypSq - adjSq;
3637 };
3638 var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3639 var x1, y1, x2, y2;
3640 var y3; // Intersect with vertical line through (x, y)
3641
3642 var up = 0; // let down = 0;
3643
3644 for (var i = 0; i < points.length / 2; i++) {
3645 x1 = points[i * 2];
3646 y1 = points[i * 2 + 1];
3647
3648 if (i + 1 < points.length / 2) {
3649 x2 = points[(i + 1) * 2];
3650 y2 = points[(i + 1) * 2 + 1];
3651 } else {
3652 x2 = points[(i + 1 - points.length / 2) * 2];
3653 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3654 }
3655
3656 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3657 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3658
3659 if (y3 > y) {
3660 up++;
3661 } // if( y3 < y ){
3662 // down++;
3663 // }
3664
3665 } else {
3666 continue;
3667 }
3668 }
3669
3670 if (up % 2 === 0) {
3671 return false;
3672 } else {
3673 return true;
3674 }
3675 };
3676 var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3677 var transformedPoints = new Array(basePoints.length); // Gives negative angle
3678
3679 var angle;
3680
3681 if (direction[0] != null) {
3682 angle = Math.atan(direction[1] / direction[0]);
3683
3684 if (direction[0] < 0) {
3685 angle = angle + Math.PI / 2;
3686 } else {
3687 angle = -angle - Math.PI / 2;
3688 }
3689 } else {
3690 angle = direction;
3691 }
3692
3693 var cos = Math.cos(-angle);
3694 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
3695
3696 for (var i = 0; i < transformedPoints.length / 2; i++) {
3697 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3698 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3699 transformedPoints[i * 2] += centerX;
3700 transformedPoints[i * 2 + 1] += centerY;
3701 }
3702
3703 var points;
3704
3705 if (padding > 0) {
3706 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3707 points = joinLines(expandedLineSet);
3708 } else {
3709 points = transformedPoints;
3710 }
3711
3712 return pointInsidePolygonPoints(x, y, points);
3713 };
3714 var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3715 var cutPolygonPoints = new Array(basePoints.length);
3716 var halfW = width / 2;
3717 var halfH = height / 2;
3718 var cornerRadius = getRoundPolygonRadius(width, height);
3719 var squaredCornerRadius = cornerRadius * cornerRadius;
3720
3721 for (var i = 0; i < basePoints.length / 4; i++) {
3722 var sourceUv = void 0,
3723 destUv = void 0;
3724
3725 if (i === 0) {
3726 sourceUv = basePoints.length - 2;
3727 } else {
3728 sourceUv = i * 4 - 2;
3729 }
3730
3731 destUv = i * 4 + 2;
3732 var px = centerX + halfW * basePoints[i * 4];
3733 var py = centerY + halfH * basePoints[i * 4 + 1];
3734 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3735 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3736 var cp0x = px - offset * basePoints[sourceUv];
3737 var cp0y = py - offset * basePoints[sourceUv + 1];
3738 var cp1x = px + offset * basePoints[destUv];
3739 var cp1y = py + offset * basePoints[destUv + 1];
3740 cutPolygonPoints[i * 4] = cp0x;
3741 cutPolygonPoints[i * 4 + 1] = cp0y;
3742 cutPolygonPoints[i * 4 + 2] = cp1x;
3743 cutPolygonPoints[i * 4 + 3] = cp1y;
3744 var orthx = basePoints[sourceUv + 1];
3745 var orthy = -basePoints[sourceUv];
3746 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3747
3748 if (cosAlpha < 0) {
3749 orthx *= -1;
3750 orthy *= -1;
3751 }
3752
3753 var cx = cp0x + orthx * cornerRadius;
3754 var cy = cp0y + orthy * cornerRadius;
3755 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3756
3757 if (squaredDistance <= squaredCornerRadius) {
3758 return true;
3759 }
3760 }
3761
3762 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3763 };
3764 var joinLines = function joinLines(lineSet) {
3765 var vertices = new Array(lineSet.length / 2);
3766 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3767 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3768
3769 for (var i = 0; i < lineSet.length / 4; i++) {
3770 currentLineStartX = lineSet[i * 4];
3771 currentLineStartY = lineSet[i * 4 + 1];
3772 currentLineEndX = lineSet[i * 4 + 2];
3773 currentLineEndY = lineSet[i * 4 + 3];
3774
3775 if (i < lineSet.length / 4 - 1) {
3776 nextLineStartX = lineSet[(i + 1) * 4];
3777 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3778 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3779 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3780 } else {
3781 nextLineStartX = lineSet[0];
3782 nextLineStartY = lineSet[1];
3783 nextLineEndX = lineSet[2];
3784 nextLineEndY = lineSet[3];
3785 }
3786
3787 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3788 vertices[i * 2] = intersection[0];
3789 vertices[i * 2 + 1] = intersection[1];
3790 }
3791
3792 return vertices;
3793 };
3794 var expandPolygon = function expandPolygon(points, pad) {
3795 var expandedLineSet = new Array(points.length * 2);
3796 var currentPointX, currentPointY, nextPointX, nextPointY;
3797
3798 for (var i = 0; i < points.length / 2; i++) {
3799 currentPointX = points[i * 2];
3800 currentPointY = points[i * 2 + 1];
3801
3802 if (i < points.length / 2 - 1) {
3803 nextPointX = points[(i + 1) * 2];
3804 nextPointY = points[(i + 1) * 2 + 1];
3805 } else {
3806 nextPointX = points[0];
3807 nextPointY = points[1];
3808 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3809 // Assume CCW polygon winding
3810
3811
3812 var offsetX = nextPointY - currentPointY;
3813 var offsetY = -(nextPointX - currentPointX); // Normalize
3814
3815 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3816 var normalizedOffsetX = offsetX / offsetLength;
3817 var normalizedOffsetY = offsetY / offsetLength;
3818 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3819 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3820 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3821 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3822 }
3823
3824 return expandedLineSet;
3825 };
3826 var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3827 var dispX = centerX - x;
3828 var dispY = centerY - y;
3829 dispX /= ellipseWradius;
3830 dispY /= ellipseHradius;
3831 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3832 var newLength = len - 1;
3833
3834 if (newLength < 0) {
3835 return [];
3836 }
3837
3838 var lenProportion = newLength / len;
3839 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3840 };
3841 var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3842 x -= centerX;
3843 y -= centerY;
3844 x /= width / 2 + padding;
3845 y /= height / 2 + padding;
3846 return x * x + y * y <= 1;
3847 }; // Returns intersections of increasing distance from line's start point
3848
3849 var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3850 // Calculate d, direction vector of line
3851 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3852
3853 var f = [x1 - centerX, y1 - centerY];
3854 var a = d[0] * d[0] + d[1] * d[1];
3855 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3856 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3857 var discriminant = b * b - 4 * a * c;
3858
3859 if (discriminant < 0) {
3860 return [];
3861 }
3862
3863 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3864 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3865 var tMin = Math.min(t1, t2);
3866 var tMax = Math.max(t1, t2);
3867 var inRangeParams = [];
3868
3869 if (tMin >= 0 && tMin <= 1) {
3870 inRangeParams.push(tMin);
3871 }
3872
3873 if (tMax >= 0 && tMax <= 1) {
3874 inRangeParams.push(tMax);
3875 }
3876
3877 if (inRangeParams.length === 0) {
3878 return [];
3879 }
3880
3881 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3882 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3883
3884 if (inRangeParams.length > 1) {
3885 if (inRangeParams[0] == inRangeParams[1]) {
3886 return [nearIntersectionX, nearIntersectionY];
3887 } else {
3888 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3889 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3890 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3891 }
3892 } else {
3893 return [nearIntersectionX, nearIntersectionY];
3894 }
3895 };
3896 var midOfThree = function midOfThree(a, b, c) {
3897 if (b <= a && a <= c || c <= a && a <= b) {
3898 return a;
3899 } else if (a <= b && b <= c || c <= b && b <= a) {
3900 return b;
3901 } else {
3902 return c;
3903 }
3904 }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3905
3906 var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3907 var dx13 = x1 - x3;
3908 var dx21 = x2 - x1;
3909 var dx43 = x4 - x3;
3910 var dy13 = y1 - y3;
3911 var dy21 = y2 - y1;
3912 var dy43 = y4 - y3;
3913 var ua_t = dx43 * dy13 - dy43 * dx13;
3914 var ub_t = dx21 * dy13 - dy21 * dx13;
3915 var u_b = dy43 * dx21 - dx43 * dy21;
3916
3917 if (u_b !== 0) {
3918 var ua = ua_t / u_b;
3919 var ub = ub_t / u_b;
3920 var flptThreshold = 0.001;
3921
3922 var _min = 0 - flptThreshold;
3923
3924 var _max = 1 + flptThreshold;
3925
3926 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3927 return [x1 + ua * dx21, y1 + ua * dy21];
3928 } else {
3929 if (!infiniteLines) {
3930 return [];
3931 } else {
3932 return [x1 + ua * dx21, y1 + ua * dy21];
3933 }
3934 }
3935 } else {
3936 if (ua_t === 0 || ub_t === 0) {
3937 // Parallel, coincident lines. Check if overlap
3938 // Check endpoint of second line
3939 if (midOfThree(x1, x2, x4) === x4) {
3940 return [x4, y4];
3941 } // Check start point of second line
3942
3943
3944 if (midOfThree(x1, x2, x3) === x3) {
3945 return [x3, y3];
3946 } // Endpoint of first line
3947
3948
3949 if (midOfThree(x3, x4, x2) === x2) {
3950 return [x2, y2];
3951 }
3952
3953 return [];
3954 } else {
3955 // Parallel, non-coincident
3956 return [];
3957 }
3958 }
3959 }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3960 // intersect a node polygon (pts transformed)
3961 //
3962 // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3963 // intersect the points (no transform)
3964
3965 var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3966 var intersections = [];
3967 var intersection;
3968 var transformedPoints = new Array(basePoints.length);
3969 var doTransform = true;
3970
3971 if (width == null) {
3972 doTransform = false;
3973 }
3974
3975 var points;
3976
3977 if (doTransform) {
3978 for (var i = 0; i < transformedPoints.length / 2; i++) {
3979 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3980 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3981 }
3982
3983 if (padding > 0) {
3984 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3985 points = joinLines(expandedLineSet);
3986 } else {
3987 points = transformedPoints;
3988 }
3989 } else {
3990 points = basePoints;
3991 }
3992
3993 var currentX, currentY, nextX, nextY;
3994
3995 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3996 currentX = points[_i2 * 2];
3997 currentY = points[_i2 * 2 + 1];
3998
3999 if (_i2 < points.length / 2 - 1) {
4000 nextX = points[(_i2 + 1) * 2];
4001 nextY = points[(_i2 + 1) * 2 + 1];
4002 } else {
4003 nextX = points[0];
4004 nextY = points[1];
4005 }
4006
4007 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
4008
4009 if (intersection.length !== 0) {
4010 intersections.push(intersection[0], intersection[1]);
4011 }
4012 }
4013
4014 return intersections;
4015 };
4016 var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
4017 var intersections = [];
4018 var intersection;
4019 var lines = new Array(basePoints.length);
4020 var halfW = width / 2;
4021 var halfH = height / 2;
4022 var cornerRadius = getRoundPolygonRadius(width, height);
4023
4024 for (var i = 0; i < basePoints.length / 4; i++) {
4025 var sourceUv = void 0,
4026 destUv = void 0;
4027
4028 if (i === 0) {
4029 sourceUv = basePoints.length - 2;
4030 } else {
4031 sourceUv = i * 4 - 2;
4032 }
4033
4034 destUv = i * 4 + 2;
4035 var px = centerX + halfW * basePoints[i * 4];
4036 var py = centerY + halfH * basePoints[i * 4 + 1];
4037 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
4038 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
4039 var cp0x = px - offset * basePoints[sourceUv];
4040 var cp0y = py - offset * basePoints[sourceUv + 1];
4041 var cp1x = px + offset * basePoints[destUv];
4042 var cp1y = py + offset * basePoints[destUv + 1];
4043
4044 if (i === 0) {
4045 lines[basePoints.length - 2] = cp0x;
4046 lines[basePoints.length - 1] = cp0y;
4047 } else {
4048 lines[i * 4 - 2] = cp0x;
4049 lines[i * 4 - 1] = cp0y;
4050 }
4051
4052 lines[i * 4] = cp1x;
4053 lines[i * 4 + 1] = cp1y;
4054 var orthx = basePoints[sourceUv + 1];
4055 var orthy = -basePoints[sourceUv];
4056 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
4057
4058 if (cosAlpha < 0) {
4059 orthx *= -1;
4060 orthy *= -1;
4061 }
4062
4063 var cx = cp0x + orthx * cornerRadius;
4064 var cy = cp0y + orthy * cornerRadius;
4065 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
4066
4067 if (intersection.length !== 0) {
4068 intersections.push(intersection[0], intersection[1]);
4069 }
4070 }
4071
4072 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
4073 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
4074
4075 if (intersection.length !== 0) {
4076 intersections.push(intersection[0], intersection[1]);
4077 }
4078 }
4079
4080 if (intersections.length > 2) {
4081 var lowestIntersection = [intersections[0], intersections[1]];
4082 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
4083
4084 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
4085 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
4086
4087 if (squaredDistance <= lowestSquaredDistance) {
4088 lowestIntersection[0] = intersections[_i4 * 2];
4089 lowestIntersection[1] = intersections[_i4 * 2 + 1];
4090 lowestSquaredDistance = squaredDistance;
4091 }
4092 }
4093
4094 return lowestIntersection;
4095 }
4096
4097 return intersections;
4098 };
4099 var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
4100 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
4101 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
4102 var lenRatio = (length - amount) / length;
4103
4104 if (lenRatio < 0) {
4105 lenRatio = 0.00001;
4106 }
4107
4108 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
4109 };
4110 var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
4111 var points = generateUnitNgonPoints(sides, rotationRadians);
4112 points = fitPolygonToSquare(points);
4113 return points;
4114 };
4115 var fitPolygonToSquare = function fitPolygonToSquare(points) {
4116 var x, y;
4117 var sides = points.length / 2;
4118 var minX = Infinity,
4119 minY = Infinity,
4120 maxX = -Infinity,
4121 maxY = -Infinity;
4122
4123 for (var i = 0; i < sides; i++) {
4124 x = points[2 * i];
4125 y = points[2 * i + 1];
4126 minX = Math.min(minX, x);
4127 maxX = Math.max(maxX, x);
4128 minY = Math.min(minY, y);
4129 maxY = Math.max(maxY, y);
4130 } // stretch factors
4131
4132
4133 var sx = 2 / (maxX - minX);
4134 var sy = 2 / (maxY - minY);
4135
4136 for (var _i5 = 0; _i5 < sides; _i5++) {
4137 x = points[2 * _i5] = points[2 * _i5] * sx;
4138 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
4139 minX = Math.min(minX, x);
4140 maxX = Math.max(maxX, x);
4141 minY = Math.min(minY, y);
4142 maxY = Math.max(maxY, y);
4143 }
4144
4145 if (minY < -1) {
4146 for (var _i6 = 0; _i6 < sides; _i6++) {
4147 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
4148 }
4149 }
4150
4151 return points;
4152 };
4153 var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4154 var increment = 1.0 / sides * 2 * Math.PI;
4155 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4156 startAngle += rotationRadians;
4157 var points = new Array(sides * 2);
4158 var currentAngle;
4159
4160 for (var i = 0; i < sides; i++) {
4161 currentAngle = i * increment + startAngle;
4162 points[2 * i] = Math.cos(currentAngle); // x
4163
4164 points[2 * i + 1] = Math.sin(-currentAngle); // y
4165 }
4166
4167 return points;
4168 }; // Set the default radius, unless half of width or height is smaller than default
4169
4170 var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4171 return Math.min(width / 4, height / 4, 8);
4172 }; // Set the default radius
4173
4174 var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4175 return Math.min(width / 10, height / 10, 8);
4176 };
4177 var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4178 return 8;
4179 };
4180 var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4181 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4182 }; // get curve width, height, and control point position offsets as a percentage of node height / width
4183
4184 var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4185 return {
4186 heightOffset: Math.min(15, 0.05 * height),
4187 widthOffset: Math.min(100, 0.25 * width),
4188 ctrlPtOffsetPct: 0.05
4189 };
4190 };
4191
4192 var pageRankDefaults = defaults({
4193 dampingFactor: 0.8,
4194 precision: 0.000001,
4195 iterations: 200,
4196 weight: function weight(edge) {
4197 return 1;
4198 }
4199 });
4200 var elesfn$7 = {
4201 pageRank: function pageRank(options) {
4202 var _pageRankDefaults = pageRankDefaults(options),
4203 dampingFactor = _pageRankDefaults.dampingFactor,
4204 precision = _pageRankDefaults.precision,
4205 iterations = _pageRankDefaults.iterations,
4206 weight = _pageRankDefaults.weight;
4207
4208 var cy = this._private.cy;
4209
4210 var _this$byGroup = this.byGroup(),
4211 nodes = _this$byGroup.nodes,
4212 edges = _this$byGroup.edges;
4213
4214 var numNodes = nodes.length;
4215 var numNodesSqd = numNodes * numNodes;
4216 var numEdges = edges.length; // Construct transposed adjacency matrix
4217 // First lets have a zeroed matrix of the right size
4218 // We'll also keep track of the sum of each column
4219
4220 var matrix = new Array(numNodesSqd);
4221 var columnSum = new Array(numNodes);
4222 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
4223
4224 for (var i = 0; i < numNodes; i++) {
4225 for (var j = 0; j < numNodes; j++) {
4226 var n = i * numNodes + j;
4227 matrix[n] = 0;
4228 }
4229
4230 columnSum[i] = 0;
4231 } // Now, process edges
4232
4233
4234 for (var _i = 0; _i < numEdges; _i++) {
4235 var edge = edges[_i];
4236 var srcId = edge.data('source');
4237 var tgtId = edge.data('target'); // Don't include loops in the matrix
4238
4239 if (srcId === tgtId) {
4240 continue;
4241 }
4242
4243 var s = nodes.indexOfId(srcId);
4244 var t = nodes.indexOfId(tgtId);
4245 var w = weight(edge);
4246
4247 var _n = t * numNodes + s; // Update matrix
4248
4249
4250 matrix[_n] += w; // Update column sum
4251
4252 columnSum[s] += w;
4253 } // Add additional probability based on damping factor
4254 // Also, take into account columns that have sum = 0
4255
4256
4257 var p = 1.0 / numNodes + additionalProb; // Shorthand
4258 // Traverse matrix, column by column
4259
4260 for (var _j = 0; _j < numNodes; _j++) {
4261 if (columnSum[_j] === 0) {
4262 // No 'links' out from node jth, assume equal probability for each possible node
4263 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4264 var _n2 = _i2 * numNodes + _j;
4265
4266 matrix[_n2] = p;
4267 }
4268 } else {
4269 // Node jth has outgoing link, compute normalized probabilities
4270 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4271 var _n3 = _i3 * numNodes + _j;
4272
4273 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4274 }
4275 }
4276 } // Compute dominant eigenvector using power method
4277
4278
4279 var eigenvector = new Array(numNodes);
4280 var temp = new Array(numNodes);
4281 var previous; // Start with a vector of all 1's
4282 // Also, initialize a null vector which will be used as shorthand
4283
4284 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4285 eigenvector[_i4] = 1;
4286 }
4287
4288 for (var iter = 0; iter < iterations; iter++) {
4289 // Temp array with all 0's
4290 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4291 temp[_i5] = 0;
4292 } // Multiply matrix with previous result
4293
4294
4295 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4296 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4297 var _n4 = _i6 * numNodes + _j2;
4298
4299 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4300 }
4301 }
4302
4303 inPlaceSumNormalize(temp);
4304 previous = eigenvector;
4305 eigenvector = temp;
4306 temp = previous;
4307 var diff = 0; // Compute difference (squared module) of both vectors
4308
4309 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4310 var delta = previous[_i7] - eigenvector[_i7];
4311 diff += delta * delta;
4312 } // If difference is less than the desired threshold, stop iterating
4313
4314
4315 if (diff < precision) {
4316 break;
4317 }
4318 } // Construct result
4319
4320
4321 var res = {
4322 rank: function rank(node) {
4323 node = cy.collection(node)[0];
4324 return eigenvector[nodes.indexOf(node)];
4325 }
4326 };
4327 return res;
4328 } // pageRank
4329
4330 }; // elesfn
4331
4332 var defaults$1 = defaults({
4333 root: null,
4334 weight: function weight(edge) {
4335 return 1;
4336 },
4337 directed: false,
4338 alpha: 0
4339 });
4340 var elesfn$8 = {
4341 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4342 options = defaults$1(options);
4343 var cy = this.cy();
4344 var nodes = this.nodes();
4345 var numNodes = nodes.length;
4346
4347 if (!options.directed) {
4348 var degrees = {};
4349 var maxDegree = 0;
4350
4351 for (var i = 0; i < numNodes; i++) {
4352 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
4353
4354 options.root = node;
4355 var currDegree = this.degreeCentrality(options);
4356
4357 if (maxDegree < currDegree.degree) {
4358 maxDegree = currDegree.degree;
4359 }
4360
4361 degrees[node.id()] = currDegree.degree;
4362 }
4363
4364 return {
4365 degree: function degree(node) {
4366 if (maxDegree === 0) {
4367 return 0;
4368 }
4369
4370 if (string(node)) {
4371 // from is a selector string
4372 node = cy.filter(node);
4373 }
4374
4375 return degrees[node.id()] / maxDegree;
4376 }
4377 };
4378 } else {
4379 var indegrees = {};
4380 var outdegrees = {};
4381 var maxIndegree = 0;
4382 var maxOutdegree = 0;
4383
4384 for (var _i = 0; _i < numNodes; _i++) {
4385 var _node = nodes[_i];
4386
4387 var id = _node.id(); // add current node to the current options object and call degreeCentrality
4388
4389
4390 options.root = _node;
4391
4392 var _currDegree = this.degreeCentrality(options);
4393
4394 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4395 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4396 indegrees[id] = _currDegree.indegree;
4397 outdegrees[id] = _currDegree.outdegree;
4398 }
4399
4400 return {
4401 indegree: function indegree(node) {
4402 if (maxIndegree == 0) {
4403 return 0;
4404 }
4405
4406 if (string(node)) {
4407 // from is a selector string
4408 node = cy.filter(node);
4409 }
4410
4411 return indegrees[node.id()] / maxIndegree;
4412 },
4413 outdegree: function outdegree(node) {
4414 if (maxOutdegree === 0) {
4415 return 0;
4416 }
4417
4418 if (string(node)) {
4419 // from is a selector string
4420 node = cy.filter(node);
4421 }
4422
4423 return outdegrees[node.id()] / maxOutdegree;
4424 }
4425 };
4426 }
4427 },
4428 // degreeCentralityNormalized
4429 // Implemented from the algorithm in Opsahl's paper
4430 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4431 // check the heading 2 "Degree"
4432 degreeCentrality: function degreeCentrality(options) {
4433 options = defaults$1(options);
4434 var cy = this.cy();
4435 var callingEles = this;
4436 var _options = options,
4437 root = _options.root,
4438 weight = _options.weight,
4439 directed = _options.directed,
4440 alpha = _options.alpha;
4441 root = cy.collection(root)[0];
4442
4443 if (!directed) {
4444 var connEdges = root.connectedEdges().intersection(callingEles);
4445 var k = connEdges.length;
4446 var s = 0; // Now, sum edge weights
4447
4448 for (var i = 0; i < connEdges.length; i++) {
4449 s += weight(connEdges[i]);
4450 }
4451
4452 return {
4453 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4454 };
4455 } else {
4456 var edges = root.connectedEdges();
4457 var incoming = edges.filter(function (edge) {
4458 return edge.target().same(root) && callingEles.has(edge);
4459 });
4460 var outgoing = edges.filter(function (edge) {
4461 return edge.source().same(root) && callingEles.has(edge);
4462 });
4463 var k_in = incoming.length;
4464 var k_out = outgoing.length;
4465 var s_in = 0;
4466 var s_out = 0; // Now, sum incoming edge weights
4467
4468 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4469 s_in += weight(incoming[_i2]);
4470 } // Now, sum outgoing edge weights
4471
4472
4473 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4474 s_out += weight(outgoing[_i3]);
4475 }
4476
4477 return {
4478 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4479 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4480 };
4481 }
4482 } // degreeCentrality
4483
4484 }; // elesfn
4485 // nice, short mathemathical alias
4486
4487 elesfn$8.dc = elesfn$8.degreeCentrality;
4488 elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
4489
4490 var defaults$2 = defaults({
4491 harmonic: true,
4492 weight: function weight() {
4493 return 1;
4494 },
4495 directed: false,
4496 root: null
4497 });
4498 var elesfn$9 = {
4499 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4500 var _defaults = defaults$2(options),
4501 harmonic = _defaults.harmonic,
4502 weight = _defaults.weight,
4503 directed = _defaults.directed;
4504
4505 var cy = this.cy();
4506 var closenesses = {};
4507 var maxCloseness = 0;
4508 var nodes = this.nodes();
4509 var fw = this.floydWarshall({
4510 weight: weight,
4511 directed: directed
4512 }); // Compute closeness for every node and find the maximum closeness
4513
4514 for (var i = 0; i < nodes.length; i++) {
4515 var currCloseness = 0;
4516 var node_i = nodes[i];
4517
4518 for (var j = 0; j < nodes.length; j++) {
4519 if (i !== j) {
4520 var d = fw.distance(node_i, nodes[j]);
4521
4522 if (harmonic) {
4523 currCloseness += 1 / d;
4524 } else {
4525 currCloseness += d;
4526 }
4527 }
4528 }
4529
4530 if (!harmonic) {
4531 currCloseness = 1 / currCloseness;
4532 }
4533
4534 if (maxCloseness < currCloseness) {
4535 maxCloseness = currCloseness;
4536 }
4537
4538 closenesses[node_i.id()] = currCloseness;
4539 }
4540
4541 return {
4542 closeness: function closeness(node) {
4543 if (maxCloseness == 0) {
4544 return 0;
4545 }
4546
4547 if (string(node)) {
4548 // from is a selector string
4549 node = cy.filter(node)[0].id();
4550 } else {
4551 // from is a node
4552 node = node.id();
4553 }
4554
4555 return closenesses[node] / maxCloseness;
4556 }
4557 };
4558 },
4559 // Implemented from pseudocode from wikipedia
4560 closenessCentrality: function closenessCentrality(options) {
4561 var _defaults2 = defaults$2(options),
4562 root = _defaults2.root,
4563 weight = _defaults2.weight,
4564 directed = _defaults2.directed,
4565 harmonic = _defaults2.harmonic;
4566
4567 root = this.filter(root)[0]; // we need distance from this node to every other node
4568
4569 var dijkstra = this.dijkstra({
4570 root: root,
4571 weight: weight,
4572 directed: directed
4573 });
4574 var totalDistance = 0;
4575 var nodes = this.nodes();
4576
4577 for (var i = 0; i < nodes.length; i++) {
4578 var n = nodes[i];
4579
4580 if (!n.same(root)) {
4581 var d = dijkstra.distanceTo(n);
4582
4583 if (harmonic) {
4584 totalDistance += 1 / d;
4585 } else {
4586 totalDistance += d;
4587 }
4588 }
4589 }
4590
4591 return harmonic ? totalDistance : 1 / totalDistance;
4592 } // closenessCentrality
4593
4594 }; // elesfn
4595 // nice, short mathemathical alias
4596
4597 elesfn$9.cc = elesfn$9.closenessCentrality;
4598 elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
4599
4600 var defaults$3 = defaults({
4601 weight: null,
4602 directed: false
4603 });
4604 var elesfn$a = {
4605 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4606 betweennessCentrality: function betweennessCentrality(options) {
4607 var _defaults = defaults$3(options),
4608 directed = _defaults.directed,
4609 weight = _defaults.weight;
4610
4611 var weighted = weight != null;
4612 var cy = this.cy(); // starting
4613
4614 var V = this.nodes();
4615 var A = {};
4616 var _C = {};
4617 var max = 0;
4618 var C = {
4619 set: function set(key, val) {
4620 _C[key] = val;
4621
4622 if (val > max) {
4623 max = val;
4624 }
4625 },
4626 get: function get(key) {
4627 return _C[key];
4628 }
4629 }; // A contains the neighborhoods of every node
4630
4631 for (var i = 0; i < V.length; i++) {
4632 var v = V[i];
4633 var vid = v.id();
4634
4635 if (directed) {
4636 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4637 } else {
4638 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4639 }
4640
4641 C.set(vid, 0);
4642 }
4643
4644 var _loop = function _loop(s) {
4645 var sid = V[s].id();
4646 var S = []; // stack
4647
4648 var P = {};
4649 var g = {};
4650 var d = {};
4651 var Q = new heap$1(function (a, b) {
4652 return d[a] - d[b];
4653 }); // queue
4654 // init dictionaries
4655
4656 for (var _i = 0; _i < V.length; _i++) {
4657 var _vid = V[_i].id();
4658
4659 P[_vid] = [];
4660 g[_vid] = 0;
4661 d[_vid] = Infinity;
4662 }
4663
4664 g[sid] = 1; // sigma
4665
4666 d[sid] = 0; // distance to s
4667
4668 Q.push(sid);
4669
4670 while (!Q.empty()) {
4671 var _v = Q.pop();
4672
4673 S.push(_v);
4674
4675 if (weighted) {
4676 for (var j = 0; j < A[_v].length; j++) {
4677 var w = A[_v][j];
4678 var vEle = cy.getElementById(_v);
4679 var edge = void 0;
4680
4681 if (vEle.edgesTo(w).length > 0) {
4682 edge = vEle.edgesTo(w)[0];
4683 } else {
4684 edge = w.edgesTo(vEle)[0];
4685 }
4686
4687 var edgeWeight = weight(edge);
4688 w = w.id();
4689
4690 if (d[w] > d[_v] + edgeWeight) {
4691 d[w] = d[_v] + edgeWeight;
4692
4693 if (Q.nodes.indexOf(w) < 0) {
4694 //if w is not in Q
4695 Q.push(w);
4696 } else {
4697 // update position if w is in Q
4698 Q.updateItem(w);
4699 }
4700
4701 g[w] = 0;
4702 P[w] = [];
4703 }
4704
4705 if (d[w] == d[_v] + edgeWeight) {
4706 g[w] = g[w] + g[_v];
4707 P[w].push(_v);
4708 }
4709 }
4710 } else {
4711 for (var _j = 0; _j < A[_v].length; _j++) {
4712 var _w = A[_v][_j].id();
4713
4714 if (d[_w] == Infinity) {
4715 Q.push(_w);
4716 d[_w] = d[_v] + 1;
4717 }
4718
4719 if (d[_w] == d[_v] + 1) {
4720 g[_w] = g[_w] + g[_v];
4721
4722 P[_w].push(_v);
4723 }
4724 }
4725 }
4726 }
4727
4728 var e = {};
4729
4730 for (var _i2 = 0; _i2 < V.length; _i2++) {
4731 e[V[_i2].id()] = 0;
4732 }
4733
4734 while (S.length > 0) {
4735 var _w2 = S.pop();
4736
4737 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4738 var _v2 = P[_w2][_j2];
4739 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4740 }
4741
4742 if (_w2 != V[s].id()) {
4743 C.set(_w2, C.get(_w2) + e[_w2]);
4744 }
4745 }
4746 };
4747
4748 for (var s = 0; s < V.length; s++) {
4749 _loop(s);
4750 }
4751
4752 var ret = {
4753 betweenness: function betweenness(node) {
4754 var id = cy.collection(node).id();
4755 return C.get(id);
4756 },
4757 betweennessNormalized: function betweennessNormalized(node) {
4758 if (max == 0) {
4759 return 0;
4760 }
4761
4762 var id = cy.collection(node).id();
4763 return C.get(id) / max;
4764 }
4765 }; // alias
4766
4767 ret.betweennessNormalised = ret.betweennessNormalized;
4768 return ret;
4769 } // betweennessCentrality
4770
4771 }; // elesfn
4772 // nice, short mathemathical alias
4773
4774 elesfn$a.bc = elesfn$a.betweennessCentrality;
4775
4776 // Implemented by Zoe Xi @zoexi for GSOC 2016
4777 /* eslint-disable no-unused-vars */
4778
4779 var defaults$4 = defaults({
4780 expandFactor: 2,
4781 // affects time of computation and cluster granularity to some extent: M * M
4782 inflateFactor: 2,
4783 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4784 multFactor: 1,
4785 // optional self loops for each node. Use a neutral value to improve cluster computations.
4786 maxIterations: 20,
4787 // maximum number of iterations of the MCL algorithm in a single run
4788 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4789 function (edge) {
4790 return 1;
4791 }]
4792 });
4793 /* eslint-enable */
4794
4795 var setOptions = function setOptions(options) {
4796 return defaults$4(options);
4797 };
4798 /* eslint-enable */
4799
4800
4801 var getSimilarity = function getSimilarity(edge, attributes) {
4802 var total = 0;
4803
4804 for (var i = 0; i < attributes.length; i++) {
4805 total += attributes[i](edge);
4806 }
4807
4808 return total;
4809 };
4810
4811 var addLoops = function addLoops(M, n, val) {
4812 for (var i = 0; i < n; i++) {
4813 M[i * n + i] = val;
4814 }
4815 };
4816
4817 var normalize = function normalize(M, n) {
4818 var sum;
4819
4820 for (var col = 0; col < n; col++) {
4821 sum = 0;
4822
4823 for (var row = 0; row < n; row++) {
4824 sum += M[row * n + col];
4825 }
4826
4827 for (var _row = 0; _row < n; _row++) {
4828 M[_row * n + col] = M[_row * n + col] / sum;
4829 }
4830 }
4831 }; // TODO: blocked matrix multiplication?
4832
4833
4834 var mmult = function mmult(A, B, n) {
4835 var C = new Array(n * n);
4836
4837 for (var i = 0; i < n; i++) {
4838 for (var j = 0; j < n; j++) {
4839 C[i * n + j] = 0;
4840 }
4841
4842 for (var k = 0; k < n; k++) {
4843 for (var _j = 0; _j < n; _j++) {
4844 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4845 }
4846 }
4847 }
4848
4849 return C;
4850 };
4851
4852 var expand = function expand(M, n, expandFactor
4853 /** power **/
4854 ) {
4855 var _M = M.slice(0);
4856
4857 for (var p = 1; p < expandFactor; p++) {
4858 M = mmult(M, _M, n);
4859 }
4860
4861 return M;
4862 };
4863
4864 var inflate = function inflate(M, n, inflateFactor
4865 /** r **/
4866 ) {
4867 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4868
4869
4870 for (var i = 0; i < n * n; i++) {
4871 _M[i] = Math.pow(M[i], inflateFactor);
4872 }
4873
4874 normalize(_M, n);
4875 return _M;
4876 };
4877
4878 var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4879 // Check that both matrices have the same elements (i,j)
4880 for (var i = 0; i < n2; i++) {
4881 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4882
4883 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4884
4885 if (v1 !== v2) {
4886 return false;
4887 }
4888 }
4889
4890 return true;
4891 };
4892
4893 var assign = function assign(M, n, nodes, cy) {
4894 var clusters = [];
4895
4896 for (var i = 0; i < n; i++) {
4897 var cluster = [];
4898
4899 for (var j = 0; j < n; j++) {
4900 // Row-wise attractors and elements that they attract belong in same cluster
4901 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4902 cluster.push(nodes[j]);
4903 }
4904 }
4905
4906 if (cluster.length !== 0) {
4907 clusters.push(cy.collection(cluster));
4908 }
4909 }
4910
4911 return clusters;
4912 };
4913
4914 var isDuplicate = function isDuplicate(c1, c2) {
4915 for (var i = 0; i < c1.length; i++) {
4916 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4917 return false;
4918 }
4919 }
4920
4921 return true;
4922 };
4923
4924 var removeDuplicates = function removeDuplicates(clusters) {
4925 for (var i = 0; i < clusters.length; i++) {
4926 for (var j = 0; j < clusters.length; j++) {
4927 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4928 clusters.splice(j, 1);
4929 }
4930 }
4931 }
4932
4933 return clusters;
4934 };
4935
4936 var markovClustering = function markovClustering(options) {
4937 var nodes = this.nodes();
4938 var edges = this.edges();
4939 var cy = this.cy(); // Set parameters of algorithm:
4940
4941 var opts = setOptions(options); // Map each node to its position in node array
4942
4943 var id2position = {};
4944
4945 for (var i = 0; i < nodes.length; i++) {
4946 id2position[nodes[i].id()] = i;
4947 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4948
4949
4950 var n = nodes.length,
4951 n2 = n * n;
4952
4953 var M = new Array(n2),
4954 _M;
4955
4956 for (var _i = 0; _i < n2; _i++) {
4957 M[_i] = 0;
4958 }
4959
4960 for (var e = 0; e < edges.length; e++) {
4961 var edge = edges[e];
4962 var _i2 = id2position[edge.source().id()];
4963 var j = id2position[edge.target().id()];
4964 var sim = getSimilarity(edge, opts.attributes);
4965 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4966
4967 M[j * n + _i2] += sim;
4968 } // Begin Markov cluster algorithm
4969 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4970
4971
4972 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4973
4974 normalize(M, n);
4975 var isStillMoving = true;
4976 var iterations = 0;
4977
4978 while (isStillMoving && iterations < opts.maxIterations) {
4979 isStillMoving = false; // Step 3:
4980
4981 _M = expand(M, n, opts.expandFactor); // Step 4:
4982
4983 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4984
4985 if (!hasConverged(M, _M, n2, 4)) {
4986 isStillMoving = true;
4987 }
4988
4989 iterations++;
4990 } // Build clusters from matrix
4991
4992
4993 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4994
4995 clusters = removeDuplicates(clusters);
4996 return clusters;
4997 };
4998
4999 var markovClustering$1 = {
5000 markovClustering: markovClustering,
5001 mcl: markovClustering
5002 };
5003
5004 // Common distance metrics for clustering algorithms
5005
5006 var identity = function identity(x) {
5007 return x;
5008 };
5009
5010 var absDiff = function absDiff(p, q) {
5011 return Math.abs(q - p);
5012 };
5013
5014 var addAbsDiff = function addAbsDiff(total, p, q) {
5015 return total + absDiff(p, q);
5016 };
5017
5018 var addSquaredDiff = function addSquaredDiff(total, p, q) {
5019 return total + Math.pow(q - p, 2);
5020 };
5021
5022 var sqrt = function sqrt(x) {
5023 return Math.sqrt(x);
5024 };
5025
5026 var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
5027 return Math.max(currentMax, absDiff(p, q));
5028 };
5029
5030 var getDistance = function getDistance(length, getP, getQ, init, visit) {
5031 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
5032 var ret = init;
5033 var p, q;
5034
5035 for (var dim = 0; dim < length; dim++) {
5036 p = getP(dim);
5037 q = getQ(dim);
5038 ret = visit(ret, p, q);
5039 }
5040
5041 return post(ret);
5042 };
5043
5044 var distances = {
5045 euclidean: function euclidean(length, getP, getQ) {
5046 if (length >= 2) {
5047 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
5048 } else {
5049 // for single attr case, more efficient to avoid sqrt
5050 return getDistance(length, getP, getQ, 0, addAbsDiff);
5051 }
5052 },
5053 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
5054 return getDistance(length, getP, getQ, 0, addSquaredDiff);
5055 },
5056 manhattan: function manhattan(length, getP, getQ) {
5057 return getDistance(length, getP, getQ, 0, addAbsDiff);
5058 },
5059 max: function max(length, getP, getQ) {
5060 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
5061 }
5062 }; // in case the user accidentally doesn't use camel case
5063
5064 distances['squared-euclidean'] = distances['squaredEuclidean'];
5065 distances['squaredeuclidean'] = distances['squaredEuclidean'];
5066 function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
5067 var impl;
5068
5069 if (fn(method)) {
5070 impl = method;
5071 } else {
5072 impl = distances[method] || distances.euclidean;
5073 }
5074
5075 if (length === 0 && fn(method)) {
5076 return impl(nodeP, nodeQ);
5077 } else {
5078 return impl(length, getP, getQ, nodeP, nodeQ);
5079 }
5080 }
5081
5082 var defaults$5 = defaults({
5083 k: 2,
5084 m: 2,
5085 sensitivityThreshold: 0.0001,
5086 distance: 'euclidean',
5087 maxIterations: 10,
5088 attributes: [],
5089 testMode: false,
5090 testCentroids: null
5091 });
5092
5093 var setOptions$1 = function setOptions(options) {
5094 return defaults$5(options);
5095 };
5096 /* eslint-enable */
5097
5098
5099 var getDist = function getDist(type, node, centroid, attributes, mode) {
5100 var noNodeP = mode !== 'kMedoids';
5101 var getP = noNodeP ? function (i) {
5102 return centroid[i];
5103 } : function (i) {
5104 return attributes[i](centroid);
5105 };
5106
5107 var getQ = function getQ(i) {
5108 return attributes[i](node);
5109 };
5110
5111 var nodeP = centroid;
5112 var nodeQ = node;
5113 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
5114 };
5115
5116 var randomCentroids = function randomCentroids(nodes, k, attributes) {
5117 var ndim = attributes.length;
5118 var min = new Array(ndim);
5119 var max = new Array(ndim);
5120 var centroids = new Array(k);
5121 var centroid = null; // Find min, max values for each attribute dimension
5122
5123 for (var i = 0; i < ndim; i++) {
5124 min[i] = nodes.min(attributes[i]).value;
5125 max[i] = nodes.max(attributes[i]).value;
5126 } // Build k centroids, each represented as an n-dim feature vector
5127
5128
5129 for (var c = 0; c < k; c++) {
5130 centroid = [];
5131
5132 for (var _i = 0; _i < ndim; _i++) {
5133 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
5134 }
5135
5136 centroids[c] = centroid;
5137 }
5138
5139 return centroids;
5140 };
5141
5142 var classify = function classify(node, centroids, distance, attributes, type) {
5143 var min = Infinity;
5144 var index = 0;
5145
5146 for (var i = 0; i < centroids.length; i++) {
5147 var dist = getDist(distance, node, centroids[i], attributes, type);
5148
5149 if (dist < min) {
5150 min = dist;
5151 index = i;
5152 }
5153 }
5154
5155 return index;
5156 };
5157
5158 var buildCluster = function buildCluster(centroid, nodes, assignment) {
5159 var cluster = [];
5160 var node = null;
5161
5162 for (var n = 0; n < nodes.length; n++) {
5163 node = nodes[n];
5164
5165 if (assignment[node.id()] === centroid) {
5166 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
5167 cluster.push(node);
5168 }
5169 }
5170
5171 return cluster;
5172 };
5173
5174 var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
5175 return Math.abs(v2 - v1) <= sensitivityThreshold;
5176 };
5177
5178 var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
5179 for (var i = 0; i < v1.length; i++) {
5180 for (var j = 0; j < v1[i].length; j++) {
5181 var diff = Math.abs(v1[i][j] - v2[i][j]);
5182
5183 if (diff > sensitivityThreshold) {
5184 return false;
5185 }
5186 }
5187 }
5188
5189 return true;
5190 };
5191
5192 var seenBefore = function seenBefore(node, medoids, n) {
5193 for (var i = 0; i < n; i++) {
5194 if (node === medoids[i]) return true;
5195 }
5196
5197 return false;
5198 };
5199
5200 var randomMedoids = function randomMedoids(nodes, k) {
5201 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
5202 // so we need to check to see if we've already seen or chose this node before.
5203
5204 if (nodes.length < 50) {
5205 // Randomly select k medoids from the n nodes
5206 for (var i = 0; i < k; i++) {
5207 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).
5208 // Instead choose a different random node.
5209
5210 while (seenBefore(node, medoids, i)) {
5211 node = nodes[Math.floor(Math.random() * nodes.length)];
5212 }
5213
5214 medoids[i] = node;
5215 }
5216 } else {
5217 // Relatively large data set, so pretty safe to not check and just select random nodes
5218 for (var _i2 = 0; _i2 < k; _i2++) {
5219 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
5220 }
5221 }
5222
5223 return medoids;
5224 };
5225
5226 var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
5227 var cost = 0;
5228
5229 for (var n = 0; n < cluster.length; n++) {
5230 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
5231 }
5232
5233 return cost;
5234 };
5235
5236 var kMeans = function kMeans(options) {
5237 var cy = this.cy();
5238 var nodes = this.nodes();
5239 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
5240
5241 var opts = setOptions$1(options); // Begin k-means algorithm
5242
5243 var clusters = new Array(opts.k);
5244 var assignment = {};
5245 var centroids; // Step 1: Initialize centroid positions
5246
5247 if (opts.testMode) {
5248 if (typeof opts.testCentroids === 'number') {
5249 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5250 } else if (_typeof(opts.testCentroids) === 'object') {
5251 centroids = opts.testCentroids;
5252 } else {
5253 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5254 }
5255 } else {
5256 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5257 }
5258
5259 var isStillMoving = true;
5260 var iterations = 0;
5261
5262 while (isStillMoving && iterations < opts.maxIterations) {
5263 // Step 2: Assign nodes to the nearest centroid
5264 for (var n = 0; n < nodes.length; n++) {
5265 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5266
5267 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5268 } // Step 3: For each of the k clusters, update its centroid
5269
5270
5271 isStillMoving = false;
5272
5273 for (var c = 0; c < opts.k; c++) {
5274 // Get all nodes that belong to this cluster
5275 var cluster = buildCluster(c, nodes, assignment);
5276
5277 if (cluster.length === 0) {
5278 // If cluster is empty, break out early & move to next cluster
5279 continue;
5280 } // Update centroids by calculating avg of all nodes within the cluster.
5281
5282
5283 var ndim = opts.attributes.length;
5284 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5285
5286 var newCentroid = new Array(ndim);
5287 var sum = new Array(ndim);
5288
5289 for (var d = 0; d < ndim; d++) {
5290 sum[d] = 0.0;
5291
5292 for (var i = 0; i < cluster.length; i++) {
5293 node = cluster[i];
5294 sum[d] += opts.attributes[d](node);
5295 }
5296
5297 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
5298
5299 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5300 isStillMoving = true;
5301 }
5302 }
5303
5304 centroids[c] = newCentroid;
5305 clusters[c] = cy.collection(cluster);
5306 }
5307
5308 iterations++;
5309 }
5310
5311 return clusters;
5312 };
5313
5314 var kMedoids = function kMedoids(options) {
5315 var cy = this.cy();
5316 var nodes = this.nodes();
5317 var node = null;
5318 var opts = setOptions$1(options); // Begin k-medoids algorithm
5319
5320 var clusters = new Array(opts.k);
5321 var medoids;
5322 var assignment = {};
5323 var curCost;
5324 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5325 // Step 1: Initialize k medoids
5326
5327 if (opts.testMode) {
5328 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5329 medoids = opts.testCentroids;
5330 } else {
5331 medoids = randomMedoids(nodes, opts.k);
5332 }
5333 } else {
5334 medoids = randomMedoids(nodes, opts.k);
5335 }
5336
5337 var isStillMoving = true;
5338 var iterations = 0;
5339
5340 while (isStillMoving && iterations < opts.maxIterations) {
5341 // Step 2: Assign nodes to the nearest medoid
5342 for (var n = 0; n < nodes.length; n++) {
5343 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5344
5345 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5346 }
5347
5348 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
5349 // select the node with the lowest configuration cost as new medoid.
5350
5351 for (var m = 0; m < medoids.length; m++) {
5352 // Get all nodes that belong to this medoid
5353 var cluster = buildCluster(m, nodes, assignment);
5354
5355 if (cluster.length === 0) {
5356 // If cluster is empty, break out early & move to next cluster
5357 continue;
5358 }
5359
5360 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5361 // Select different medoid if its configuration has the lowest cost
5362
5363 for (var _n = 0; _n < cluster.length; _n++) {
5364 curCost = findCost(cluster[_n], cluster, opts.attributes);
5365
5366 if (curCost < minCosts[m]) {
5367 minCosts[m] = curCost;
5368 medoids[m] = cluster[_n];
5369 isStillMoving = true;
5370 }
5371 }
5372
5373 clusters[m] = cy.collection(cluster);
5374 }
5375
5376 iterations++;
5377 }
5378
5379 return clusters;
5380 };
5381
5382 var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5383 var numerator, denominator;
5384
5385 for (var n = 0; n < nodes.length; n++) {
5386 for (var c = 0; c < centroids.length; c++) {
5387 weight[n][c] = Math.pow(U[n][c], opts.m);
5388 }
5389 }
5390
5391 for (var _c = 0; _c < centroids.length; _c++) {
5392 for (var dim = 0; dim < opts.attributes.length; dim++) {
5393 numerator = 0;
5394 denominator = 0;
5395
5396 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5397 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5398 denominator += weight[_n2][_c];
5399 }
5400
5401 centroids[_c][dim] = numerator / denominator;
5402 }
5403 }
5404 };
5405
5406 var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5407 // Save previous step
5408 for (var i = 0; i < U.length; i++) {
5409 _U[i] = U[i].slice();
5410 }
5411
5412 var sum, numerator, denominator;
5413 var pow = 2 / (opts.m - 1);
5414
5415 for (var c = 0; c < centroids.length; c++) {
5416 for (var n = 0; n < nodes.length; n++) {
5417 sum = 0;
5418
5419 for (var k = 0; k < centroids.length; k++) {
5420 // against all other centroids
5421 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5422 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5423 sum += Math.pow(numerator / denominator, pow);
5424 }
5425
5426 U[n][c] = 1 / sum;
5427 }
5428 }
5429 };
5430
5431 var assign$1 = function assign(nodes, U, opts, cy) {
5432 var clusters = new Array(opts.k);
5433
5434 for (var c = 0; c < clusters.length; c++) {
5435 clusters[c] = [];
5436 }
5437
5438 var max;
5439 var index;
5440
5441 for (var n = 0; n < U.length; n++) {
5442 // for each node (U is N x C matrix)
5443 max = -Infinity;
5444 index = -1; // Determine which cluster the node is most likely to belong in
5445
5446 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5447 if (U[n][_c2] > max) {
5448 max = U[n][_c2];
5449 index = _c2;
5450 }
5451 }
5452
5453 clusters[index].push(nodes[n]);
5454 } // Turn every array into a collection of nodes
5455
5456
5457 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5458 clusters[_c3] = cy.collection(clusters[_c3]);
5459 }
5460
5461 return clusters;
5462 };
5463
5464 var fuzzyCMeans = function fuzzyCMeans(options) {
5465 var cy = this.cy();
5466 var nodes = this.nodes();
5467 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
5468
5469 var clusters;
5470 var centroids;
5471 var U;
5472
5473 var _U;
5474
5475 var weight; // Step 1: Initialize letiables.
5476
5477 _U = new Array(nodes.length);
5478
5479 for (var i = 0; i < nodes.length; i++) {
5480 // N x C matrix
5481 _U[i] = new Array(opts.k);
5482 }
5483
5484 U = new Array(nodes.length);
5485
5486 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5487 // N x C matrix
5488 U[_i3] = new Array(opts.k);
5489 }
5490
5491 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5492 var total = 0;
5493
5494 for (var j = 0; j < opts.k; j++) {
5495 U[_i4][j] = Math.random();
5496 total += U[_i4][j];
5497 }
5498
5499 for (var _j = 0; _j < opts.k; _j++) {
5500 U[_i4][_j] = U[_i4][_j] / total;
5501 }
5502 }
5503
5504 centroids = new Array(opts.k);
5505
5506 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5507 centroids[_i5] = new Array(opts.attributes.length);
5508 }
5509
5510 weight = new Array(nodes.length);
5511
5512 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5513 // N x C matrix
5514 weight[_i6] = new Array(opts.k);
5515 } // end init FCM
5516
5517
5518 var isStillMoving = true;
5519 var iterations = 0;
5520
5521 while (isStillMoving && iterations < opts.maxIterations) {
5522 isStillMoving = false; // Step 2: Calculate the centroids for each step.
5523
5524 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
5525
5526 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
5527
5528 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5529 isStillMoving = true;
5530 }
5531
5532 iterations++;
5533 } // Assign nodes to clusters with highest probability.
5534
5535
5536 clusters = assign$1(nodes, U, opts, cy);
5537 return {
5538 clusters: clusters,
5539 degreeOfMembership: U
5540 };
5541 };
5542
5543 var kClustering = {
5544 kMeans: kMeans,
5545 kMedoids: kMedoids,
5546 fuzzyCMeans: fuzzyCMeans,
5547 fcm: fuzzyCMeans
5548 };
5549
5550 // Implemented by Zoe Xi @zoexi for GSOC 2016
5551 var defaults$6 = defaults({
5552 distance: 'euclidean',
5553 // distance metric to compare nodes
5554 linkage: 'min',
5555 // linkage criterion : how to determine the distance between clusters of nodes
5556 mode: 'threshold',
5557 // mode:'threshold' => clusters must be threshold distance apart
5558 threshold: Infinity,
5559 // the distance threshold
5560 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5561 addDendrogram: false,
5562 // whether to add the dendrogram to the graph for viz
5563 dendrogramDepth: 0,
5564 // depth at which dendrogram branches are merged into the returned clusters
5565 attributes: [] // array of attr functions
5566
5567 });
5568 var linkageAliases = {
5569 'single': 'min',
5570 'complete': 'max'
5571 };
5572
5573 var setOptions$2 = function setOptions(options) {
5574 var opts = defaults$6(options);
5575 var preferredAlias = linkageAliases[opts.linkage];
5576
5577 if (preferredAlias != null) {
5578 opts.linkage = preferredAlias;
5579 }
5580
5581 return opts;
5582 };
5583
5584 var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5585 // Find two closest clusters from cached mins
5586 var minKey = 0;
5587 var min = Infinity;
5588 var dist;
5589 var attrs = opts.attributes;
5590
5591 var getDist = function getDist(n1, n2) {
5592 return clusteringDistance(opts.distance, attrs.length, function (i) {
5593 return attrs[i](n1);
5594 }, function (i) {
5595 return attrs[i](n2);
5596 }, n1, n2);
5597 };
5598
5599 for (var i = 0; i < clusters.length; i++) {
5600 var key = clusters[i].key;
5601 var _dist = dists[key][mins[key]];
5602
5603 if (_dist < min) {
5604 minKey = key;
5605 min = _dist;
5606 }
5607 }
5608
5609 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5610 return false;
5611 }
5612
5613 var c1 = index[minKey];
5614 var c2 = index[mins[minKey]];
5615 var merged; // Merge two closest clusters
5616
5617 if (opts.mode === 'dendrogram') {
5618 merged = {
5619 left: c1,
5620 right: c2,
5621 key: c1.key
5622 };
5623 } else {
5624 merged = {
5625 value: c1.value.concat(c2.value),
5626 key: c1.key
5627 };
5628 }
5629
5630 clusters[c1.index] = merged;
5631 clusters.splice(c2.index, 1);
5632 index[c1.key] = merged; // Update distances with new merged cluster
5633
5634 for (var _i = 0; _i < clusters.length; _i++) {
5635 var cur = clusters[_i];
5636
5637 if (c1.key === cur.key) {
5638 dist = Infinity;
5639 } else if (opts.linkage === 'min') {
5640 dist = dists[c1.key][cur.key];
5641
5642 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5643 dist = dists[c2.key][cur.key];
5644 }
5645 } else if (opts.linkage === 'max') {
5646 dist = dists[c1.key][cur.key];
5647
5648 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5649 dist = dists[c2.key][cur.key];
5650 }
5651 } else if (opts.linkage === 'mean') {
5652 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5653 } else {
5654 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5655 }
5656
5657 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5658 } // Update cached mins
5659
5660
5661 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5662 var key1 = clusters[_i2].key;
5663
5664 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5665 var _min = key1;
5666
5667 for (var j = 0; j < clusters.length; j++) {
5668 var key2 = clusters[j].key;
5669
5670 if (dists[key1][key2] < dists[key1][_min]) {
5671 _min = key2;
5672 }
5673 }
5674
5675 mins[key1] = _min;
5676 }
5677
5678 clusters[_i2].index = _i2;
5679 } // Clean up meta data used for clustering
5680
5681
5682 c1.key = c2.key = c1.index = c2.index = null;
5683 return true;
5684 };
5685
5686 var getAllChildren = function getAllChildren(root, arr, cy) {
5687 if (!root) return;
5688
5689 if (root.value) {
5690 arr.push(root.value);
5691 } else {
5692 if (root.left) getAllChildren(root.left, arr);
5693 if (root.right) getAllChildren(root.right, arr);
5694 }
5695 };
5696
5697 var buildDendrogram = function buildDendrogram(root, cy) {
5698 if (!root) return '';
5699
5700 if (root.left && root.right) {
5701 var leftStr = buildDendrogram(root.left, cy);
5702 var rightStr = buildDendrogram(root.right, cy);
5703 var node = cy.add({
5704 group: 'nodes',
5705 data: {
5706 id: leftStr + ',' + rightStr
5707 }
5708 });
5709 cy.add({
5710 group: 'edges',
5711 data: {
5712 source: leftStr,
5713 target: node.id()
5714 }
5715 });
5716 cy.add({
5717 group: 'edges',
5718 data: {
5719 source: rightStr,
5720 target: node.id()
5721 }
5722 });
5723 return node.id();
5724 } else if (root.value) {
5725 return root.value.id();
5726 }
5727 };
5728
5729 var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5730 if (!root) return [];
5731 var left = [],
5732 right = [],
5733 leaves = [];
5734
5735 if (k === 0) {
5736 // don't cut tree, simply return all nodes as 1 single cluster
5737 if (root.left) getAllChildren(root.left, left);
5738 if (root.right) getAllChildren(root.right, right);
5739 leaves = left.concat(right);
5740 return [cy.collection(leaves)];
5741 } else if (k === 1) {
5742 // cut at root
5743 if (root.value) {
5744 // leaf node
5745 return [cy.collection(root.value)];
5746 } else {
5747 if (root.left) getAllChildren(root.left, left);
5748 if (root.right) getAllChildren(root.right, right);
5749 return [cy.collection(left), cy.collection(right)];
5750 }
5751 } else {
5752 if (root.value) {
5753 return [cy.collection(root.value)];
5754 } else {
5755 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5756 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5757 return left.concat(right);
5758 }
5759 }
5760 };
5761 /* eslint-enable */
5762
5763
5764 var hierarchicalClustering = function hierarchicalClustering(options) {
5765 var cy = this.cy();
5766 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5767
5768 var opts = setOptions$2(options);
5769 var attrs = opts.attributes;
5770
5771 var getDist = function getDist(n1, n2) {
5772 return clusteringDistance(opts.distance, attrs.length, function (i) {
5773 return attrs[i](n1);
5774 }, function (i) {
5775 return attrs[i](n2);
5776 }, n1, n2);
5777 }; // Begin hierarchical algorithm
5778
5779
5780 var clusters = [];
5781 var dists = []; // distances between each pair of clusters
5782
5783 var mins = []; // closest cluster for each cluster
5784
5785 var index = []; // hash of all clusters by key
5786 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5787
5788 for (var n = 0; n < nodes.length; n++) {
5789 var cluster = {
5790 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5791 key: n,
5792 index: n
5793 };
5794 clusters[n] = cluster;
5795 index[n] = cluster;
5796 dists[n] = [];
5797 mins[n] = 0;
5798 } // Calculate the distance between each pair of clusters
5799
5800
5801 for (var i = 0; i < clusters.length; i++) {
5802 for (var j = 0; j <= i; j++) {
5803 var dist = void 0;
5804
5805 if (opts.mode === 'dendrogram') {
5806 // modes store cluster values differently
5807 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5808 } else {
5809 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5810 }
5811
5812 dists[i][j] = dist;
5813 dists[j][i] = dist;
5814
5815 if (dist < dists[i][mins[i]]) {
5816 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5817 }
5818 }
5819 } // Find the closest pair of clusters and merge them into a single cluster.
5820 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5821
5822
5823 var merged = mergeClosest(clusters, index, dists, mins, opts);
5824
5825 while (merged) {
5826 merged = mergeClosest(clusters, index, dists, mins, opts);
5827 }
5828
5829 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5830 // in addition to returning the clusters.
5831
5832 if (opts.mode === 'dendrogram') {
5833 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5834 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5835 } else {
5836 // Regular mode simply returns the clusters
5837 retClusters = new Array(clusters.length);
5838 clusters.forEach(function (cluster, i) {
5839 // Clean up meta data used for clustering
5840 cluster.key = cluster.index = null;
5841 retClusters[i] = cy.collection(cluster.value);
5842 });
5843 }
5844
5845 return retClusters;
5846 };
5847
5848 var hierarchicalClustering$1 = {
5849 hierarchicalClustering: hierarchicalClustering,
5850 hca: hierarchicalClustering
5851 };
5852
5853 // Implemented by Zoe Xi @zoexi for GSOC 2016
5854 var defaults$7 = defaults({
5855 distance: 'euclidean',
5856 // distance metric to compare attributes between two nodes
5857 preference: 'median',
5858 // suitability of a data point to serve as an exemplar
5859 damping: 0.8,
5860 // damping factor between [0.5, 1)
5861 maxIterations: 1000,
5862 // max number of iterations to run
5863 minIterations: 100,
5864 // min number of iterations to run in order for clustering to stop
5865 attributes: [// functions to quantify the similarity between any two points
5866 // e.g. node => node.data('weight')
5867 ]
5868 });
5869
5870 var setOptions$3 = function setOptions(options) {
5871 var dmp = options.damping;
5872 var pref = options.preference;
5873
5874 if (!(0.5 <= dmp && dmp < 1)) {
5875 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5876 }
5877
5878 var validPrefs = ['median', 'mean', 'min', 'max'];
5879
5880 if (!(validPrefs.some(function (v) {
5881 return v === pref;
5882 }) || number(pref))) {
5883 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5884 return "'".concat(p, "'");
5885 }).join(', '), "] or a number. Got: ").concat(pref));
5886 }
5887
5888 return defaults$7(options);
5889 };
5890 /* eslint-enable */
5891
5892
5893 var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5894 var attr = function attr(n, i) {
5895 return attributes[i](n);
5896 }; // nb negative because similarity should have an inverse relationship to distance
5897
5898
5899 return -clusteringDistance(type, attributes.length, function (i) {
5900 return attr(n1, i);
5901 }, function (i) {
5902 return attr(n2, i);
5903 }, n1, n2);
5904 };
5905
5906 var getPreference = function getPreference(S, preference) {
5907 // larger preference = greater # of clusters
5908 var p = null;
5909
5910 if (preference === 'median') {
5911 p = median(S);
5912 } else if (preference === 'mean') {
5913 p = mean(S);
5914 } else if (preference === 'min') {
5915 p = min(S);
5916 } else if (preference === 'max') {
5917 p = max(S);
5918 } else {
5919 // Custom preference number, as set by user
5920 p = preference;
5921 }
5922
5923 return p;
5924 };
5925
5926 var findExemplars = function findExemplars(n, R, A) {
5927 var indices = [];
5928
5929 for (var i = 0; i < n; i++) {
5930 if (R[i * n + i] + A[i * n + i] > 0) {
5931 indices.push(i);
5932 }
5933 }
5934
5935 return indices;
5936 };
5937
5938 var assignClusters = function assignClusters(n, S, exemplars) {
5939 var clusters = [];
5940
5941 for (var i = 0; i < n; i++) {
5942 var index = -1;
5943 var max = -Infinity;
5944
5945 for (var ei = 0; ei < exemplars.length; ei++) {
5946 var e = exemplars[ei];
5947
5948 if (S[i * n + e] > max) {
5949 index = e;
5950 max = S[i * n + e];
5951 }
5952 }
5953
5954 if (index > 0) {
5955 clusters.push(index);
5956 }
5957 }
5958
5959 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5960 clusters[exemplars[_ei]] = exemplars[_ei];
5961 }
5962
5963 return clusters;
5964 };
5965
5966 var assign$2 = function assign(n, S, exemplars) {
5967 var clusters = assignClusters(n, S, exemplars);
5968
5969 for (var ei = 0; ei < exemplars.length; ei++) {
5970 var ii = [];
5971
5972 for (var c = 0; c < clusters.length; c++) {
5973 if (clusters[c] === exemplars[ei]) {
5974 ii.push(c);
5975 }
5976 }
5977
5978 var maxI = -1;
5979 var maxSum = -Infinity;
5980
5981 for (var i = 0; i < ii.length; i++) {
5982 var sum = 0;
5983
5984 for (var j = 0; j < ii.length; j++) {
5985 sum += S[ii[j] * n + ii[i]];
5986 }
5987
5988 if (sum > maxSum) {
5989 maxI = i;
5990 maxSum = sum;
5991 }
5992 }
5993
5994 exemplars[ei] = ii[maxI];
5995 }
5996
5997 clusters = assignClusters(n, S, exemplars);
5998 return clusters;
5999 };
6000
6001 var affinityPropagation = function affinityPropagation(options) {
6002 var cy = this.cy();
6003 var nodes = this.nodes();
6004 var opts = setOptions$3(options); // Map each node to its position in node array
6005
6006 var id2position = {};
6007
6008 for (var i = 0; i < nodes.length; i++) {
6009 id2position[nodes[i].id()] = i;
6010 } // Begin affinity propagation algorithm
6011
6012
6013 var n; // number of data points
6014
6015 var n2; // size of matrices
6016
6017 var S; // similarity matrix (1D array)
6018
6019 var p; // preference/suitability of a data point to serve as an exemplar
6020
6021 var R; // responsibility matrix (1D array)
6022
6023 var A; // availability matrix (1D array)
6024
6025 n = nodes.length;
6026 n2 = n * n; // Initialize and build S similarity matrix
6027
6028 S = new Array(n2);
6029
6030 for (var _i = 0; _i < n2; _i++) {
6031 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
6032 }
6033
6034 for (var _i2 = 0; _i2 < n; _i2++) {
6035 for (var j = 0; j < n; j++) {
6036 if (_i2 !== j) {
6037 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
6038 }
6039 }
6040 } // Place preferences on the diagonal of S
6041
6042
6043 p = getPreference(S, opts.preference);
6044
6045 for (var _i3 = 0; _i3 < n; _i3++) {
6046 S[_i3 * n + _i3] = p;
6047 } // Initialize R responsibility matrix
6048
6049
6050 R = new Array(n2);
6051
6052 for (var _i4 = 0; _i4 < n2; _i4++) {
6053 R[_i4] = 0.0;
6054 } // Initialize A availability matrix
6055
6056
6057 A = new Array(n2);
6058
6059 for (var _i5 = 0; _i5 < n2; _i5++) {
6060 A[_i5] = 0.0;
6061 }
6062
6063 var old = new Array(n);
6064 var Rp = new Array(n);
6065 var se = new Array(n);
6066
6067 for (var _i6 = 0; _i6 < n; _i6++) {
6068 old[_i6] = 0.0;
6069 Rp[_i6] = 0.0;
6070 se[_i6] = 0;
6071 }
6072
6073 var e = new Array(n * opts.minIterations);
6074
6075 for (var _i7 = 0; _i7 < e.length; _i7++) {
6076 e[_i7] = 0;
6077 }
6078
6079 var iter;
6080
6081 for (iter = 0; iter < opts.maxIterations; iter++) {
6082 // main algorithmic loop
6083 // Update R responsibility matrix
6084 for (var _i8 = 0; _i8 < n; _i8++) {
6085 var max = -Infinity,
6086 max2 = -Infinity,
6087 maxI = -1,
6088 AS = 0.0;
6089
6090 for (var _j = 0; _j < n; _j++) {
6091 old[_j] = R[_i8 * n + _j];
6092 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
6093
6094 if (AS >= max) {
6095 max2 = max;
6096 max = AS;
6097 maxI = _j;
6098 } else if (AS > max2) {
6099 max2 = AS;
6100 }
6101 }
6102
6103 for (var _j2 = 0; _j2 < n; _j2++) {
6104 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
6105 }
6106
6107 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
6108 } // Update A availability matrix
6109
6110
6111 for (var _i9 = 0; _i9 < n; _i9++) {
6112 var sum = 0;
6113
6114 for (var _j3 = 0; _j3 < n; _j3++) {
6115 old[_j3] = A[_j3 * n + _i9];
6116 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
6117 sum += Rp[_j3];
6118 }
6119
6120 sum -= Rp[_i9];
6121 Rp[_i9] = R[_i9 * n + _i9];
6122 sum += Rp[_i9];
6123
6124 for (var _j4 = 0; _j4 < n; _j4++) {
6125 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
6126 }
6127
6128 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
6129 } // Check for convergence
6130
6131
6132 var K = 0;
6133
6134 for (var _i10 = 0; _i10 < n; _i10++) {
6135 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
6136 e[iter % opts.minIterations * n + _i10] = E;
6137 K += E;
6138 }
6139
6140 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
6141 var _sum = 0;
6142
6143 for (var _i11 = 0; _i11 < n; _i11++) {
6144 se[_i11] = 0;
6145
6146 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
6147 se[_i11] += e[_j5 * n + _i11];
6148 }
6149
6150 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
6151 _sum++;
6152 }
6153 }
6154
6155 if (_sum === n) {
6156 // then we have convergence
6157 break;
6158 }
6159 }
6160 } // Identify exemplars (cluster centers)
6161
6162
6163 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
6164
6165 var clusterIndices = assign$2(n, S, exemplarsIndices);
6166 var clusters = {};
6167
6168 for (var c = 0; c < exemplarsIndices.length; c++) {
6169 clusters[exemplarsIndices[c]] = [];
6170 }
6171
6172 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
6173 var pos = id2position[nodes[_i12].id()];
6174
6175 var clusterIndex = clusterIndices[pos];
6176
6177 if (clusterIndex != null) {
6178 // the node may have not been assigned a cluster if no valid attributes were specified
6179 clusters[clusterIndex].push(nodes[_i12]);
6180 }
6181 }
6182
6183 var retClusters = new Array(exemplarsIndices.length);
6184
6185 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
6186 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
6187 }
6188
6189 return retClusters;
6190 };
6191
6192 var affinityPropagation$1 = {
6193 affinityPropagation: affinityPropagation,
6194 ap: affinityPropagation
6195 };
6196
6197 var hierholzerDefaults = defaults({
6198 root: undefined,
6199 directed: false
6200 });
6201 var elesfn$b = {
6202 hierholzer: function hierholzer(options) {
6203 if (!plainObject(options)) {
6204 var args = arguments;
6205 options = {
6206 root: args[0],
6207 directed: args[1]
6208 };
6209 }
6210
6211 var _hierholzerDefaults = hierholzerDefaults(options),
6212 root = _hierholzerDefaults.root,
6213 directed = _hierholzerDefaults.directed;
6214
6215 var eles = this;
6216 var dflag = false;
6217 var oddIn;
6218 var oddOut;
6219 var startVertex;
6220 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
6221 var nodes = {};
6222 var edges = {};
6223
6224 if (directed) {
6225 eles.forEach(function (ele) {
6226 var id = ele.id();
6227
6228 if (ele.isNode()) {
6229 var ind = ele.indegree(true);
6230 var outd = ele.outdegree(true);
6231 var d1 = ind - outd;
6232 var d2 = outd - ind;
6233
6234 if (d1 == 1) {
6235 if (oddIn) dflag = true;else oddIn = id;
6236 } else if (d2 == 1) {
6237 if (oddOut) dflag = true;else oddOut = id;
6238 } else if (d2 > 1 || d1 > 1) {
6239 dflag = true;
6240 }
6241
6242 nodes[id] = [];
6243 ele.outgoers().forEach(function (e) {
6244 if (e.isEdge()) nodes[id].push(e.id());
6245 });
6246 } else {
6247 edges[id] = [undefined, ele.target().id()];
6248 }
6249 });
6250 } else {
6251 eles.forEach(function (ele) {
6252 var id = ele.id();
6253
6254 if (ele.isNode()) {
6255 var d = ele.degree(true);
6256
6257 if (d % 2) {
6258 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
6259 }
6260
6261 nodes[id] = [];
6262 ele.connectedEdges().forEach(function (e) {
6263 return nodes[id].push(e.id());
6264 });
6265 } else {
6266 edges[id] = [ele.source().id(), ele.target().id()];
6267 }
6268 });
6269 }
6270
6271 var result = {
6272 found: false,
6273 trail: undefined
6274 };
6275 if (dflag) return result;else if (oddOut && oddIn) {
6276 if (directed) {
6277 if (startVertex && oddOut != startVertex) {
6278 return result;
6279 }
6280
6281 startVertex = oddOut;
6282 } else {
6283 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
6284 return result;
6285 } else if (!startVertex) {
6286 startVertex = oddOut;
6287 }
6288 }
6289 } else {
6290 if (!startVertex) startVertex = eles[0].id();
6291 }
6292
6293 var walk = function walk(v) {
6294 var currentNode = v;
6295 var subtour = [v];
6296 var adj, adjTail, adjHead;
6297
6298 while (nodes[currentNode].length) {
6299 adj = nodes[currentNode].shift();
6300 adjTail = edges[adj][0];
6301 adjHead = edges[adj][1];
6302
6303 if (currentNode != adjHead) {
6304 nodes[adjHead] = nodes[adjHead].filter(function (e) {
6305 return e != adj;
6306 });
6307 currentNode = adjHead;
6308 } else if (!directed && currentNode != adjTail) {
6309 nodes[adjTail] = nodes[adjTail].filter(function (e) {
6310 return e != adj;
6311 });
6312 currentNode = adjTail;
6313 }
6314
6315 subtour.unshift(adj);
6316 subtour.unshift(currentNode);
6317 }
6318
6319 return subtour;
6320 };
6321
6322 var trail = [];
6323 var subtour = [];
6324 subtour = walk(startVertex);
6325
6326 while (subtour.length != 1) {
6327 if (nodes[subtour[0]].length == 0) {
6328 trail.unshift(eles.getElementById(subtour.shift()));
6329 trail.unshift(eles.getElementById(subtour.shift()));
6330 } else {
6331 subtour = walk(subtour.shift()).concat(subtour);
6332 }
6333 }
6334
6335 trail.unshift(eles.getElementById(subtour.shift())); // final node
6336
6337 for (var d in nodes) {
6338 if (nodes[d].length) {
6339 return result;
6340 }
6341 }
6342
6343 result.found = true;
6344 result.trail = this.spawn(trail, true);
6345 return result;
6346 }
6347 };
6348
6349 var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
6350 var eles = this;
6351 var nodes = {};
6352 var id = 0;
6353 var edgeCount = 0;
6354 var components = [];
6355 var stack = [];
6356 var visitedEdges = {};
6357
6358 var buildComponent = function buildComponent(x, y) {
6359 var i = stack.length - 1;
6360 var cutset = [];
6361 var component = eles.spawn();
6362
6363 while (stack[i].x != x || stack[i].y != y) {
6364 cutset.push(stack.pop().edge);
6365 i--;
6366 }
6367
6368 cutset.push(stack.pop().edge);
6369 cutset.forEach(function (edge) {
6370 var connectedNodes = edge.connectedNodes().intersection(eles);
6371 component.merge(edge);
6372 connectedNodes.forEach(function (node) {
6373 var nodeId = node.id();
6374 var connectedEdges = node.connectedEdges().intersection(eles);
6375 component.merge(node);
6376
6377 if (!nodes[nodeId].cutVertex) {
6378 component.merge(connectedEdges);
6379 } else {
6380 component.merge(connectedEdges.filter(function (edge) {
6381 return edge.isLoop();
6382 }));
6383 }
6384 });
6385 });
6386 components.push(component);
6387 };
6388
6389 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
6390 if (root === parent) edgeCount += 1;
6391 nodes[currentNode] = {
6392 id: id,
6393 low: id++,
6394 cutVertex: false
6395 };
6396 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
6397
6398 if (edges.size() === 0) {
6399 components.push(eles.spawn(eles.getElementById(currentNode)));
6400 } else {
6401 var sourceId, targetId, otherNodeId, edgeId;
6402 edges.forEach(function (edge) {
6403 sourceId = edge.source().id();
6404 targetId = edge.target().id();
6405 otherNodeId = sourceId === currentNode ? targetId : sourceId;
6406
6407 if (otherNodeId !== parent) {
6408 edgeId = edge.id();
6409
6410 if (!visitedEdges[edgeId]) {
6411 visitedEdges[edgeId] = true;
6412 stack.push({
6413 x: currentNode,
6414 y: otherNodeId,
6415 edge: edge
6416 });
6417 }
6418
6419 if (!(otherNodeId in nodes)) {
6420 biconnectedSearch(root, otherNodeId, currentNode);
6421 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6422
6423 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6424 nodes[currentNode].cutVertex = true;
6425 buildComponent(currentNode, otherNodeId);
6426 }
6427 } else {
6428 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6429 }
6430 }
6431 });
6432 }
6433 };
6434
6435 eles.forEach(function (ele) {
6436 if (ele.isNode()) {
6437 var nodeId = ele.id();
6438
6439 if (!(nodeId in nodes)) {
6440 edgeCount = 0;
6441 biconnectedSearch(nodeId, nodeId);
6442 nodes[nodeId].cutVertex = edgeCount > 1;
6443 }
6444 }
6445 });
6446 var cutVertices = Object.keys(nodes).filter(function (id) {
6447 return nodes[id].cutVertex;
6448 }).map(function (id) {
6449 return eles.getElementById(id);
6450 });
6451 return {
6452 cut: eles.spawn(cutVertices),
6453 components: components
6454 };
6455 };
6456
6457 var hopcroftTarjanBiconnected$1 = {
6458 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6459 htbc: hopcroftTarjanBiconnected,
6460 htb: hopcroftTarjanBiconnected,
6461 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6462 };
6463
6464 var tarjanStronglyConnected = function tarjanStronglyConnected() {
6465 var eles = this;
6466 var nodes = {};
6467 var index = 0;
6468 var components = [];
6469 var stack = [];
6470 var cut = eles.spawn(eles);
6471
6472 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6473 stack.push(sourceNodeId);
6474 nodes[sourceNodeId] = {
6475 index: index,
6476 low: index++,
6477 explored: false
6478 };
6479 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6480 connectedEdges.forEach(function (edge) {
6481 var targetNodeId = edge.target().id();
6482
6483 if (targetNodeId !== sourceNodeId) {
6484 if (!(targetNodeId in nodes)) {
6485 stronglyConnectedSearch(targetNodeId);
6486 }
6487
6488 if (!nodes[targetNodeId].explored) {
6489 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6490 }
6491 }
6492 });
6493
6494 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6495 var componentNodes = eles.spawn();
6496
6497 for (;;) {
6498 var nodeId = stack.pop();
6499 componentNodes.merge(eles.getElementById(nodeId));
6500 nodes[nodeId].low = nodes[sourceNodeId].index;
6501 nodes[nodeId].explored = true;
6502
6503 if (nodeId === sourceNodeId) {
6504 break;
6505 }
6506 }
6507
6508 var componentEdges = componentNodes.edgesWith(componentNodes);
6509 var component = componentNodes.merge(componentEdges);
6510 components.push(component);
6511 cut = cut.difference(component);
6512 }
6513 };
6514
6515 eles.forEach(function (ele) {
6516 if (ele.isNode()) {
6517 var nodeId = ele.id();
6518
6519 if (!(nodeId in nodes)) {
6520 stronglyConnectedSearch(nodeId);
6521 }
6522 }
6523 });
6524 return {
6525 cut: cut,
6526 components: components
6527 };
6528 };
6529
6530 var tarjanStronglyConnected$1 = {
6531 tarjanStronglyConnected: tarjanStronglyConnected,
6532 tsc: tarjanStronglyConnected,
6533 tscc: tarjanStronglyConnected,
6534 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6535 };
6536
6537 var elesfn$c = {};
6538 [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) {
6539 extend(elesfn$c, props);
6540 });
6541
6542 /*!
6543 Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6544 Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6545 Licensed under The MIT License (http://opensource.org/licenses/MIT)
6546 */
6547
6548 /* promise states [Promises/A+ 2.1] */
6549 var STATE_PENDING = 0;
6550 /* [Promises/A+ 2.1.1] */
6551
6552 var STATE_FULFILLED = 1;
6553 /* [Promises/A+ 2.1.2] */
6554
6555 var STATE_REJECTED = 2;
6556 /* [Promises/A+ 2.1.3] */
6557
6558 /* promise object constructor */
6559
6560 var api = function api(executor) {
6561 /* optionally support non-constructor/plain-function call */
6562 if (!(this instanceof api)) return new api(executor);
6563 /* initialize object */
6564
6565 this.id = 'Thenable/1.0.7';
6566 this.state = STATE_PENDING;
6567 /* initial state */
6568
6569 this.fulfillValue = undefined;
6570 /* initial value */
6571
6572 /* [Promises/A+ 1.3, 2.1.2.2] */
6573
6574 this.rejectReason = undefined;
6575 /* initial reason */
6576
6577 /* [Promises/A+ 1.5, 2.1.3.2] */
6578
6579 this.onFulfilled = [];
6580 /* initial handlers */
6581
6582 this.onRejected = [];
6583 /* initial handlers */
6584
6585 /* provide optional information-hiding proxy */
6586
6587 this.proxy = {
6588 then: this.then.bind(this)
6589 };
6590 /* support optional executor function */
6591
6592 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6593 };
6594 /* promise API methods */
6595
6596
6597 api.prototype = {
6598 /* promise resolving methods */
6599 fulfill: function fulfill(value) {
6600 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6601 },
6602 reject: function reject(value) {
6603 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6604 },
6605
6606 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6607 then: function then(onFulfilled, onRejected) {
6608 var curr = this;
6609 var next = new api();
6610 /* [Promises/A+ 2.2.7] */
6611
6612 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
6613 /* [Promises/A+ 2.2.2/2.2.6] */
6614
6615 curr.onRejected.push(resolver(onRejected, next, 'reject'));
6616 /* [Promises/A+ 2.2.3/2.2.6] */
6617
6618 execute(curr);
6619 return next.proxy;
6620 /* [Promises/A+ 2.2.7, 3.3] */
6621 }
6622 };
6623 /* deliver an action */
6624
6625 var deliver = function deliver(curr, state, name, value) {
6626 if (curr.state === STATE_PENDING) {
6627 curr.state = state;
6628 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6629
6630 curr[name] = value;
6631 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6632
6633 execute(curr);
6634 }
6635
6636 return curr;
6637 };
6638 /* execute all handlers */
6639
6640
6641 var execute = function execute(curr) {
6642 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6643 };
6644 /* execute particular set of handlers */
6645
6646
6647 var execute_handlers = function execute_handlers(curr, name, value) {
6648 /* global setImmediate: true */
6649
6650 /* global setTimeout: true */
6651
6652 /* short-circuit processing */
6653 if (curr[name].length === 0) return;
6654 /* iterate over all handlers, exactly once */
6655
6656 var handlers = curr[name];
6657 curr[name] = [];
6658 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6659
6660 var func = function func() {
6661 for (var i = 0; i < handlers.length; i++) {
6662 handlers[i](value);
6663 }
6664 /* [Promises/A+ 2.2.5] */
6665
6666 };
6667 /* execute procedure asynchronously */
6668
6669 /* [Promises/A+ 2.2.4, 3.1] */
6670
6671
6672 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6673 };
6674 /* generate a resolver function */
6675
6676
6677 var resolver = function resolver(cb, next, method) {
6678 return function (value) {
6679 if (typeof cb !== 'function')
6680 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6681 next[method].call(next, value);
6682 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
6683 else {
6684 var result;
6685
6686 try {
6687 result = cb(value);
6688 }
6689 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
6690 catch (e) {
6691 next.reject(e);
6692 /* [Promises/A+ 2.2.7.2] */
6693
6694 return;
6695 }
6696
6697 resolve(next, result);
6698 /* [Promises/A+ 2.2.7.1] */
6699 }
6700 };
6701 };
6702 /* "Promise Resolution Procedure" */
6703
6704 /* [Promises/A+ 2.3] */
6705
6706
6707 var resolve = function resolve(promise, x) {
6708 /* sanity check arguments */
6709
6710 /* [Promises/A+ 2.3.1] */
6711 if (promise === x || promise.proxy === x) {
6712 promise.reject(new TypeError('cannot resolve promise with itself'));
6713 return;
6714 }
6715 /* surgically check for a "then" method
6716 (mainly to just call the "getter" of "then" only once) */
6717
6718
6719 var then;
6720
6721 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6722 try {
6723 then = x.then;
6724 }
6725 /* [Promises/A+ 2.3.3.1, 3.5] */
6726 catch (e) {
6727 promise.reject(e);
6728 /* [Promises/A+ 2.3.3.2] */
6729
6730 return;
6731 }
6732 }
6733 /* handle own Thenables [Promises/A+ 2.3.2]
6734 and similar "thenables" [Promises/A+ 2.3.3] */
6735
6736
6737 if (typeof then === 'function') {
6738 var resolved = false;
6739
6740 try {
6741 /* call retrieved "then" method */
6742
6743 /* [Promises/A+ 2.3.3.3] */
6744 then.call(x,
6745 /* resolvePromise */
6746
6747 /* [Promises/A+ 2.3.3.3.1] */
6748 function (y) {
6749 if (resolved) return;
6750 resolved = true;
6751 /* [Promises/A+ 2.3.3.3.3] */
6752
6753 if (y === x)
6754 /* [Promises/A+ 3.6] */
6755 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6756 },
6757 /* rejectPromise */
6758
6759 /* [Promises/A+ 2.3.3.3.2] */
6760 function (r) {
6761 if (resolved) return;
6762 resolved = true;
6763 /* [Promises/A+ 2.3.3.3.3] */
6764
6765 promise.reject(r);
6766 });
6767 } catch (e) {
6768 if (!resolved)
6769 /* [Promises/A+ 2.3.3.3.3] */
6770 promise.reject(e);
6771 /* [Promises/A+ 2.3.3.3.4] */
6772 }
6773
6774 return;
6775 }
6776 /* handle other values */
6777
6778
6779 promise.fulfill(x);
6780 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6781 }; // so we always have Promise.all()
6782
6783
6784 api.all = function (ps) {
6785 return new api(function (resolveAll, rejectAll) {
6786 var vals = new Array(ps.length);
6787 var doneCount = 0;
6788
6789 var fulfill = function fulfill(i, val) {
6790 vals[i] = val;
6791 doneCount++;
6792
6793 if (doneCount === ps.length) {
6794 resolveAll(vals);
6795 }
6796 };
6797
6798 for (var i = 0; i < ps.length; i++) {
6799 (function (i) {
6800 var p = ps[i];
6801 var isPromise = p != null && p.then != null;
6802
6803 if (isPromise) {
6804 p.then(function (val) {
6805 fulfill(i, val);
6806 }, function (err) {
6807 rejectAll(err);
6808 });
6809 } else {
6810 var val = p;
6811 fulfill(i, val);
6812 }
6813 })(i);
6814 }
6815 });
6816 };
6817
6818 api.resolve = function (val) {
6819 return new api(function (resolve, reject) {
6820 resolve(val);
6821 });
6822 };
6823
6824 api.reject = function (val) {
6825 return new api(function (resolve, reject) {
6826 reject(val);
6827 });
6828 };
6829
6830 var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6831
6832 var Animation = function Animation(target, opts, opts2) {
6833 var isCore = core(target);
6834 var isEle = !isCore;
6835
6836 var _p = this._private = extend({
6837 duration: 1000
6838 }, opts, opts2);
6839
6840 _p.target = target;
6841 _p.style = _p.style || _p.css;
6842 _p.started = false;
6843 _p.playing = false;
6844 _p.hooked = false;
6845 _p.applying = false;
6846 _p.progress = 0;
6847 _p.completes = [];
6848 _p.frames = [];
6849
6850 if (_p.complete && fn(_p.complete)) {
6851 _p.completes.push(_p.complete);
6852 }
6853
6854 if (isEle) {
6855 var pos = target.position();
6856 _p.startPosition = _p.startPosition || {
6857 x: pos.x,
6858 y: pos.y
6859 };
6860 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6861 }
6862
6863 if (isCore) {
6864 var pan = target.pan();
6865 _p.startPan = {
6866 x: pan.x,
6867 y: pan.y
6868 };
6869 _p.startZoom = target.zoom();
6870 } // for future timeline/animations impl
6871
6872
6873 this.length = 1;
6874 this[0] = this;
6875 };
6876
6877 var anifn = Animation.prototype;
6878 extend(anifn, {
6879 instanceString: function instanceString() {
6880 return 'animation';
6881 },
6882 hook: function hook() {
6883 var _p = this._private;
6884
6885 if (!_p.hooked) {
6886 // add to target's animation queue
6887 var q;
6888 var tAni = _p.target._private.animation;
6889
6890 if (_p.queue) {
6891 q = tAni.queue;
6892 } else {
6893 q = tAni.current;
6894 }
6895
6896 q.push(this); // add to the animation loop pool
6897
6898 if (elementOrCollection(_p.target)) {
6899 _p.target.cy().addToAnimationPool(_p.target);
6900 }
6901
6902 _p.hooked = true;
6903 }
6904
6905 return this;
6906 },
6907 play: function play() {
6908 var _p = this._private; // autorewind
6909
6910 if (_p.progress === 1) {
6911 _p.progress = 0;
6912 }
6913
6914 _p.playing = true;
6915 _p.started = false; // needs to be started by animation loop
6916
6917 _p.stopped = false;
6918 this.hook(); // the animation loop will start the animation...
6919
6920 return this;
6921 },
6922 playing: function playing() {
6923 return this._private.playing;
6924 },
6925 apply: function apply() {
6926 var _p = this._private;
6927 _p.applying = true;
6928 _p.started = false; // needs to be started by animation loop
6929
6930 _p.stopped = false;
6931 this.hook(); // the animation loop will apply the animation at this progress
6932
6933 return this;
6934 },
6935 applying: function applying() {
6936 return this._private.applying;
6937 },
6938 pause: function pause() {
6939 var _p = this._private;
6940 _p.playing = false;
6941 _p.started = false;
6942 return this;
6943 },
6944 stop: function stop() {
6945 var _p = this._private;
6946 _p.playing = false;
6947 _p.started = false;
6948 _p.stopped = true; // to be removed from animation queues
6949
6950 return this;
6951 },
6952 rewind: function rewind() {
6953 return this.progress(0);
6954 },
6955 fastforward: function fastforward() {
6956 return this.progress(1);
6957 },
6958 time: function time(t) {
6959 var _p = this._private;
6960
6961 if (t === undefined) {
6962 return _p.progress * _p.duration;
6963 } else {
6964 return this.progress(t / _p.duration);
6965 }
6966 },
6967 progress: function progress(p) {
6968 var _p = this._private;
6969 var wasPlaying = _p.playing;
6970
6971 if (p === undefined) {
6972 return _p.progress;
6973 } else {
6974 if (wasPlaying) {
6975 this.pause();
6976 }
6977
6978 _p.progress = p;
6979 _p.started = false;
6980
6981 if (wasPlaying) {
6982 this.play();
6983 }
6984 }
6985
6986 return this;
6987 },
6988 completed: function completed() {
6989 return this._private.progress === 1;
6990 },
6991 reverse: function reverse() {
6992 var _p = this._private;
6993 var wasPlaying = _p.playing;
6994
6995 if (wasPlaying) {
6996 this.pause();
6997 }
6998
6999 _p.progress = 1 - _p.progress;
7000 _p.started = false;
7001
7002 var swap = function swap(a, b) {
7003 var _pa = _p[a];
7004
7005 if (_pa == null) {
7006 return;
7007 }
7008
7009 _p[a] = _p[b];
7010 _p[b] = _pa;
7011 };
7012
7013 swap('zoom', 'startZoom');
7014 swap('pan', 'startPan');
7015 swap('position', 'startPosition'); // swap styles
7016
7017 if (_p.style) {
7018 for (var i = 0; i < _p.style.length; i++) {
7019 var prop = _p.style[i];
7020 var name = prop.name;
7021 var startStyleProp = _p.startStyle[name];
7022 _p.startStyle[name] = prop;
7023 _p.style[i] = startStyleProp;
7024 }
7025 }
7026
7027 if (wasPlaying) {
7028 this.play();
7029 }
7030
7031 return this;
7032 },
7033 promise: function promise(type) {
7034 var _p = this._private;
7035 var arr;
7036
7037 switch (type) {
7038 case 'frame':
7039 arr = _p.frames;
7040 break;
7041
7042 default:
7043 case 'complete':
7044 case 'completed':
7045 arr = _p.completes;
7046 }
7047
7048 return new Promise$1(function (resolve, reject) {
7049 arr.push(function () {
7050 resolve();
7051 });
7052 });
7053 }
7054 });
7055 anifn.complete = anifn.completed;
7056 anifn.run = anifn.play;
7057 anifn.running = anifn.playing;
7058
7059 var define = {
7060 animated: function animated() {
7061 return function animatedImpl() {
7062 var self = this;
7063 var selfIsArrayLike = self.length !== undefined;
7064 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7065
7066 var cy = this._private.cy || this;
7067
7068 if (!cy.styleEnabled()) {
7069 return false;
7070 }
7071
7072 var ele = all[0];
7073
7074 if (ele) {
7075 return ele._private.animation.current.length > 0;
7076 }
7077 };
7078 },
7079 // animated
7080 clearQueue: function clearQueue() {
7081 return function clearQueueImpl() {
7082 var self = this;
7083 var selfIsArrayLike = self.length !== undefined;
7084 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7085
7086 var cy = this._private.cy || this;
7087
7088 if (!cy.styleEnabled()) {
7089 return this;
7090 }
7091
7092 for (var i = 0; i < all.length; i++) {
7093 var ele = all[i];
7094 ele._private.animation.queue = [];
7095 }
7096
7097 return this;
7098 };
7099 },
7100 // clearQueue
7101 delay: function delay() {
7102 return function delayImpl(time, complete) {
7103 var cy = this._private.cy || this;
7104
7105 if (!cy.styleEnabled()) {
7106 return this;
7107 }
7108
7109 return this.animate({
7110 delay: time,
7111 duration: time,
7112 complete: complete
7113 });
7114 };
7115 },
7116 // delay
7117 delayAnimation: function delayAnimation() {
7118 return function delayAnimationImpl(time, complete) {
7119 var cy = this._private.cy || this;
7120
7121 if (!cy.styleEnabled()) {
7122 return this;
7123 }
7124
7125 return this.animation({
7126 delay: time,
7127 duration: time,
7128 complete: complete
7129 });
7130 };
7131 },
7132 // delay
7133 animation: function animation() {
7134 return function animationImpl(properties, params) {
7135 var self = this;
7136 var selfIsArrayLike = self.length !== undefined;
7137 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7138
7139 var cy = this._private.cy || this;
7140 var isCore = !selfIsArrayLike;
7141 var isEles = !isCore;
7142
7143 if (!cy.styleEnabled()) {
7144 return this;
7145 }
7146
7147 var style = cy.style();
7148 properties = extend({}, properties, params);
7149 var propertiesEmpty = Object.keys(properties).length === 0;
7150
7151 if (propertiesEmpty) {
7152 return new Animation(all[0], properties); // nothing to animate
7153 }
7154
7155 if (properties.duration === undefined) {
7156 properties.duration = 400;
7157 }
7158
7159 switch (properties.duration) {
7160 case 'slow':
7161 properties.duration = 600;
7162 break;
7163
7164 case 'fast':
7165 properties.duration = 200;
7166 break;
7167 }
7168
7169 if (isEles) {
7170 properties.style = style.getPropsList(properties.style || properties.css);
7171 properties.css = undefined;
7172 }
7173
7174 if (isEles && properties.renderedPosition != null) {
7175 var rpos = properties.renderedPosition;
7176 var pan = cy.pan();
7177 var zoom = cy.zoom();
7178 properties.position = renderedToModelPosition(rpos, zoom, pan);
7179 } // override pan w/ panBy if set
7180
7181
7182 if (isCore && properties.panBy != null) {
7183 var panBy = properties.panBy;
7184 var cyPan = cy.pan();
7185 properties.pan = {
7186 x: cyPan.x + panBy.x,
7187 y: cyPan.y + panBy.y
7188 };
7189 } // override pan w/ center if set
7190
7191
7192 var center = properties.center || properties.centre;
7193
7194 if (isCore && center != null) {
7195 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
7196
7197 if (centerPan != null) {
7198 properties.pan = centerPan;
7199 }
7200 } // override pan & zoom w/ fit if set
7201
7202
7203 if (isCore && properties.fit != null) {
7204 var fit = properties.fit;
7205 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
7206
7207 if (fitVp != null) {
7208 properties.pan = fitVp.pan;
7209 properties.zoom = fitVp.zoom;
7210 }
7211 } // override zoom (& potentially pan) w/ zoom obj if set
7212
7213
7214 if (isCore && plainObject(properties.zoom)) {
7215 var vp = cy.getZoomedViewport(properties.zoom);
7216
7217 if (vp != null) {
7218 if (vp.zoomed) {
7219 properties.zoom = vp.zoom;
7220 }
7221
7222 if (vp.panned) {
7223 properties.pan = vp.pan;
7224 }
7225 } else {
7226 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
7227 }
7228 }
7229
7230 return new Animation(all[0], properties);
7231 };
7232 },
7233 // animate
7234 animate: function animate() {
7235 return function animateImpl(properties, params) {
7236 var self = this;
7237 var selfIsArrayLike = self.length !== undefined;
7238 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7239
7240 var cy = this._private.cy || this;
7241
7242 if (!cy.styleEnabled()) {
7243 return this;
7244 }
7245
7246 if (params) {
7247 properties = extend({}, properties, params);
7248 } // manually hook and run the animation
7249
7250
7251 for (var i = 0; i < all.length; i++) {
7252 var ele = all[i];
7253 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
7254 var ani = ele.animation(properties, queue ? {
7255 queue: true
7256 } : undefined);
7257 ani.play();
7258 }
7259
7260 return this; // chaining
7261 };
7262 },
7263 // animate
7264 stop: function stop() {
7265 return function stopImpl(clearQueue, jumpToEnd) {
7266 var self = this;
7267 var selfIsArrayLike = self.length !== undefined;
7268 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7269
7270 var cy = this._private.cy || this;
7271
7272 if (!cy.styleEnabled()) {
7273 return this;
7274 }
7275
7276 for (var i = 0; i < all.length; i++) {
7277 var ele = all[i];
7278 var _p = ele._private;
7279 var anis = _p.animation.current;
7280
7281 for (var j = 0; j < anis.length; j++) {
7282 var ani = anis[j];
7283 var ani_p = ani._private;
7284
7285 if (jumpToEnd) {
7286 // next iteration of the animation loop, the animation
7287 // will go straight to the end and be removed
7288 ani_p.duration = 0;
7289 }
7290 } // clear the queue of future animations
7291
7292
7293 if (clearQueue) {
7294 _p.animation.queue = [];
7295 }
7296
7297 if (!jumpToEnd) {
7298 _p.animation.current = [];
7299 }
7300 } // we have to notify (the animation loop doesn't do it for us on `stop`)
7301
7302
7303 cy.notify('draw');
7304 return this;
7305 };
7306 } // stop
7307
7308 }; // define
7309
7310 var define$1 = {
7311 // access data field
7312 data: function data(params) {
7313 var defaults = {
7314 field: 'data',
7315 bindingEvent: 'data',
7316 allowBinding: false,
7317 allowSetting: false,
7318 allowGetting: false,
7319 settingEvent: 'data',
7320 settingTriggersEvent: false,
7321 triggerFnName: 'trigger',
7322 immutableKeys: {},
7323 // key => true if immutable
7324 updateStyle: false,
7325 beforeGet: function beforeGet(self) {},
7326 beforeSet: function beforeSet(self, obj) {},
7327 onSet: function onSet(self) {},
7328 canSet: function canSet(self) {
7329 return true;
7330 }
7331 };
7332 params = extend({}, defaults, params);
7333 return function dataImpl(name, value) {
7334 var p = params;
7335 var self = this;
7336 var selfIsArrayLike = self.length !== undefined;
7337 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7338
7339 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
7340
7341 if (string(name)) {
7342 // set or get property
7343 // .data('foo')
7344 if (p.allowGetting && value === undefined) {
7345 // get
7346 var ret;
7347
7348 if (single) {
7349 p.beforeGet(single);
7350 ret = single._private[p.field][name];
7351 }
7352
7353 return ret; // .data('foo', 'bar')
7354 } else if (p.allowSetting && value !== undefined) {
7355 // set
7356 var valid = !p.immutableKeys[name];
7357
7358 if (valid) {
7359 var change = _defineProperty({}, name, value);
7360
7361 p.beforeSet(self, change);
7362
7363 for (var i = 0, l = all.length; i < l; i++) {
7364 var ele = all[i];
7365
7366 if (p.canSet(ele)) {
7367 ele._private[p.field][name] = value;
7368 }
7369 } // update mappers if asked
7370
7371
7372 if (p.updateStyle) {
7373 self.updateStyle();
7374 } // call onSet callback
7375
7376
7377 p.onSet(self);
7378
7379 if (p.settingTriggersEvent) {
7380 self[p.triggerFnName](p.settingEvent);
7381 }
7382 }
7383 } // .data({ 'foo': 'bar' })
7384
7385 } else if (p.allowSetting && plainObject(name)) {
7386 // extend
7387 var obj = name;
7388 var k, v;
7389 var keys = Object.keys(obj);
7390 p.beforeSet(self, obj);
7391
7392 for (var _i = 0; _i < keys.length; _i++) {
7393 k = keys[_i];
7394 v = obj[k];
7395
7396 var _valid = !p.immutableKeys[k];
7397
7398 if (_valid) {
7399 for (var j = 0; j < all.length; j++) {
7400 var _ele = all[j];
7401
7402 if (p.canSet(_ele)) {
7403 _ele._private[p.field][k] = v;
7404 }
7405 }
7406 }
7407 } // update mappers if asked
7408
7409
7410 if (p.updateStyle) {
7411 self.updateStyle();
7412 } // call onSet callback
7413
7414
7415 p.onSet(self);
7416
7417 if (p.settingTriggersEvent) {
7418 self[p.triggerFnName](p.settingEvent);
7419 } // .data(function(){ ... })
7420
7421 } else if (p.allowBinding && fn(name)) {
7422 // bind to event
7423 var fn$1 = name;
7424 self.on(p.bindingEvent, fn$1); // .data()
7425 } else if (p.allowGetting && name === undefined) {
7426 // get whole object
7427 var _ret;
7428
7429 if (single) {
7430 p.beforeGet(single);
7431 _ret = single._private[p.field];
7432 }
7433
7434 return _ret;
7435 }
7436
7437 return self; // maintain chainability
7438 }; // function
7439 },
7440 // data
7441 // remove data field
7442 removeData: function removeData(params) {
7443 var defaults = {
7444 field: 'data',
7445 event: 'data',
7446 triggerFnName: 'trigger',
7447 triggerEvent: false,
7448 immutableKeys: {} // key => true if immutable
7449
7450 };
7451 params = extend({}, defaults, params);
7452 return function removeDataImpl(names) {
7453 var p = params;
7454 var self = this;
7455 var selfIsArrayLike = self.length !== undefined;
7456 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7457 // .removeData('foo bar')
7458
7459 if (string(names)) {
7460 // then get the list of keys, and delete them
7461 var keys = names.split(/\s+/);
7462 var l = keys.length;
7463
7464 for (var i = 0; i < l; i++) {
7465 // delete each non-empty key
7466 var key = keys[i];
7467
7468 if (emptyString(key)) {
7469 continue;
7470 }
7471
7472 var valid = !p.immutableKeys[key]; // not valid if immutable
7473
7474 if (valid) {
7475 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
7476 all[i_a]._private[p.field][key] = undefined;
7477 }
7478 }
7479 }
7480
7481 if (p.triggerEvent) {
7482 self[p.triggerFnName](p.event);
7483 } // .removeData()
7484
7485 } else if (names === undefined) {
7486 // then delete all keys
7487 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
7488 var _privateFields = all[_i_a]._private[p.field];
7489
7490 var _keys = Object.keys(_privateFields);
7491
7492 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
7493 var _key = _keys[_i2];
7494 var validKeyToDelete = !p.immutableKeys[_key];
7495
7496 if (validKeyToDelete) {
7497 _privateFields[_key] = undefined;
7498 }
7499 }
7500 }
7501
7502 if (p.triggerEvent) {
7503 self[p.triggerFnName](p.event);
7504 }
7505 }
7506
7507 return self; // maintain chaining
7508 }; // function
7509 } // removeData
7510
7511 }; // define
7512
7513 var define$2 = {
7514 eventAliasesOn: function eventAliasesOn(proto) {
7515 var p = proto;
7516 p.addListener = p.listen = p.bind = p.on;
7517 p.unlisten = p.unbind = p.off = p.removeListener;
7518 p.trigger = p.emit; // this is just a wrapper alias of .on()
7519
7520 p.pon = p.promiseOn = function (events, selector) {
7521 var self = this;
7522 var args = Array.prototype.slice.call(arguments, 0);
7523 return new Promise$1(function (resolve, reject) {
7524 var callback = function callback(e) {
7525 self.off.apply(self, offArgs);
7526 resolve(e);
7527 };
7528
7529 var onArgs = args.concat([callback]);
7530 var offArgs = onArgs.concat([]);
7531 self.on.apply(self, onArgs);
7532 });
7533 };
7534 }
7535 }; // define
7536
7537 // use this module to cherry pick functions into your prototype
7538 var define$3 = {};
7539 [define, define$1, define$2].forEach(function (m) {
7540 extend(define$3, m);
7541 });
7542
7543 var elesfn$d = {
7544 animate: define$3.animate(),
7545 animation: define$3.animation(),
7546 animated: define$3.animated(),
7547 clearQueue: define$3.clearQueue(),
7548 delay: define$3.delay(),
7549 delayAnimation: define$3.delayAnimation(),
7550 stop: define$3.stop()
7551 };
7552
7553 var elesfn$e = {
7554 classes: function classes(_classes) {
7555 var self = this;
7556
7557 if (_classes === undefined) {
7558 var ret = [];
7559
7560 self[0]._private.classes.forEach(function (cls) {
7561 return ret.push(cls);
7562 });
7563
7564 return ret;
7565 } else if (!array(_classes)) {
7566 // extract classes from string
7567 _classes = (_classes || '').match(/\S+/g) || [];
7568 }
7569
7570 var changed = [];
7571 var classesSet = new Set$1(_classes); // check and update each ele
7572
7573 for (var j = 0; j < self.length; j++) {
7574 var ele = self[j];
7575 var _p = ele._private;
7576 var eleClasses = _p.classes;
7577 var changedEle = false; // check if ele has all of the passed classes
7578
7579 for (var i = 0; i < _classes.length; i++) {
7580 var cls = _classes[i];
7581 var eleHasClass = eleClasses.has(cls);
7582
7583 if (!eleHasClass) {
7584 changedEle = true;
7585 break;
7586 }
7587 } // check if ele has classes outside of those passed
7588
7589
7590 if (!changedEle) {
7591 changedEle = eleClasses.size !== _classes.length;
7592 }
7593
7594 if (changedEle) {
7595 _p.classes = classesSet;
7596 changed.push(ele);
7597 }
7598 } // trigger update style on those eles that had class changes
7599
7600
7601 if (changed.length > 0) {
7602 this.spawn(changed).updateStyle().emit('class');
7603 }
7604
7605 return self;
7606 },
7607 addClass: function addClass(classes) {
7608 return this.toggleClass(classes, true);
7609 },
7610 hasClass: function hasClass(className) {
7611 var ele = this[0];
7612 return ele != null && ele._private.classes.has(className);
7613 },
7614 toggleClass: function toggleClass(classes, toggle) {
7615 if (!array(classes)) {
7616 // extract classes from string
7617 classes = classes.match(/\S+/g) || [];
7618 }
7619
7620 var self = this;
7621 var toggleUndefd = toggle === undefined;
7622 var changed = []; // eles who had classes changed
7623
7624 for (var i = 0, il = self.length; i < il; i++) {
7625 var ele = self[i];
7626 var eleClasses = ele._private.classes;
7627 var changedEle = false;
7628
7629 for (var j = 0; j < classes.length; j++) {
7630 var cls = classes[j];
7631 var hasClass = eleClasses.has(cls);
7632 var changedNow = false;
7633
7634 if (toggle || toggleUndefd && !hasClass) {
7635 eleClasses.add(cls);
7636 changedNow = true;
7637 } else if (!toggle || toggleUndefd && hasClass) {
7638 eleClasses["delete"](cls);
7639 changedNow = true;
7640 }
7641
7642 if (!changedEle && changedNow) {
7643 changed.push(ele);
7644 changedEle = true;
7645 }
7646 } // for j classes
7647
7648 } // for i eles
7649 // trigger update style on those eles that had class changes
7650
7651
7652 if (changed.length > 0) {
7653 this.spawn(changed).updateStyle().emit('class');
7654 }
7655
7656 return self;
7657 },
7658 removeClass: function removeClass(classes) {
7659 return this.toggleClass(classes, false);
7660 },
7661 flashClass: function flashClass(classes, duration) {
7662 var self = this;
7663
7664 if (duration == null) {
7665 duration = 250;
7666 } else if (duration === 0) {
7667 return self; // nothing to do really
7668 }
7669
7670 self.addClass(classes);
7671 setTimeout(function () {
7672 self.removeClass(classes);
7673 }, duration);
7674 return self;
7675 }
7676 };
7677 elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
7678
7679 var tokens = {
7680 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
7681 // chars we need to escape in let names, etc
7682 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
7683 // binary comparison op (used in data selectors)
7684 boolOp: '\\?|\\!|\\^',
7685 // boolean (unary) operators (used in data selectors)
7686 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
7687 // string literals (used in data selectors) -- doublequotes | singlequotes
7688 number: number$1,
7689 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
7690 meta: 'degree|indegree|outdegree',
7691 // allowed metadata fields (i.e. allowed functions to use from Collection)
7692 separator: '\\s*,\\s*',
7693 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
7694 descendant: '\\s+',
7695 child: '\\s+>\\s+',
7696 subject: '\\$',
7697 group: 'node|edge|\\*',
7698 directedEdge: '\\s+->\\s+',
7699 undirectedEdge: '\\s+<->\\s+'
7700 };
7701 tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
7702
7703 tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7704
7705 tokens.className = tokens.variable; // a class name (follows variable conventions)
7706
7707 tokens.id = tokens.variable; // an element id (follows variable conventions)
7708
7709 (function () {
7710 var ops, op, i; // add @ variants to comparatorOp
7711
7712 ops = tokens.comparatorOp.split('|');
7713
7714 for (i = 0; i < ops.length; i++) {
7715 op = ops[i];
7716 tokens.comparatorOp += '|@' + op;
7717 } // add ! variants to comparatorOp
7718
7719
7720 ops = tokens.comparatorOp.split('|');
7721
7722 for (i = 0; i < ops.length; i++) {
7723 op = ops[i];
7724
7725 if (op.indexOf('!') >= 0) {
7726 continue;
7727 } // skip ops that explicitly contain !
7728
7729
7730 if (op === '=') {
7731 continue;
7732 } // skip = b/c != is explicitly defined
7733
7734
7735 tokens.comparatorOp += '|\\!' + op;
7736 }
7737 })();
7738
7739 /**
7740 * Make a new query object
7741 *
7742 * @prop type {Type} The type enum (int) of the query
7743 * @prop checks List of checks to make against an ele to test for a match
7744 */
7745 var newQuery = function newQuery() {
7746 return {
7747 checks: []
7748 };
7749 };
7750
7751 /**
7752 * A check type enum-like object. Uses integer values for fast match() lookup.
7753 * The ordering does not matter as long as the ints are unique.
7754 */
7755 var Type = {
7756 /** E.g. node */
7757 GROUP: 0,
7758
7759 /** A collection of elements */
7760 COLLECTION: 1,
7761
7762 /** A filter(ele) function */
7763 FILTER: 2,
7764
7765 /** E.g. [foo > 1] */
7766 DATA_COMPARE: 3,
7767
7768 /** E.g. [foo] */
7769 DATA_EXIST: 4,
7770
7771 /** E.g. [?foo] */
7772 DATA_BOOL: 5,
7773
7774 /** E.g. [[degree > 2]] */
7775 META_COMPARE: 6,
7776
7777 /** E.g. :selected */
7778 STATE: 7,
7779
7780 /** E.g. #foo */
7781 ID: 8,
7782
7783 /** E.g. .foo */
7784 CLASS: 9,
7785
7786 /** E.g. #foo <-> #bar */
7787 UNDIRECTED_EDGE: 10,
7788
7789 /** E.g. #foo -> #bar */
7790 DIRECTED_EDGE: 11,
7791
7792 /** E.g. $#foo -> #bar */
7793 NODE_SOURCE: 12,
7794
7795 /** E.g. #foo -> $#bar */
7796 NODE_TARGET: 13,
7797
7798 /** E.g. $#foo <-> #bar */
7799 NODE_NEIGHBOR: 14,
7800
7801 /** E.g. #foo > #bar */
7802 CHILD: 15,
7803
7804 /** E.g. #foo #bar */
7805 DESCENDANT: 16,
7806
7807 /** E.g. $#foo > #bar */
7808 PARENT: 17,
7809
7810 /** E.g. $#foo #bar */
7811 ANCESTOR: 18,
7812
7813 /** E.g. #foo > $bar > #baz */
7814 COMPOUND_SPLIT: 19,
7815
7816 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7817 TRUE: 20
7818 };
7819
7820 var stateSelectors = [{
7821 selector: ':selected',
7822 matches: function matches(ele) {
7823 return ele.selected();
7824 }
7825 }, {
7826 selector: ':unselected',
7827 matches: function matches(ele) {
7828 return !ele.selected();
7829 }
7830 }, {
7831 selector: ':selectable',
7832 matches: function matches(ele) {
7833 return ele.selectable();
7834 }
7835 }, {
7836 selector: ':unselectable',
7837 matches: function matches(ele) {
7838 return !ele.selectable();
7839 }
7840 }, {
7841 selector: ':locked',
7842 matches: function matches(ele) {
7843 return ele.locked();
7844 }
7845 }, {
7846 selector: ':unlocked',
7847 matches: function matches(ele) {
7848 return !ele.locked();
7849 }
7850 }, {
7851 selector: ':visible',
7852 matches: function matches(ele) {
7853 return ele.visible();
7854 }
7855 }, {
7856 selector: ':hidden',
7857 matches: function matches(ele) {
7858 return !ele.visible();
7859 }
7860 }, {
7861 selector: ':transparent',
7862 matches: function matches(ele) {
7863 return ele.transparent();
7864 }
7865 }, {
7866 selector: ':grabbed',
7867 matches: function matches(ele) {
7868 return ele.grabbed();
7869 }
7870 }, {
7871 selector: ':free',
7872 matches: function matches(ele) {
7873 return !ele.grabbed();
7874 }
7875 }, {
7876 selector: ':removed',
7877 matches: function matches(ele) {
7878 return ele.removed();
7879 }
7880 }, {
7881 selector: ':inside',
7882 matches: function matches(ele) {
7883 return !ele.removed();
7884 }
7885 }, {
7886 selector: ':grabbable',
7887 matches: function matches(ele) {
7888 return ele.grabbable();
7889 }
7890 }, {
7891 selector: ':ungrabbable',
7892 matches: function matches(ele) {
7893 return !ele.grabbable();
7894 }
7895 }, {
7896 selector: ':animated',
7897 matches: function matches(ele) {
7898 return ele.animated();
7899 }
7900 }, {
7901 selector: ':unanimated',
7902 matches: function matches(ele) {
7903 return !ele.animated();
7904 }
7905 }, {
7906 selector: ':parent',
7907 matches: function matches(ele) {
7908 return ele.isParent();
7909 }
7910 }, {
7911 selector: ':childless',
7912 matches: function matches(ele) {
7913 return ele.isChildless();
7914 }
7915 }, {
7916 selector: ':child',
7917 matches: function matches(ele) {
7918 return ele.isChild();
7919 }
7920 }, {
7921 selector: ':orphan',
7922 matches: function matches(ele) {
7923 return ele.isOrphan();
7924 }
7925 }, {
7926 selector: ':nonorphan',
7927 matches: function matches(ele) {
7928 return ele.isChild();
7929 }
7930 }, {
7931 selector: ':compound',
7932 matches: function matches(ele) {
7933 if (ele.isNode()) {
7934 return ele.isParent();
7935 } else {
7936 return ele.source().isParent() || ele.target().isParent();
7937 }
7938 }
7939 }, {
7940 selector: ':loop',
7941 matches: function matches(ele) {
7942 return ele.isLoop();
7943 }
7944 }, {
7945 selector: ':simple',
7946 matches: function matches(ele) {
7947 return ele.isSimple();
7948 }
7949 }, {
7950 selector: ':active',
7951 matches: function matches(ele) {
7952 return ele.active();
7953 }
7954 }, {
7955 selector: ':inactive',
7956 matches: function matches(ele) {
7957 return !ele.active();
7958 }
7959 }, {
7960 selector: ':backgrounding',
7961 matches: function matches(ele) {
7962 return ele.backgrounding();
7963 }
7964 }, {
7965 selector: ':nonbackgrounding',
7966 matches: function matches(ele) {
7967 return !ele.backgrounding();
7968 }
7969 }].sort(function (a, b) {
7970 // n.b. selectors that are starting substrings of others must have the longer ones first
7971 return descending(a.selector, b.selector);
7972 });
7973
7974 var lookup = function () {
7975 var selToFn = {};
7976 var s;
7977
7978 for (var i = 0; i < stateSelectors.length; i++) {
7979 s = stateSelectors[i];
7980 selToFn[s.selector] = s.matches;
7981 }
7982
7983 return selToFn;
7984 }();
7985
7986 var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7987 return lookup[sel](ele);
7988 };
7989 var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7990 return s.selector;
7991 }).join('|') + ')';
7992
7993 // so that values get compared properly in Selector.filter()
7994
7995 var cleanMetaChars = function cleanMetaChars(str) {
7996 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7997 return $1;
7998 });
7999 };
8000
8001 var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
8002 selector[selector.length - 1] = replacementQuery;
8003 }; // NOTE: add new expression syntax here to have it recognised by the parser;
8004 // - a query contains all adjacent (i.e. no separator in between) expressions;
8005 // - the current query is stored in selector[i]
8006 // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
8007
8008
8009 var exprs = [{
8010 name: 'group',
8011 // just used for identifying when debugging
8012 query: true,
8013 regex: '(' + tokens.group + ')',
8014 populate: function populate(selector, query, _ref) {
8015 var _ref2 = _slicedToArray(_ref, 1),
8016 group = _ref2[0];
8017
8018 query.checks.push({
8019 type: Type.GROUP,
8020 value: group === '*' ? group : group + 's'
8021 });
8022 }
8023 }, {
8024 name: 'state',
8025 query: true,
8026 regex: stateSelectorRegex,
8027 populate: function populate(selector, query, _ref3) {
8028 var _ref4 = _slicedToArray(_ref3, 1),
8029 state = _ref4[0];
8030
8031 query.checks.push({
8032 type: Type.STATE,
8033 value: state
8034 });
8035 }
8036 }, {
8037 name: 'id',
8038 query: true,
8039 regex: '\\#(' + tokens.id + ')',
8040 populate: function populate(selector, query, _ref5) {
8041 var _ref6 = _slicedToArray(_ref5, 1),
8042 id = _ref6[0];
8043
8044 query.checks.push({
8045 type: Type.ID,
8046 value: cleanMetaChars(id)
8047 });
8048 }
8049 }, {
8050 name: 'className',
8051 query: true,
8052 regex: '\\.(' + tokens.className + ')',
8053 populate: function populate(selector, query, _ref7) {
8054 var _ref8 = _slicedToArray(_ref7, 1),
8055 className = _ref8[0];
8056
8057 query.checks.push({
8058 type: Type.CLASS,
8059 value: cleanMetaChars(className)
8060 });
8061 }
8062 }, {
8063 name: 'dataExists',
8064 query: true,
8065 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
8066 populate: function populate(selector, query, _ref9) {
8067 var _ref10 = _slicedToArray(_ref9, 1),
8068 variable = _ref10[0];
8069
8070 query.checks.push({
8071 type: Type.DATA_EXIST,
8072 field: cleanMetaChars(variable)
8073 });
8074 }
8075 }, {
8076 name: 'dataCompare',
8077 query: true,
8078 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
8079 populate: function populate(selector, query, _ref11) {
8080 var _ref12 = _slicedToArray(_ref11, 3),
8081 variable = _ref12[0],
8082 comparatorOp = _ref12[1],
8083 value = _ref12[2];
8084
8085 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
8086
8087 if (valueIsString) {
8088 value = value.substring(1, value.length - 1);
8089 } else {
8090 value = parseFloat(value);
8091 }
8092
8093 query.checks.push({
8094 type: Type.DATA_COMPARE,
8095 field: cleanMetaChars(variable),
8096 operator: comparatorOp,
8097 value: value
8098 });
8099 }
8100 }, {
8101 name: 'dataBool',
8102 query: true,
8103 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
8104 populate: function populate(selector, query, _ref13) {
8105 var _ref14 = _slicedToArray(_ref13, 2),
8106 boolOp = _ref14[0],
8107 variable = _ref14[1];
8108
8109 query.checks.push({
8110 type: Type.DATA_BOOL,
8111 field: cleanMetaChars(variable),
8112 operator: boolOp
8113 });
8114 }
8115 }, {
8116 name: 'metaCompare',
8117 query: true,
8118 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
8119 populate: function populate(selector, query, _ref15) {
8120 var _ref16 = _slicedToArray(_ref15, 3),
8121 meta = _ref16[0],
8122 comparatorOp = _ref16[1],
8123 number = _ref16[2];
8124
8125 query.checks.push({
8126 type: Type.META_COMPARE,
8127 field: cleanMetaChars(meta),
8128 operator: comparatorOp,
8129 value: parseFloat(number)
8130 });
8131 }
8132 }, {
8133 name: 'nextQuery',
8134 separator: true,
8135 regex: tokens.separator,
8136 populate: function populate(selector, query) {
8137 var currentSubject = selector.currentSubject;
8138 var edgeCount = selector.edgeCount;
8139 var compoundCount = selector.compoundCount;
8140 var lastQ = selector[selector.length - 1];
8141
8142 if (currentSubject != null) {
8143 lastQ.subject = currentSubject;
8144 selector.currentSubject = null;
8145 }
8146
8147 lastQ.edgeCount = edgeCount;
8148 lastQ.compoundCount = compoundCount;
8149 selector.edgeCount = 0;
8150 selector.compoundCount = 0; // go on to next query
8151
8152 var nextQuery = selector[selector.length++] = newQuery();
8153 return nextQuery; // this is the new query to be filled by the following exprs
8154 }
8155 }, {
8156 name: 'directedEdge',
8157 separator: true,
8158 regex: tokens.directedEdge,
8159 populate: function populate(selector, query) {
8160 if (selector.currentSubject == null) {
8161 // undirected edge
8162 var edgeQuery = newQuery();
8163 var source = query;
8164 var target = newQuery();
8165 edgeQuery.checks.push({
8166 type: Type.DIRECTED_EDGE,
8167 source: source,
8168 target: target
8169 }); // the query in the selector should be the edge rather than the source
8170
8171 replaceLastQuery(selector, query, edgeQuery);
8172 selector.edgeCount++; // we're now populating the target query with expressions that follow
8173
8174 return target;
8175 } else {
8176 // source/target
8177 var srcTgtQ = newQuery();
8178 var _source = query;
8179
8180 var _target = newQuery();
8181
8182 srcTgtQ.checks.push({
8183 type: Type.NODE_SOURCE,
8184 source: _source,
8185 target: _target
8186 }); // the query in the selector should be the neighbourhood rather than the node
8187
8188 replaceLastQuery(selector, query, srcTgtQ);
8189 selector.edgeCount++;
8190 return _target; // now populating the target with the following expressions
8191 }
8192 }
8193 }, {
8194 name: 'undirectedEdge',
8195 separator: true,
8196 regex: tokens.undirectedEdge,
8197 populate: function populate(selector, query) {
8198 if (selector.currentSubject == null) {
8199 // undirected edge
8200 var edgeQuery = newQuery();
8201 var source = query;
8202 var target = newQuery();
8203 edgeQuery.checks.push({
8204 type: Type.UNDIRECTED_EDGE,
8205 nodes: [source, target]
8206 }); // the query in the selector should be the edge rather than the source
8207
8208 replaceLastQuery(selector, query, edgeQuery);
8209 selector.edgeCount++; // we're now populating the target query with expressions that follow
8210
8211 return target;
8212 } else {
8213 // neighbourhood
8214 var nhoodQ = newQuery();
8215 var node = query;
8216 var neighbor = newQuery();
8217 nhoodQ.checks.push({
8218 type: Type.NODE_NEIGHBOR,
8219 node: node,
8220 neighbor: neighbor
8221 }); // the query in the selector should be the neighbourhood rather than the node
8222
8223 replaceLastQuery(selector, query, nhoodQ);
8224 return neighbor; // now populating the neighbor with following expressions
8225 }
8226 }
8227 }, {
8228 name: 'child',
8229 separator: true,
8230 regex: tokens.child,
8231 populate: function populate(selector, query) {
8232 if (selector.currentSubject == null) {
8233 // default: child query
8234 var parentChildQuery = newQuery();
8235 var child = newQuery();
8236 var parent = selector[selector.length - 1];
8237 parentChildQuery.checks.push({
8238 type: Type.CHILD,
8239 parent: parent,
8240 child: child
8241 }); // the query in the selector should be the '>' itself
8242
8243 replaceLastQuery(selector, query, parentChildQuery);
8244 selector.compoundCount++; // we're now populating the child query with expressions that follow
8245
8246 return child;
8247 } else if (selector.currentSubject === query) {
8248 // compound split query
8249 var compound = newQuery();
8250 var left = selector[selector.length - 1];
8251 var right = newQuery();
8252 var subject = newQuery();
8253
8254 var _child = newQuery();
8255
8256 var _parent = newQuery(); // set up the root compound q
8257
8258
8259 compound.checks.push({
8260 type: Type.COMPOUND_SPLIT,
8261 left: left,
8262 right: right,
8263 subject: subject
8264 }); // populate the subject and replace the q at the old spot (within left) with TRUE
8265
8266 subject.checks = query.checks; // take the checks from the left
8267
8268 query.checks = [{
8269 type: Type.TRUE
8270 }]; // checks under left refs the subject implicitly
8271 // set up the right q
8272
8273 _parent.checks.push({
8274 type: Type.TRUE
8275 }); // parent implicitly refs the subject
8276
8277
8278 right.checks.push({
8279 type: Type.PARENT,
8280 // type is swapped on right side queries
8281 parent: _parent,
8282 child: _child // empty for now
8283
8284 });
8285 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
8286
8287 selector.currentSubject = subject;
8288 selector.compoundCount++;
8289 return _child; // now populating the right side's child
8290 } else {
8291 // parent query
8292 // info for parent query
8293 var _parent2 = newQuery();
8294
8295 var _child2 = newQuery();
8296
8297 var pcQChecks = [{
8298 type: Type.PARENT,
8299 parent: _parent2,
8300 child: _child2
8301 }]; // the parent-child query takes the place of the query previously being populated
8302
8303 _parent2.checks = query.checks; // the previous query contains the checks for the parent
8304
8305 query.checks = pcQChecks; // pc query takes over
8306
8307 selector.compoundCount++;
8308 return _child2; // we're now populating the child
8309 }
8310 }
8311 }, {
8312 name: 'descendant',
8313 separator: true,
8314 regex: tokens.descendant,
8315 populate: function populate(selector, query) {
8316 if (selector.currentSubject == null) {
8317 // default: descendant query
8318 var ancChQuery = newQuery();
8319 var descendant = newQuery();
8320 var ancestor = selector[selector.length - 1];
8321 ancChQuery.checks.push({
8322 type: Type.DESCENDANT,
8323 ancestor: ancestor,
8324 descendant: descendant
8325 }); // the query in the selector should be the '>' itself
8326
8327 replaceLastQuery(selector, query, ancChQuery);
8328 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
8329
8330 return descendant;
8331 } else if (selector.currentSubject === query) {
8332 // compound split query
8333 var compound = newQuery();
8334 var left = selector[selector.length - 1];
8335 var right = newQuery();
8336 var subject = newQuery();
8337
8338 var _descendant = newQuery();
8339
8340 var _ancestor = newQuery(); // set up the root compound q
8341
8342
8343 compound.checks.push({
8344 type: Type.COMPOUND_SPLIT,
8345 left: left,
8346 right: right,
8347 subject: subject
8348 }); // populate the subject and replace the q at the old spot (within left) with TRUE
8349
8350 subject.checks = query.checks; // take the checks from the left
8351
8352 query.checks = [{
8353 type: Type.TRUE
8354 }]; // checks under left refs the subject implicitly
8355 // set up the right q
8356
8357 _ancestor.checks.push({
8358 type: Type.TRUE
8359 }); // ancestor implicitly refs the subject
8360
8361
8362 right.checks.push({
8363 type: Type.ANCESTOR,
8364 // type is swapped on right side queries
8365 ancestor: _ancestor,
8366 descendant: _descendant // empty for now
8367
8368 });
8369 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
8370
8371 selector.currentSubject = subject;
8372 selector.compoundCount++;
8373 return _descendant; // now populating the right side's descendant
8374 } else {
8375 // ancestor query
8376 // info for parent query
8377 var _ancestor2 = newQuery();
8378
8379 var _descendant2 = newQuery();
8380
8381 var adQChecks = [{
8382 type: Type.ANCESTOR,
8383 ancestor: _ancestor2,
8384 descendant: _descendant2
8385 }]; // the parent-child query takes the place of the query previously being populated
8386
8387 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
8388
8389 query.checks = adQChecks; // pc query takes over
8390
8391 selector.compoundCount++;
8392 return _descendant2; // we're now populating the child
8393 }
8394 }
8395 }, {
8396 name: 'subject',
8397 modifier: true,
8398 regex: tokens.subject,
8399 populate: function populate(selector, query) {
8400 if (selector.currentSubject != null && selector.currentSubject !== query) {
8401 warn('Redefinition of subject in selector `' + selector.toString() + '`');
8402 return false;
8403 }
8404
8405 selector.currentSubject = query;
8406 var topQ = selector[selector.length - 1];
8407 var topChk = topQ.checks[0];
8408 var topType = topChk == null ? null : topChk.type;
8409
8410 if (topType === Type.DIRECTED_EDGE) {
8411 // directed edge with subject on the target
8412 // change to target node check
8413 topChk.type = Type.NODE_TARGET;
8414 } else if (topType === Type.UNDIRECTED_EDGE) {
8415 // undirected edge with subject on the second node
8416 // change to neighbor check
8417 topChk.type = Type.NODE_NEIGHBOR;
8418 topChk.node = topChk.nodes[1]; // second node is subject
8419
8420 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
8421
8422 topChk.nodes = null;
8423 }
8424 }
8425 }];
8426 exprs.forEach(function (e) {
8427 return e.regexObj = new RegExp('^' + e.regex);
8428 });
8429
8430 /**
8431 * Of all the expressions, find the first match in the remaining text.
8432 * @param {string} remaining The remaining text to parse
8433 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
8434 */
8435
8436 var consumeExpr = function consumeExpr(remaining) {
8437 var expr;
8438 var match;
8439 var name;
8440
8441 for (var j = 0; j < exprs.length; j++) {
8442 var e = exprs[j];
8443 var n = e.name;
8444 var m = remaining.match(e.regexObj);
8445
8446 if (m != null) {
8447 match = m;
8448 expr = e;
8449 name = n;
8450 var consumed = m[0];
8451 remaining = remaining.substring(consumed.length);
8452 break; // we've consumed one expr, so we can return now
8453 }
8454 }
8455
8456 return {
8457 expr: expr,
8458 match: match,
8459 name: name,
8460 remaining: remaining
8461 };
8462 };
8463 /**
8464 * Consume all the leading whitespace
8465 * @param {string} remaining The text to consume
8466 * @returns The text with the leading whitespace removed
8467 */
8468
8469
8470 var consumeWhitespace = function consumeWhitespace(remaining) {
8471 var match = remaining.match(/^\s+/);
8472
8473 if (match) {
8474 var consumed = match[0];
8475 remaining = remaining.substring(consumed.length);
8476 }
8477
8478 return remaining;
8479 };
8480 /**
8481 * Parse the string and store the parsed representation in the Selector.
8482 * @param {string} selector The selector string
8483 * @returns `true` if the selector was successfully parsed, `false` otherwise
8484 */
8485
8486
8487 var parse = function parse(selector) {
8488 var self = this;
8489 var remaining = self.inputText = selector;
8490 var currentQuery = self[0] = newQuery();
8491 self.length = 1;
8492 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
8493
8494 for (;;) {
8495 var exprInfo = consumeExpr(remaining);
8496
8497 if (exprInfo.expr == null) {
8498 warn('The selector `' + selector + '`is invalid');
8499 return false;
8500 } else {
8501 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
8502
8503 var ret = exprInfo.expr.populate(self, currentQuery, args);
8504
8505 if (ret === false) {
8506 return false; // exit if population failed
8507 } else if (ret != null) {
8508 currentQuery = ret; // change the current query to be filled if the expr specifies
8509 }
8510 }
8511
8512 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
8513
8514 if (remaining.match(/^\s*$/)) {
8515 break;
8516 }
8517 }
8518
8519 var lastQ = self[self.length - 1];
8520
8521 if (self.currentSubject != null) {
8522 lastQ.subject = self.currentSubject;
8523 }
8524
8525 lastQ.edgeCount = self.edgeCount;
8526 lastQ.compoundCount = self.compoundCount;
8527
8528 for (var i = 0; i < self.length; i++) {
8529 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
8530
8531 if (q.compoundCount > 0 && q.edgeCount > 0) {
8532 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
8533 return false;
8534 }
8535
8536 if (q.edgeCount > 1) {
8537 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
8538 return false;
8539 } else if (q.edgeCount === 1) {
8540 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.');
8541 }
8542 }
8543
8544 return true; // success
8545 };
8546 /**
8547 * Get the selector represented as a string. This value uses default formatting,
8548 * so things like spacing may differ from the input text passed to the constructor.
8549 * @returns {string} The selector string
8550 */
8551
8552
8553 var toString = function toString() {
8554 if (this.toStringCache != null) {
8555 return this.toStringCache;
8556 }
8557
8558 var clean = function clean(obj) {
8559 if (obj == null) {
8560 return '';
8561 } else {
8562 return obj;
8563 }
8564 };
8565
8566 var cleanVal = function cleanVal(val) {
8567 if (string(val)) {
8568 return '"' + val + '"';
8569 } else {
8570 return clean(val);
8571 }
8572 };
8573
8574 var space = function space(val) {
8575 return ' ' + val + ' ';
8576 };
8577
8578 var checkToString = function checkToString(check, subject) {
8579 var type = check.type,
8580 value = check.value;
8581
8582 switch (type) {
8583 case Type.GROUP:
8584 {
8585 var group = clean(value);
8586 return group.substring(0, group.length - 1);
8587 }
8588
8589 case Type.DATA_COMPARE:
8590 {
8591 var field = check.field,
8592 operator = check.operator;
8593 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
8594 }
8595
8596 case Type.DATA_BOOL:
8597 {
8598 var _operator = check.operator,
8599 _field = check.field;
8600 return '[' + clean(_operator) + _field + ']';
8601 }
8602
8603 case Type.DATA_EXIST:
8604 {
8605 var _field2 = check.field;
8606 return '[' + _field2 + ']';
8607 }
8608
8609 case Type.META_COMPARE:
8610 {
8611 var _operator2 = check.operator,
8612 _field3 = check.field;
8613 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
8614 }
8615
8616 case Type.STATE:
8617 {
8618 return value;
8619 }
8620
8621 case Type.ID:
8622 {
8623 return '#' + value;
8624 }
8625
8626 case Type.CLASS:
8627 {
8628 return '.' + value;
8629 }
8630
8631 case Type.PARENT:
8632 case Type.CHILD:
8633 {
8634 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
8635 }
8636
8637 case Type.ANCESTOR:
8638 case Type.DESCENDANT:
8639 {
8640 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
8641 }
8642
8643 case Type.COMPOUND_SPLIT:
8644 {
8645 var lhs = queryToString(check.left, subject);
8646 var sub = queryToString(check.subject, subject);
8647 var rhs = queryToString(check.right, subject);
8648 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
8649 }
8650
8651 case Type.TRUE:
8652 {
8653 return '';
8654 }
8655 }
8656 };
8657
8658 var queryToString = function queryToString(query, subject) {
8659 return query.checks.reduce(function (str, chk, i) {
8660 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
8661 }, '');
8662 };
8663
8664 var str = '';
8665
8666 for (var i = 0; i < this.length; i++) {
8667 var query = this[i];
8668 str += queryToString(query, query.subject);
8669
8670 if (this.length > 1 && i < this.length - 1) {
8671 str += ', ';
8672 }
8673 }
8674
8675 this.toStringCache = str;
8676 return str;
8677 };
8678 var parse$1 = {
8679 parse: parse,
8680 toString: toString
8681 };
8682
8683 var valCmp = function valCmp(fieldVal, operator, value) {
8684 var matches;
8685 var isFieldStr = string(fieldVal);
8686 var isFieldNum = number(fieldVal);
8687 var isValStr = string(value);
8688 var fieldStr, valStr;
8689 var caseInsensitive = false;
8690 var notExpr = false;
8691 var isIneqCmp = false;
8692
8693 if (operator.indexOf('!') >= 0) {
8694 operator = operator.replace('!', '');
8695 notExpr = true;
8696 }
8697
8698 if (operator.indexOf('@') >= 0) {
8699 operator = operator.replace('@', '');
8700 caseInsensitive = true;
8701 }
8702
8703 if (isFieldStr || isValStr || caseInsensitive) {
8704 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8705 valStr = '' + value;
8706 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8707 // even if we're comparing numbers
8708
8709
8710 if (caseInsensitive) {
8711 fieldVal = fieldStr = fieldStr.toLowerCase();
8712 value = valStr = valStr.toLowerCase();
8713 }
8714
8715 switch (operator) {
8716 case '*=':
8717 matches = fieldStr.indexOf(valStr) >= 0;
8718 break;
8719
8720 case '$=':
8721 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8722 break;
8723
8724 case '^=':
8725 matches = fieldStr.indexOf(valStr) === 0;
8726 break;
8727
8728 case '=':
8729 matches = fieldVal === value;
8730 break;
8731
8732 case '>':
8733 isIneqCmp = true;
8734 matches = fieldVal > value;
8735 break;
8736
8737 case '>=':
8738 isIneqCmp = true;
8739 matches = fieldVal >= value;
8740 break;
8741
8742 case '<':
8743 isIneqCmp = true;
8744 matches = fieldVal < value;
8745 break;
8746
8747 case '<=':
8748 isIneqCmp = true;
8749 matches = fieldVal <= value;
8750 break;
8751
8752 default:
8753 matches = false;
8754 break;
8755 } // apply the not op, but null vals for inequalities should always stay non-matching
8756
8757
8758 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8759 matches = !matches;
8760 }
8761
8762 return matches;
8763 };
8764 var boolCmp = function boolCmp(fieldVal, operator) {
8765 switch (operator) {
8766 case '?':
8767 return fieldVal ? true : false;
8768
8769 case '!':
8770 return fieldVal ? false : true;
8771
8772 case '^':
8773 return fieldVal === undefined;
8774 }
8775 };
8776 var existCmp = function existCmp(fieldVal) {
8777 return fieldVal !== undefined;
8778 };
8779 var data = function data(ele, field) {
8780 return ele.data(field);
8781 };
8782 var meta = function meta(ele, field) {
8783 return ele[field]();
8784 };
8785
8786 /** A lookup of `match(check, ele)` functions by `Type` int */
8787
8788 var match = [];
8789 /**
8790 * Returns whether the query matches for the element
8791 * @param query The `{ type, value, ... }` query object
8792 * @param ele The element to compare against
8793 */
8794
8795 var matches = function matches(query, ele) {
8796 return query.checks.every(function (chk) {
8797 return match[chk.type](chk, ele);
8798 });
8799 };
8800
8801 match[Type.GROUP] = function (check, ele) {
8802 var group = check.value;
8803 return group === '*' || group === ele.group();
8804 };
8805
8806 match[Type.STATE] = function (check, ele) {
8807 var stateSelector = check.value;
8808 return stateSelectorMatches(stateSelector, ele);
8809 };
8810
8811 match[Type.ID] = function (check, ele) {
8812 var id = check.value;
8813 return ele.id() === id;
8814 };
8815
8816 match[Type.CLASS] = function (check, ele) {
8817 var cls = check.value;
8818 return ele.hasClass(cls);
8819 };
8820
8821 match[Type.META_COMPARE] = function (check, ele) {
8822 var field = check.field,
8823 operator = check.operator,
8824 value = check.value;
8825 return valCmp(meta(ele, field), operator, value);
8826 };
8827
8828 match[Type.DATA_COMPARE] = function (check, ele) {
8829 var field = check.field,
8830 operator = check.operator,
8831 value = check.value;
8832 return valCmp(data(ele, field), operator, value);
8833 };
8834
8835 match[Type.DATA_BOOL] = function (check, ele) {
8836 var field = check.field,
8837 operator = check.operator;
8838 return boolCmp(data(ele, field), operator);
8839 };
8840
8841 match[Type.DATA_EXIST] = function (check, ele) {
8842 var field = check.field,
8843 operator = check.operator;
8844 return existCmp(data(ele, field));
8845 };
8846
8847 match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8848 var qA = check.nodes[0];
8849 var qB = check.nodes[1];
8850 var src = ele.source();
8851 var tgt = ele.target();
8852 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8853 };
8854
8855 match[Type.NODE_NEIGHBOR] = function (check, ele) {
8856 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8857 return n.isNode() && matches(check.neighbor, n);
8858 });
8859 };
8860
8861 match[Type.DIRECTED_EDGE] = function (check, ele) {
8862 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8863 };
8864
8865 match[Type.NODE_SOURCE] = function (check, ele) {
8866 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8867 return n.isNode() && matches(check.target, n);
8868 });
8869 };
8870
8871 match[Type.NODE_TARGET] = function (check, ele) {
8872 return matches(check.target, ele) && ele.incomers().some(function (n) {
8873 return n.isNode() && matches(check.source, n);
8874 });
8875 };
8876
8877 match[Type.CHILD] = function (check, ele) {
8878 return matches(check.child, ele) && matches(check.parent, ele.parent());
8879 };
8880
8881 match[Type.PARENT] = function (check, ele) {
8882 return matches(check.parent, ele) && ele.children().some(function (c) {
8883 return matches(check.child, c);
8884 });
8885 };
8886
8887 match[Type.DESCENDANT] = function (check, ele) {
8888 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8889 return matches(check.ancestor, a);
8890 });
8891 };
8892
8893 match[Type.ANCESTOR] = function (check, ele) {
8894 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8895 return matches(check.descendant, d);
8896 });
8897 };
8898
8899 match[Type.COMPOUND_SPLIT] = function (check, ele) {
8900 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8901 };
8902
8903 match[Type.TRUE] = function () {
8904 return true;
8905 };
8906
8907 match[Type.COLLECTION] = function (check, ele) {
8908 var collection = check.value;
8909 return collection.has(ele);
8910 };
8911
8912 match[Type.FILTER] = function (check, ele) {
8913 var filter = check.value;
8914 return filter(ele);
8915 };
8916
8917 var filter = function filter(collection) {
8918 var self = this; // for 1 id #foo queries, just get the element
8919
8920 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8921 return collection.getElementById(self[0].checks[0].value).collection();
8922 }
8923
8924 var selectorFunction = function selectorFunction(element) {
8925 for (var j = 0; j < self.length; j++) {
8926 var query = self[j];
8927
8928 if (matches(query, element)) {
8929 return true;
8930 }
8931 }
8932
8933 return false;
8934 };
8935
8936 if (self.text() == null) {
8937 selectorFunction = function selectorFunction() {
8938 return true;
8939 };
8940 }
8941
8942 return collection.filter(selectorFunction);
8943 }; // filter
8944 // does selector match a single element?
8945
8946
8947 var matches$1 = function matches$1(ele) {
8948 var self = this;
8949
8950 for (var j = 0; j < self.length; j++) {
8951 var query = self[j];
8952
8953 if (matches(query, ele)) {
8954 return true;
8955 }
8956 }
8957
8958 return false;
8959 }; // matches
8960
8961
8962 var matching = {
8963 matches: matches$1,
8964 filter: filter
8965 };
8966
8967 var Selector = function Selector(selector) {
8968 this.inputText = selector;
8969 this.currentSubject = null;
8970 this.compoundCount = 0;
8971 this.edgeCount = 0;
8972 this.length = 0;
8973
8974 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8975 this.addQuery({
8976 checks: [{
8977 type: Type.COLLECTION,
8978 value: selector.collection()
8979 }]
8980 });
8981 } else if (fn(selector)) {
8982 this.addQuery({
8983 checks: [{
8984 type: Type.FILTER,
8985 value: selector
8986 }]
8987 });
8988 } else if (string(selector)) {
8989 if (!this.parse(selector)) {
8990 this.invalid = true;
8991 }
8992 } else {
8993 error('A selector must be created from a string; found ');
8994 }
8995 };
8996
8997 var selfn = Selector.prototype;
8998 [parse$1, matching].forEach(function (p) {
8999 return extend(selfn, p);
9000 });
9001
9002 selfn.text = function () {
9003 return this.inputText;
9004 };
9005
9006 selfn.size = function () {
9007 return this.length;
9008 };
9009
9010 selfn.eq = function (i) {
9011 return this[i];
9012 };
9013
9014 selfn.sameText = function (otherSel) {
9015 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
9016 };
9017
9018 selfn.addQuery = function (q) {
9019 this[this.length++] = q;
9020 };
9021
9022 selfn.selector = selfn.toString;
9023
9024 var elesfn$f = {
9025 allAre: function allAre(selector) {
9026 var selObj = new Selector(selector);
9027 return this.every(function (ele) {
9028 return selObj.matches(ele);
9029 });
9030 },
9031 is: function is(selector) {
9032 var selObj = new Selector(selector);
9033 return this.some(function (ele) {
9034 return selObj.matches(ele);
9035 });
9036 },
9037 some: function some(fn, thisArg) {
9038 for (var i = 0; i < this.length; i++) {
9039 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9040
9041 if (ret) {
9042 return true;
9043 }
9044 }
9045
9046 return false;
9047 },
9048 every: function every(fn, thisArg) {
9049 for (var i = 0; i < this.length; i++) {
9050 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9051
9052 if (!ret) {
9053 return false;
9054 }
9055 }
9056
9057 return true;
9058 },
9059 same: function same(collection) {
9060 // cheap collection ref check
9061 if (this === collection) {
9062 return true;
9063 }
9064
9065 collection = this.cy().collection(collection);
9066 var thisLength = this.length;
9067 var collectionLength = collection.length; // cheap length check
9068
9069 if (thisLength !== collectionLength) {
9070 return false;
9071 } // cheap element ref check
9072
9073
9074 if (thisLength === 1) {
9075 return this[0] === collection[0];
9076 }
9077
9078 return this.every(function (ele) {
9079 return collection.hasElementWithId(ele.id());
9080 });
9081 },
9082 anySame: function anySame(collection) {
9083 collection = this.cy().collection(collection);
9084 return this.some(function (ele) {
9085 return collection.hasElementWithId(ele.id());
9086 });
9087 },
9088 allAreNeighbors: function allAreNeighbors(collection) {
9089 collection = this.cy().collection(collection);
9090 var nhood = this.neighborhood();
9091 return collection.every(function (ele) {
9092 return nhood.hasElementWithId(ele.id());
9093 });
9094 },
9095 contains: function contains(collection) {
9096 collection = this.cy().collection(collection);
9097 var self = this;
9098 return collection.every(function (ele) {
9099 return self.hasElementWithId(ele.id());
9100 });
9101 }
9102 };
9103 elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
9104 elesfn$f.has = elesfn$f.contains;
9105 elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
9106
9107 var cache = function cache(fn, name) {
9108 return function traversalCache(arg1, arg2, arg3, arg4) {
9109 var selectorOrEles = arg1;
9110 var eles = this;
9111 var key;
9112
9113 if (selectorOrEles == null) {
9114 key = '';
9115 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
9116 key = selectorOrEles.id();
9117 }
9118
9119 if (eles.length === 1 && key) {
9120 var _p = eles[0]._private;
9121 var tch = _p.traversalCache = _p.traversalCache || {};
9122 var ch = tch[name] = tch[name] || [];
9123 var hash = hashString(key);
9124 var cacheHit = ch[hash];
9125
9126 if (cacheHit) {
9127 return cacheHit;
9128 } else {
9129 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
9130 }
9131 } else {
9132 return fn.call(eles, arg1, arg2, arg3, arg4);
9133 }
9134 };
9135 };
9136
9137 var elesfn$g = {
9138 parent: function parent(selector) {
9139 var parents = []; // optimisation for single ele call
9140
9141 if (this.length === 1) {
9142 var parent = this[0]._private.parent;
9143
9144 if (parent) {
9145 return parent;
9146 }
9147 }
9148
9149 for (var i = 0; i < this.length; i++) {
9150 var ele = this[i];
9151 var _parent = ele._private.parent;
9152
9153 if (_parent) {
9154 parents.push(_parent);
9155 }
9156 }
9157
9158 return this.spawn(parents, true).filter(selector);
9159 },
9160 parents: function parents(selector) {
9161 var parents = [];
9162 var eles = this.parent();
9163
9164 while (eles.nonempty()) {
9165 for (var i = 0; i < eles.length; i++) {
9166 var ele = eles[i];
9167 parents.push(ele);
9168 }
9169
9170 eles = eles.parent();
9171 }
9172
9173 return this.spawn(parents, true).filter(selector);
9174 },
9175 commonAncestors: function commonAncestors(selector) {
9176 var ancestors;
9177
9178 for (var i = 0; i < this.length; i++) {
9179 var ele = this[i];
9180 var parents = ele.parents();
9181 ancestors = ancestors || parents;
9182 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
9183 }
9184
9185 return ancestors.filter(selector);
9186 },
9187 orphans: function orphans(selector) {
9188 return this.stdFilter(function (ele) {
9189 return ele.isOrphan();
9190 }).filter(selector);
9191 },
9192 nonorphans: function nonorphans(selector) {
9193 return this.stdFilter(function (ele) {
9194 return ele.isChild();
9195 }).filter(selector);
9196 },
9197 children: cache(function (selector) {
9198 var children = [];
9199
9200 for (var i = 0; i < this.length; i++) {
9201 var ele = this[i];
9202 var eleChildren = ele._private.children;
9203
9204 for (var j = 0; j < eleChildren.length; j++) {
9205 children.push(eleChildren[j]);
9206 }
9207 }
9208
9209 return this.spawn(children, true).filter(selector);
9210 }, 'children'),
9211 siblings: function siblings(selector) {
9212 return this.parent().children().not(this).filter(selector);
9213 },
9214 isParent: function isParent() {
9215 var ele = this[0];
9216
9217 if (ele) {
9218 return ele.isNode() && ele._private.children.length !== 0;
9219 }
9220 },
9221 isChildless: function isChildless() {
9222 var ele = this[0];
9223
9224 if (ele) {
9225 return ele.isNode() && ele._private.children.length === 0;
9226 }
9227 },
9228 isChild: function isChild() {
9229 var ele = this[0];
9230
9231 if (ele) {
9232 return ele.isNode() && ele._private.parent != null;
9233 }
9234 },
9235 isOrphan: function isOrphan() {
9236 var ele = this[0];
9237
9238 if (ele) {
9239 return ele.isNode() && ele._private.parent == null;
9240 }
9241 },
9242 descendants: function descendants(selector) {
9243 var elements = [];
9244
9245 function add(eles) {
9246 for (var i = 0; i < eles.length; i++) {
9247 var ele = eles[i];
9248 elements.push(ele);
9249
9250 if (ele.children().nonempty()) {
9251 add(ele.children());
9252 }
9253 }
9254 }
9255
9256 add(this.children());
9257 return this.spawn(elements, true).filter(selector);
9258 }
9259 };
9260
9261 function forEachCompound(eles, fn, includeSelf, recursiveStep) {
9262 var q = [];
9263 var did = new Set$1();
9264 var cy = eles.cy();
9265 var hasCompounds = cy.hasCompoundNodes();
9266
9267 for (var i = 0; i < eles.length; i++) {
9268 var ele = eles[i];
9269
9270 if (includeSelf) {
9271 q.push(ele);
9272 } else if (hasCompounds) {
9273 recursiveStep(q, did, ele);
9274 }
9275 }
9276
9277 while (q.length > 0) {
9278 var _ele = q.shift();
9279
9280 fn(_ele);
9281 did.add(_ele.id());
9282
9283 if (hasCompounds) {
9284 recursiveStep(q, did, _ele);
9285 }
9286 }
9287
9288 return eles;
9289 }
9290
9291 function addChildren(q, did, ele) {
9292 if (ele.isParent()) {
9293 var children = ele._private.children;
9294
9295 for (var i = 0; i < children.length; i++) {
9296 var child = children[i];
9297
9298 if (!did.has(child.id())) {
9299 q.push(child);
9300 }
9301 }
9302 }
9303 } // very efficient version of eles.add( eles.descendants() ).forEach()
9304 // for internal use
9305
9306
9307 elesfn$g.forEachDown = function (fn) {
9308 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9309 return forEachCompound(this, fn, includeSelf, addChildren);
9310 };
9311
9312 function addParent(q, did, ele) {
9313 if (ele.isChild()) {
9314 var parent = ele._private.parent;
9315
9316 if (!did.has(parent.id())) {
9317 q.push(parent);
9318 }
9319 }
9320 }
9321
9322 elesfn$g.forEachUp = function (fn) {
9323 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9324 return forEachCompound(this, fn, includeSelf, addParent);
9325 };
9326
9327 function addParentAndChildren(q, did, ele) {
9328 addParent(q, did, ele);
9329 addChildren(q, did, ele);
9330 }
9331
9332 elesfn$g.forEachUpAndDown = function (fn) {
9333 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9334 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
9335 }; // aliases
9336
9337
9338 elesfn$g.ancestors = elesfn$g.parents;
9339
9340 var fn$1, elesfn$h;
9341 fn$1 = elesfn$h = {
9342 data: define$3.data({
9343 field: 'data',
9344 bindingEvent: 'data',
9345 allowBinding: true,
9346 allowSetting: true,
9347 settingEvent: 'data',
9348 settingTriggersEvent: true,
9349 triggerFnName: 'trigger',
9350 allowGetting: true,
9351 immutableKeys: {
9352 'id': true,
9353 'source': true,
9354 'target': true,
9355 'parent': true
9356 },
9357 updateStyle: true
9358 }),
9359 removeData: define$3.removeData({
9360 field: 'data',
9361 event: 'data',
9362 triggerFnName: 'trigger',
9363 triggerEvent: true,
9364 immutableKeys: {
9365 'id': true,
9366 'source': true,
9367 'target': true,
9368 'parent': true
9369 },
9370 updateStyle: true
9371 }),
9372 scratch: define$3.data({
9373 field: 'scratch',
9374 bindingEvent: 'scratch',
9375 allowBinding: true,
9376 allowSetting: true,
9377 settingEvent: 'scratch',
9378 settingTriggersEvent: true,
9379 triggerFnName: 'trigger',
9380 allowGetting: true,
9381 updateStyle: true
9382 }),
9383 removeScratch: define$3.removeData({
9384 field: 'scratch',
9385 event: 'scratch',
9386 triggerFnName: 'trigger',
9387 triggerEvent: true,
9388 updateStyle: true
9389 }),
9390 rscratch: define$3.data({
9391 field: 'rscratch',
9392 allowBinding: false,
9393 allowSetting: true,
9394 settingTriggersEvent: false,
9395 allowGetting: true
9396 }),
9397 removeRscratch: define$3.removeData({
9398 field: 'rscratch',
9399 triggerEvent: false
9400 }),
9401 id: function id() {
9402 var ele = this[0];
9403
9404 if (ele) {
9405 return ele._private.data.id;
9406 }
9407 }
9408 }; // aliases
9409
9410 fn$1.attr = fn$1.data;
9411 fn$1.removeAttr = fn$1.removeData;
9412 var data$1 = elesfn$h;
9413
9414 var elesfn$i = {};
9415
9416 function defineDegreeFunction(callback) {
9417 return function (includeLoops) {
9418 var self = this;
9419
9420 if (includeLoops === undefined) {
9421 includeLoops = true;
9422 }
9423
9424 if (self.length === 0) {
9425 return;
9426 }
9427
9428 if (self.isNode() && !self.removed()) {
9429 var degree = 0;
9430 var node = self[0];
9431 var connectedEdges = node._private.edges;
9432
9433 for (var i = 0; i < connectedEdges.length; i++) {
9434 var edge = connectedEdges[i];
9435
9436 if (!includeLoops && edge.isLoop()) {
9437 continue;
9438 }
9439
9440 degree += callback(node, edge);
9441 }
9442
9443 return degree;
9444 } else {
9445 return;
9446 }
9447 };
9448 }
9449
9450 extend(elesfn$i, {
9451 degree: defineDegreeFunction(function (node, edge) {
9452 if (edge.source().same(edge.target())) {
9453 return 2;
9454 } else {
9455 return 1;
9456 }
9457 }),
9458 indegree: defineDegreeFunction(function (node, edge) {
9459 if (edge.target().same(node)) {
9460 return 1;
9461 } else {
9462 return 0;
9463 }
9464 }),
9465 outdegree: defineDegreeFunction(function (node, edge) {
9466 if (edge.source().same(node)) {
9467 return 1;
9468 } else {
9469 return 0;
9470 }
9471 })
9472 });
9473
9474 function defineDegreeBoundsFunction(degreeFn, callback) {
9475 return function (includeLoops) {
9476 var ret;
9477 var nodes = this.nodes();
9478
9479 for (var i = 0; i < nodes.length; i++) {
9480 var ele = nodes[i];
9481 var degree = ele[degreeFn](includeLoops);
9482
9483 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
9484 ret = degree;
9485 }
9486 }
9487
9488 return ret;
9489 };
9490 }
9491
9492 extend(elesfn$i, {
9493 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
9494 return degree < min;
9495 }),
9496 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
9497 return degree > max;
9498 }),
9499 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
9500 return degree < min;
9501 }),
9502 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
9503 return degree > max;
9504 }),
9505 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
9506 return degree < min;
9507 }),
9508 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
9509 return degree > max;
9510 })
9511 });
9512 extend(elesfn$i, {
9513 totalDegree: function totalDegree(includeLoops) {
9514 var total = 0;
9515 var nodes = this.nodes();
9516
9517 for (var i = 0; i < nodes.length; i++) {
9518 total += nodes[i].degree(includeLoops);
9519 }
9520
9521 return total;
9522 }
9523 });
9524
9525 var fn$2, elesfn$j;
9526
9527 var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
9528 for (var i = 0; i < eles.length; i++) {
9529 var ele = eles[i];
9530
9531 if (!ele.locked()) {
9532 var oldPos = ele._private.position;
9533 var delta = {
9534 x: newPos.x != null ? newPos.x - oldPos.x : 0,
9535 y: newPos.y != null ? newPos.y - oldPos.y : 0
9536 };
9537
9538 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
9539 ele.children().shift(delta, silent);
9540 }
9541
9542 ele.shiftCachedBoundingBox(delta);
9543 }
9544 }
9545 };
9546
9547 var positionDef = {
9548 field: 'position',
9549 bindingEvent: 'position',
9550 allowBinding: true,
9551 allowSetting: true,
9552 settingEvent: 'position',
9553 settingTriggersEvent: true,
9554 triggerFnName: 'emitAndNotify',
9555 allowGetting: true,
9556 validKeys: ['x', 'y'],
9557 beforeGet: function beforeGet(ele) {
9558 ele.updateCompoundBounds();
9559 },
9560 beforeSet: function beforeSet(eles, newPos) {
9561 beforePositionSet(eles, newPos, false);
9562 },
9563 onSet: function onSet(eles) {
9564 eles.dirtyCompoundBoundsCache();
9565 },
9566 canSet: function canSet(ele) {
9567 return !ele.locked();
9568 }
9569 };
9570 fn$2 = elesfn$j = {
9571 position: define$3.data(positionDef),
9572 // position but no notification to renderer
9573 silentPosition: define$3.data(extend({}, positionDef, {
9574 allowBinding: false,
9575 allowSetting: true,
9576 settingTriggersEvent: false,
9577 allowGetting: false,
9578 beforeSet: function beforeSet(eles, newPos) {
9579 beforePositionSet(eles, newPos, true);
9580 }
9581 })),
9582 positions: function positions(pos, silent) {
9583 if (plainObject(pos)) {
9584 if (silent) {
9585 this.silentPosition(pos);
9586 } else {
9587 this.position(pos);
9588 }
9589 } else if (fn(pos)) {
9590 var _fn = pos;
9591 var cy = this.cy();
9592 cy.startBatch();
9593
9594 for (var i = 0; i < this.length; i++) {
9595 var ele = this[i];
9596
9597 var _pos = void 0;
9598
9599 if (_pos = _fn(ele, i)) {
9600 if (silent) {
9601 ele.silentPosition(_pos);
9602 } else {
9603 ele.position(_pos);
9604 }
9605 }
9606 }
9607
9608 cy.endBatch();
9609 }
9610
9611 return this; // chaining
9612 },
9613 silentPositions: function silentPositions(pos) {
9614 return this.positions(pos, true);
9615 },
9616 shift: function shift(dim, val, silent) {
9617 var delta;
9618
9619 if (plainObject(dim)) {
9620 delta = {
9621 x: number(dim.x) ? dim.x : 0,
9622 y: number(dim.y) ? dim.y : 0
9623 };
9624 silent = val;
9625 } else if (string(dim) && number(val)) {
9626 delta = {
9627 x: 0,
9628 y: 0
9629 };
9630 delta[dim] = val;
9631 }
9632
9633 if (delta != null) {
9634 var cy = this.cy();
9635 cy.startBatch();
9636
9637 for (var i = 0; i < this.length; i++) {
9638 var ele = this[i];
9639 var pos = ele.position();
9640 var newPos = {
9641 x: pos.x + delta.x,
9642 y: pos.y + delta.y
9643 };
9644
9645 if (silent) {
9646 ele.silentPosition(newPos);
9647 } else {
9648 ele.position(newPos);
9649 }
9650 }
9651
9652 cy.endBatch();
9653 }
9654
9655 return this;
9656 },
9657 silentShift: function silentShift(dim, val) {
9658 if (plainObject(dim)) {
9659 this.shift(dim, true);
9660 } else if (string(dim) && number(val)) {
9661 this.shift(dim, val, true);
9662 }
9663
9664 return this;
9665 },
9666 // get/set the rendered (i.e. on screen) positon of the element
9667 renderedPosition: function renderedPosition(dim, val) {
9668 var ele = this[0];
9669 var cy = this.cy();
9670 var zoom = cy.zoom();
9671 var pan = cy.pan();
9672 var rpos = plainObject(dim) ? dim : undefined;
9673 var setting = rpos !== undefined || val !== undefined && string(dim);
9674
9675 if (ele && ele.isNode()) {
9676 // must have an element and must be a node to return position
9677 if (setting) {
9678 for (var i = 0; i < this.length; i++) {
9679 var _ele = this[i];
9680
9681 if (val !== undefined) {
9682 // set one dimension
9683 _ele.position(dim, (val - pan[dim]) / zoom);
9684 } else if (rpos !== undefined) {
9685 // set whole position
9686 _ele.position(renderedToModelPosition(rpos, zoom, pan));
9687 }
9688 }
9689 } else {
9690 // getting
9691 var pos = ele.position();
9692 rpos = modelToRenderedPosition(pos, zoom, pan);
9693
9694 if (dim === undefined) {
9695 // then return the whole rendered position
9696 return rpos;
9697 } else {
9698 // then return the specified dimension
9699 return rpos[dim];
9700 }
9701 }
9702 } else if (!setting) {
9703 return undefined; // for empty collection case
9704 }
9705
9706 return this; // chaining
9707 },
9708 // get/set the position relative to the parent
9709 relativePosition: function relativePosition(dim, val) {
9710 var ele = this[0];
9711 var cy = this.cy();
9712 var ppos = plainObject(dim) ? dim : undefined;
9713 var setting = ppos !== undefined || val !== undefined && string(dim);
9714 var hasCompoundNodes = cy.hasCompoundNodes();
9715
9716 if (ele && ele.isNode()) {
9717 // must have an element and must be a node to return position
9718 if (setting) {
9719 for (var i = 0; i < this.length; i++) {
9720 var _ele2 = this[i];
9721 var parent = hasCompoundNodes ? _ele2.parent() : null;
9722 var hasParent = parent && parent.length > 0;
9723 var relativeToParent = hasParent;
9724
9725 if (hasParent) {
9726 parent = parent[0];
9727 }
9728
9729 var origin = relativeToParent ? parent.position() : {
9730 x: 0,
9731 y: 0
9732 };
9733
9734 if (val !== undefined) {
9735 // set one dimension
9736 _ele2.position(dim, val + origin[dim]);
9737 } else if (ppos !== undefined) {
9738 // set whole position
9739 _ele2.position({
9740 x: ppos.x + origin.x,
9741 y: ppos.y + origin.y
9742 });
9743 }
9744 }
9745 } else {
9746 // getting
9747 var pos = ele.position();
9748
9749 var _parent = hasCompoundNodes ? ele.parent() : null;
9750
9751 var _hasParent = _parent && _parent.length > 0;
9752
9753 var _relativeToParent = _hasParent;
9754
9755 if (_hasParent) {
9756 _parent = _parent[0];
9757 }
9758
9759 var _origin = _relativeToParent ? _parent.position() : {
9760 x: 0,
9761 y: 0
9762 };
9763
9764 ppos = {
9765 x: pos.x - _origin.x,
9766 y: pos.y - _origin.y
9767 };
9768
9769 if (dim === undefined) {
9770 // then return the whole rendered position
9771 return ppos;
9772 } else {
9773 // then return the specified dimension
9774 return ppos[dim];
9775 }
9776 }
9777 } else if (!setting) {
9778 return undefined; // for empty collection case
9779 }
9780
9781 return this; // chaining
9782 }
9783 }; // aliases
9784
9785 fn$2.modelPosition = fn$2.point = fn$2.position;
9786 fn$2.modelPositions = fn$2.points = fn$2.positions;
9787 fn$2.renderedPoint = fn$2.renderedPosition;
9788 fn$2.relativePoint = fn$2.relativePosition;
9789 var position = elesfn$j;
9790
9791 var fn$3, elesfn$k;
9792 fn$3 = elesfn$k = {};
9793
9794 elesfn$k.renderedBoundingBox = function (options) {
9795 var bb = this.boundingBox(options);
9796 var cy = this.cy();
9797 var zoom = cy.zoom();
9798 var pan = cy.pan();
9799 var x1 = bb.x1 * zoom + pan.x;
9800 var x2 = bb.x2 * zoom + pan.x;
9801 var y1 = bb.y1 * zoom + pan.y;
9802 var y2 = bb.y2 * zoom + pan.y;
9803 return {
9804 x1: x1,
9805 x2: x2,
9806 y1: y1,
9807 y2: y2,
9808 w: x2 - x1,
9809 h: y2 - y1
9810 };
9811 };
9812
9813 elesfn$k.dirtyCompoundBoundsCache = function () {
9814 var cy = this.cy();
9815
9816 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9817 return this;
9818 }
9819
9820 this.forEachUp(function (ele) {
9821 if (ele.isParent()) {
9822 var _p = ele._private;
9823 _p.compoundBoundsClean = false;
9824 _p.bbCache = null;
9825 ele.emitAndNotify('bounds');
9826 }
9827 });
9828 return this;
9829 };
9830
9831 elesfn$k.updateCompoundBounds = function () {
9832 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9833 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9834
9835 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9836 return this;
9837 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9838
9839
9840 if (!force && cy.batching()) {
9841 return this;
9842 }
9843
9844 function update(parent) {
9845 if (!parent.isParent()) {
9846 return;
9847 }
9848
9849 var _p = parent._private;
9850 var children = parent.children();
9851 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9852 var min = {
9853 width: {
9854 val: parent.pstyle('min-width').pfValue,
9855 left: parent.pstyle('min-width-bias-left'),
9856 right: parent.pstyle('min-width-bias-right')
9857 },
9858 height: {
9859 val: parent.pstyle('min-height').pfValue,
9860 top: parent.pstyle('min-height-bias-top'),
9861 bottom: parent.pstyle('min-height-bias-bottom')
9862 }
9863 };
9864 var bb = children.boundingBox({
9865 includeLabels: includeLabels,
9866 includeOverlays: false,
9867 // updating the compound bounds happens outside of the regular
9868 // cache cycle (i.e. before fired events)
9869 useCache: false
9870 });
9871 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9872
9873 if (bb.w === 0 || bb.h === 0) {
9874 bb = {
9875 w: parent.pstyle('width').pfValue,
9876 h: parent.pstyle('height').pfValue
9877 };
9878 bb.x1 = pos.x - bb.w / 2;
9879 bb.x2 = pos.x + bb.w / 2;
9880 bb.y1 = pos.y - bb.h / 2;
9881 bb.y2 = pos.y + bb.h / 2;
9882 }
9883
9884 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9885 var biasDiff = 0;
9886 var biasComplementDiff = 0;
9887 var biasTotal = propBias + propBiasComplement;
9888
9889 if (propDiff > 0 && biasTotal > 0) {
9890 biasDiff = propBias / biasTotal * propDiff;
9891 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9892 }
9893
9894 return {
9895 biasDiff: biasDiff,
9896 biasComplementDiff: biasComplementDiff
9897 };
9898 }
9899
9900 function computePaddingValues(width, height, paddingObject, relativeTo) {
9901 // Assuming percentage is number from 0 to 1
9902 if (paddingObject.units === '%') {
9903 switch (relativeTo) {
9904 case 'width':
9905 return width > 0 ? paddingObject.pfValue * width : 0;
9906
9907 case 'height':
9908 return height > 0 ? paddingObject.pfValue * height : 0;
9909
9910 case 'average':
9911 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9912
9913 case 'min':
9914 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9915
9916 case 'max':
9917 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9918
9919 default:
9920 return 0;
9921 }
9922 } else if (paddingObject.units === 'px') {
9923 return paddingObject.pfValue;
9924 } else {
9925 return 0;
9926 }
9927 }
9928
9929 var leftVal = min.width.left.value;
9930
9931 if (min.width.left.units === 'px' && min.width.val > 0) {
9932 leftVal = leftVal * 100 / min.width.val;
9933 }
9934
9935 var rightVal = min.width.right.value;
9936
9937 if (min.width.right.units === 'px' && min.width.val > 0) {
9938 rightVal = rightVal * 100 / min.width.val;
9939 }
9940
9941 var topVal = min.height.top.value;
9942
9943 if (min.height.top.units === 'px' && min.height.val > 0) {
9944 topVal = topVal * 100 / min.height.val;
9945 }
9946
9947 var bottomVal = min.height.bottom.value;
9948
9949 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9950 bottomVal = bottomVal * 100 / min.height.val;
9951 }
9952
9953 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9954 var diffLeft = widthBiasDiffs.biasDiff;
9955 var diffRight = widthBiasDiffs.biasComplementDiff;
9956 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9957 var diffTop = heightBiasDiffs.biasDiff;
9958 var diffBottom = heightBiasDiffs.biasComplementDiff;
9959 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9960 _p.autoWidth = Math.max(bb.w, min.width.val);
9961 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9962 _p.autoHeight = Math.max(bb.h, min.height.val);
9963 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9964 }
9965
9966 for (var i = 0; i < this.length; i++) {
9967 var ele = this[i];
9968 var _p = ele._private;
9969
9970 if (!_p.compoundBoundsClean) {
9971 update(ele);
9972
9973 if (!cy.batching()) {
9974 _p.compoundBoundsClean = true;
9975 }
9976 }
9977 }
9978
9979 return this;
9980 };
9981
9982 var noninf = function noninf(x) {
9983 if (x === Infinity || x === -Infinity) {
9984 return 0;
9985 }
9986
9987 return x;
9988 };
9989
9990 var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9991 // don't update with zero area boxes
9992 if (x2 - x1 === 0 || y2 - y1 === 0) {
9993 return;
9994 } // don't update with null dim
9995
9996
9997 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9998 return;
9999 }
10000
10001 b.x1 = x1 < b.x1 ? x1 : b.x1;
10002 b.x2 = x2 > b.x2 ? x2 : b.x2;
10003 b.y1 = y1 < b.y1 ? y1 : b.y1;
10004 b.y2 = y2 > b.y2 ? y2 : b.y2;
10005 b.w = b.x2 - b.x1;
10006 b.h = b.y2 - b.y1;
10007 };
10008
10009 var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
10010 if (b2 == null) {
10011 return b;
10012 }
10013
10014 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
10015 };
10016
10017 var prefixedProperty = function prefixedProperty(obj, field, prefix) {
10018 return getPrefixedProperty(obj, field, prefix);
10019 };
10020
10021 var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
10022 if (ele.cy().headless()) {
10023 return;
10024 }
10025
10026 var _p = ele._private;
10027 var rstyle = _p.rstyle;
10028 var halfArW = rstyle.arrowWidth / 2;
10029 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
10030 var x;
10031 var y;
10032
10033 if (arrowType !== 'none') {
10034 if (prefix === 'source') {
10035 x = rstyle.srcX;
10036 y = rstyle.srcY;
10037 } else if (prefix === 'target') {
10038 x = rstyle.tgtX;
10039 y = rstyle.tgtY;
10040 } else {
10041 x = rstyle.midX;
10042 y = rstyle.midY;
10043 } // always store the individual arrow bounds
10044
10045
10046 var bbs = _p.arrowBounds = _p.arrowBounds || {};
10047 var bb = bbs[prefix] = bbs[prefix] || {};
10048 bb.x1 = x - halfArW;
10049 bb.y1 = y - halfArW;
10050 bb.x2 = x + halfArW;
10051 bb.y2 = y + halfArW;
10052 bb.w = bb.x2 - bb.x1;
10053 bb.h = bb.y2 - bb.y1;
10054 expandBoundingBox(bb, 1);
10055 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
10056 }
10057 };
10058
10059 var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
10060 if (ele.cy().headless()) {
10061 return;
10062 }
10063
10064 var prefixDash;
10065
10066 if (prefix) {
10067 prefixDash = prefix + '-';
10068 } else {
10069 prefixDash = '';
10070 }
10071
10072 var _p = ele._private;
10073 var rstyle = _p.rstyle;
10074 var label = ele.pstyle(prefixDash + 'label').strValue;
10075
10076 if (label) {
10077 var halign = ele.pstyle('text-halign');
10078 var valign = ele.pstyle('text-valign');
10079 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
10080 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
10081 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
10082 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
10083 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
10084 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
10085 var isEdge = ele.isEdge();
10086 var rotation = ele.pstyle(prefixDash + 'text-rotation');
10087 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
10088 var borderWidth = ele.pstyle('text-border-width').pfValue;
10089 var halfBorderWidth = borderWidth / 2;
10090 var padding = ele.pstyle('text-background-padding').pfValue;
10091 var lh = labelHeight;
10092 var lw = labelWidth;
10093 var lw_2 = lw / 2;
10094 var lh_2 = lh / 2;
10095 var lx1, lx2, ly1, ly2;
10096
10097 if (isEdge) {
10098 lx1 = labelX - lw_2;
10099 lx2 = labelX + lw_2;
10100 ly1 = labelY - lh_2;
10101 ly2 = labelY + lh_2;
10102 } else {
10103 switch (halign.value) {
10104 case 'left':
10105 lx1 = labelX - lw;
10106 lx2 = labelX;
10107 break;
10108
10109 case 'center':
10110 lx1 = labelX - lw_2;
10111 lx2 = labelX + lw_2;
10112 break;
10113
10114 case 'right':
10115 lx1 = labelX;
10116 lx2 = labelX + lw;
10117 break;
10118 }
10119
10120 switch (valign.value) {
10121 case 'top':
10122 ly1 = labelY - lh;
10123 ly2 = labelY;
10124 break;
10125
10126 case 'center':
10127 ly1 = labelY - lh_2;
10128 ly2 = labelY + lh_2;
10129 break;
10130
10131 case 'bottom':
10132 ly1 = labelY;
10133 ly2 = labelY + lh;
10134 break;
10135 }
10136 } // shift by margin and expand by outline and border
10137
10138
10139 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
10140 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
10141 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
10142 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
10143
10144 var bbPrefix = prefix || 'main';
10145 var bbs = _p.labelBounds;
10146 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
10147 bb.x1 = lx1;
10148 bb.y1 = ly1;
10149 bb.x2 = lx2;
10150 bb.y2 = ly2;
10151 bb.w = lx2 - lx1;
10152 bb.h = ly2 - ly1;
10153 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
10154
10155 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
10156 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
10157
10158 if (isAutorotate || isPfValue) {
10159 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
10160 var cos = Math.cos(theta);
10161 var sin = Math.sin(theta); // rotation point (default value for center-center)
10162
10163 var xo = (lx1 + lx2) / 2;
10164 var yo = (ly1 + ly2) / 2;
10165
10166 if (!isEdge) {
10167 switch (halign.value) {
10168 case 'left':
10169 xo = lx2;
10170 break;
10171
10172 case 'right':
10173 xo = lx1;
10174 break;
10175 }
10176
10177 switch (valign.value) {
10178 case 'top':
10179 yo = ly2;
10180 break;
10181
10182 case 'bottom':
10183 yo = ly1;
10184 break;
10185 }
10186 }
10187
10188 var rotate = function rotate(x, y) {
10189 x = x - xo;
10190 y = y - yo;
10191 return {
10192 x: x * cos - y * sin + xo,
10193 y: x * sin + y * cos + yo
10194 };
10195 };
10196
10197 var px1y1 = rotate(lx1, ly1);
10198 var px1y2 = rotate(lx1, ly2);
10199 var px2y1 = rotate(lx2, ly1);
10200 var px2y2 = rotate(lx2, ly2);
10201 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10202 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10203 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10204 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10205 }
10206
10207 var bbPrefixRot = bbPrefix + 'Rot';
10208 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
10209 bbRot.x1 = lx1;
10210 bbRot.y1 = ly1;
10211 bbRot.x2 = lx2;
10212 bbRot.y2 = ly2;
10213 bbRot.w = lx2 - lx1;
10214 bbRot.h = ly2 - ly1;
10215 updateBounds(bounds, lx1, ly1, lx2, ly2);
10216 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
10217 }
10218
10219 return bounds;
10220 }; // get the bounding box of the elements (in raw model position)
10221
10222
10223 var boundingBoxImpl = function boundingBoxImpl(ele, options) {
10224 var cy = ele._private.cy;
10225 var styleEnabled = cy.styleEnabled();
10226 var headless = cy.headless();
10227 var bounds = makeBoundingBox();
10228 var _p = ele._private;
10229 var isNode = ele.isNode();
10230 var isEdge = ele.isEdge();
10231 var ex1, ex2, ey1, ey2; // extrema of body / lines
10232
10233 var x, y; // node pos
10234
10235 var rstyle = _p.rstyle;
10236 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
10237 // (other factors like width values will be considered later in this function anyway)
10238
10239 var isDisplayed = function isDisplayed(ele) {
10240 return ele.pstyle('display').value !== 'none';
10241 };
10242
10243 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
10244 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
10245
10246 if (displayed) {
10247 // displayed suffices, since we will find zero area eles anyway
10248 var overlayOpacity = 0;
10249 var overlayPadding = 0;
10250
10251 if (styleEnabled && options.includeOverlays) {
10252 overlayOpacity = ele.pstyle('overlay-opacity').value;
10253
10254 if (overlayOpacity !== 0) {
10255 overlayPadding = ele.pstyle('overlay-padding').value;
10256 }
10257 }
10258
10259 var w = 0;
10260 var wHalf = 0;
10261
10262 if (styleEnabled) {
10263 w = ele.pstyle('width').pfValue;
10264 wHalf = w / 2;
10265 }
10266
10267 if (isNode && options.includeNodes) {
10268 var pos = ele.position();
10269 x = pos.x;
10270 y = pos.y;
10271
10272 var _w = ele.outerWidth();
10273
10274 var halfW = _w / 2;
10275 var h = ele.outerHeight();
10276 var halfH = h / 2; // handle node dimensions
10277 /////////////////////////
10278
10279 ex1 = x - halfW;
10280 ex2 = x + halfW;
10281 ey1 = y - halfH;
10282 ey2 = y + halfH;
10283 updateBounds(bounds, ex1, ey1, ex2, ey2);
10284 } else if (isEdge && options.includeEdges) {
10285 if (styleEnabled && !headless) {
10286 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
10287 //////////////////////////////////////////////
10288
10289 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10290 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10291 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10292 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
10293
10294 ex1 -= wHalf;
10295 ex2 += wHalf;
10296 ey1 -= wHalf;
10297 ey2 += wHalf;
10298 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
10299 ////////////////
10300
10301 if (curveStyle === 'haystack') {
10302 var hpts = rstyle.haystackPts;
10303
10304 if (hpts && hpts.length === 2) {
10305 ex1 = hpts[0].x;
10306 ey1 = hpts[0].y;
10307 ex2 = hpts[1].x;
10308 ey2 = hpts[1].y;
10309
10310 if (ex1 > ex2) {
10311 var temp = ex1;
10312 ex1 = ex2;
10313 ex2 = temp;
10314 }
10315
10316 if (ey1 > ey2) {
10317 var _temp = ey1;
10318 ey1 = ey2;
10319 ey2 = _temp;
10320 }
10321
10322 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
10323 }
10324 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
10325 var pts;
10326
10327 switch (curveStyle) {
10328 case 'bezier':
10329 case 'unbundled-bezier':
10330 pts = rstyle.bezierPts;
10331 break;
10332
10333 case 'segments':
10334 case 'taxi':
10335 pts = rstyle.linePts;
10336 break;
10337 }
10338
10339 if (pts != null) {
10340 for (var j = 0; j < pts.length; j++) {
10341 var pt = pts[j];
10342 ex1 = pt.x - wHalf;
10343 ex2 = pt.x + wHalf;
10344 ey1 = pt.y - wHalf;
10345 ey2 = pt.y + wHalf;
10346 updateBounds(bounds, ex1, ey1, ex2, ey2);
10347 }
10348 }
10349 } // bezier-like or segment-like edge
10350
10351 } else {
10352 // headless or style disabled
10353 // fallback on source and target positions
10354 //////////////////////////////////////////
10355 var n1 = ele.source();
10356 var n1pos = n1.position();
10357 var n2 = ele.target();
10358 var n2pos = n2.position();
10359 ex1 = n1pos.x;
10360 ex2 = n2pos.x;
10361 ey1 = n1pos.y;
10362 ey2 = n2pos.y;
10363
10364 if (ex1 > ex2) {
10365 var _temp2 = ex1;
10366 ex1 = ex2;
10367 ex2 = _temp2;
10368 }
10369
10370 if (ey1 > ey2) {
10371 var _temp3 = ey1;
10372 ey1 = ey2;
10373 ey2 = _temp3;
10374 } // take into account edge width
10375
10376
10377 ex1 -= wHalf;
10378 ex2 += wHalf;
10379 ey1 -= wHalf;
10380 ey2 += wHalf;
10381 updateBounds(bounds, ex1, ey1, ex2, ey2);
10382 } // headless or style disabled
10383
10384 } // edges
10385 // handle edge arrow size
10386 /////////////////////////
10387
10388
10389 if (styleEnabled && options.includeEdges && isEdge) {
10390 updateBoundsFromArrow(bounds, ele, 'mid-source');
10391 updateBoundsFromArrow(bounds, ele, 'mid-target');
10392 updateBoundsFromArrow(bounds, ele, 'source');
10393 updateBoundsFromArrow(bounds, ele, 'target');
10394 } // ghost
10395 ////////
10396
10397
10398 if (styleEnabled) {
10399 var ghost = ele.pstyle('ghost').value === 'yes';
10400
10401 if (ghost) {
10402 var gx = ele.pstyle('ghost-offset-x').pfValue;
10403 var gy = ele.pstyle('ghost-offset-y').pfValue;
10404 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
10405 }
10406 } // always store the body bounds separately from the labels
10407
10408
10409 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
10410 assignBoundingBox(bbBody, bounds);
10411 expandBoundingBoxSides(bbBody, manualExpansion);
10412 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
10413 // overlay
10414 //////////
10415
10416 if (styleEnabled) {
10417 ex1 = bounds.x1;
10418 ex2 = bounds.x2;
10419 ey1 = bounds.y1;
10420 ey2 = bounds.y2;
10421 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
10422 } // always store the body bounds separately from the labels
10423
10424
10425 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
10426 assignBoundingBox(bbOverlay, bounds);
10427 expandBoundingBoxSides(bbOverlay, manualExpansion);
10428 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
10429 // handle label dimensions
10430 //////////////////////////
10431
10432 var bbLabels = _p.labelBounds = _p.labelBounds || {};
10433
10434 if (bbLabels.all != null) {
10435 clearBoundingBox(bbLabels.all);
10436 } else {
10437 bbLabels.all = makeBoundingBox();
10438 }
10439
10440 if (styleEnabled && options.includeLabels) {
10441 if (options.includeMainLabels) {
10442 updateBoundsFromLabel(bounds, ele, null);
10443 }
10444
10445 if (isEdge) {
10446 if (options.includeSourceLabels) {
10447 updateBoundsFromLabel(bounds, ele, 'source');
10448 }
10449
10450 if (options.includeTargetLabels) {
10451 updateBoundsFromLabel(bounds, ele, 'target');
10452 }
10453 }
10454 } // style enabled for labels
10455
10456 } // if displayed
10457
10458
10459 bounds.x1 = noninf(bounds.x1);
10460 bounds.y1 = noninf(bounds.y1);
10461 bounds.x2 = noninf(bounds.x2);
10462 bounds.y2 = noninf(bounds.y2);
10463 bounds.w = noninf(bounds.x2 - bounds.x1);
10464 bounds.h = noninf(bounds.y2 - bounds.y1);
10465
10466 if (bounds.w > 0 && bounds.h > 0 && displayed) {
10467 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
10468
10469 expandBoundingBox(bounds, 1);
10470 }
10471
10472 return bounds;
10473 };
10474
10475 var getKey = function getKey(opts) {
10476 var i = 0;
10477
10478 var tf = function tf(val) {
10479 return (val ? 1 : 0) << i++;
10480 };
10481
10482 var key = 0;
10483 key += tf(opts.incudeNodes);
10484 key += tf(opts.includeEdges);
10485 key += tf(opts.includeLabels);
10486 key += tf(opts.includeMainLabels);
10487 key += tf(opts.includeSourceLabels);
10488 key += tf(opts.includeTargetLabels);
10489 key += tf(opts.includeOverlays);
10490 return key;
10491 };
10492
10493 var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
10494 if (ele.isEdge()) {
10495 var p1 = ele.source().position();
10496 var p2 = ele.target().position();
10497
10498 var r = function r(x) {
10499 return Math.round(x);
10500 };
10501
10502 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
10503 } else {
10504 return 0;
10505 }
10506 };
10507
10508 var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
10509 var _p = ele._private;
10510 var bb;
10511 var isEdge = ele.isEdge();
10512 var key = opts == null ? defBbOptsKey : getKey(opts);
10513 var usingDefOpts = key === defBbOptsKey;
10514 var currPosKey = getBoundingBoxPosKey(ele);
10515 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10516 var useCache = opts.useCache && isPosKeySame;
10517
10518 var isDirty = function isDirty(ele) {
10519 return ele._private.bbCache == null;
10520 };
10521
10522 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
10523
10524 if (needRecalc) {
10525 if (!isPosKeySame) {
10526 ele.recalculateRenderedStyle();
10527 }
10528
10529 bb = boundingBoxImpl(ele, defBbOpts);
10530 _p.bbCache = bb;
10531 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
10532 _p.bbCachePosKey = currPosKey;
10533 } else {
10534 bb = _p.bbCache;
10535 }
10536
10537 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
10538 var shift = assignShiftToBoundingBox;
10539 var delta = _p.bbCacheShift;
10540
10541 var safeShift = function safeShift(bb, delta) {
10542 if (bb != null) {
10543 shift(bb, delta);
10544 }
10545 };
10546
10547 shift(bb, delta);
10548 var bodyBounds = _p.bodyBounds,
10549 overlayBounds = _p.overlayBounds,
10550 labelBounds = _p.labelBounds,
10551 arrowBounds = _p.arrowBounds;
10552 safeShift(bodyBounds, delta);
10553 safeShift(overlayBounds, delta);
10554
10555 if (arrowBounds != null) {
10556 safeShift(arrowBounds.source, delta);
10557 safeShift(arrowBounds.target, delta);
10558 safeShift(arrowBounds['mid-source'], delta);
10559 safeShift(arrowBounds['mid-target'], delta);
10560 }
10561
10562 if (labelBounds != null) {
10563 safeShift(labelBounds.main, delta);
10564 safeShift(labelBounds.all, delta);
10565 safeShift(labelBounds.source, delta);
10566 safeShift(labelBounds.target, delta);
10567 }
10568 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
10569
10570
10571 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
10572
10573 if (!usingDefOpts) {
10574 var isNode = ele.isNode();
10575 bb = makeBoundingBox();
10576
10577 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
10578 if (opts.includeOverlays) {
10579 updateBoundsFromBox(bb, _p.overlayBounds);
10580 } else {
10581 updateBoundsFromBox(bb, _p.bodyBounds);
10582 }
10583 }
10584
10585 if (opts.includeLabels) {
10586 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
10587 updateBoundsFromBox(bb, _p.labelBounds.all);
10588 } else {
10589 if (opts.includeMainLabels) {
10590 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
10591 }
10592
10593 if (opts.includeSourceLabels) {
10594 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
10595 }
10596
10597 if (opts.includeTargetLabels) {
10598 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
10599 }
10600 }
10601 }
10602
10603 bb.w = bb.x2 - bb.x1;
10604 bb.h = bb.y2 - bb.y1;
10605 }
10606
10607 return bb;
10608 };
10609
10610 var defBbOpts = {
10611 includeNodes: true,
10612 includeEdges: true,
10613 includeLabels: true,
10614 includeMainLabels: true,
10615 includeSourceLabels: true,
10616 includeTargetLabels: true,
10617 includeOverlays: true,
10618 useCache: true
10619 };
10620 var defBbOptsKey = getKey(defBbOpts);
10621 var filledBbOpts = defaults(defBbOpts);
10622
10623 elesfn$k.boundingBox = function (options) {
10624 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
10625 // specified s.t. the cache is used, so check for this case to make it faster by
10626 // avoiding the overhead of the rest of the function
10627
10628 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
10629 if (options === undefined) {
10630 options = defBbOpts;
10631 } else {
10632 options = filledBbOpts(options);
10633 }
10634
10635 bounds = cachedBoundingBoxImpl(this[0], options);
10636 } else {
10637 bounds = makeBoundingBox();
10638 options = options || defBbOpts;
10639 var opts = filledBbOpts(options);
10640 var eles = this;
10641 var cy = eles.cy();
10642 var styleEnabled = cy.styleEnabled();
10643
10644 if (styleEnabled) {
10645 for (var i = 0; i < eles.length; i++) {
10646 var ele = eles[i];
10647 var _p = ele._private;
10648 var currPosKey = getBoundingBoxPosKey(ele);
10649 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10650 var useCache = opts.useCache && isPosKeySame;
10651 ele.recalculateRenderedStyle(useCache);
10652 }
10653 }
10654
10655 this.updateCompoundBounds();
10656
10657 for (var _i = 0; _i < eles.length; _i++) {
10658 var _ele = eles[_i];
10659 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
10660 }
10661 }
10662
10663 bounds.x1 = noninf(bounds.x1);
10664 bounds.y1 = noninf(bounds.y1);
10665 bounds.x2 = noninf(bounds.x2);
10666 bounds.y2 = noninf(bounds.y2);
10667 bounds.w = noninf(bounds.x2 - bounds.x1);
10668 bounds.h = noninf(bounds.y2 - bounds.y1);
10669 return bounds;
10670 };
10671
10672 elesfn$k.dirtyBoundingBoxCache = function () {
10673 for (var i = 0; i < this.length; i++) {
10674 var _p = this[i]._private;
10675 _p.bbCache = null;
10676 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
10677 _p.bbCachePosKey = null;
10678 _p.bodyBounds = null;
10679 _p.overlayBounds = null;
10680 _p.labelBounds.all = null;
10681 _p.labelBounds.source = null;
10682 _p.labelBounds.target = null;
10683 _p.labelBounds.main = null;
10684 _p.labelBounds.sourceRot = null;
10685 _p.labelBounds.targetRot = null;
10686 _p.labelBounds.mainRot = null;
10687 _p.arrowBounds.source = null;
10688 _p.arrowBounds.target = null;
10689 _p.arrowBounds['mid-source'] = null;
10690 _p.arrowBounds['mid-target'] = null;
10691 }
10692
10693 this.emitAndNotify('bounds');
10694 return this;
10695 };
10696
10697 elesfn$k.shiftCachedBoundingBox = function (delta) {
10698 for (var i = 0; i < this.length; i++) {
10699 var ele = this[i];
10700 var _p = ele._private;
10701 var bb = _p.bbCache;
10702
10703 if (bb != null) {
10704 _p.bbCacheShift.x += delta.x;
10705 _p.bbCacheShift.y += delta.y;
10706 }
10707 }
10708
10709 this.emitAndNotify('bounds');
10710 return this;
10711 }; // private helper to get bounding box for custom node positions
10712 // - good for perf in certain cases but currently requires dirtying the rendered style
10713 // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
10714 // - try to use for only things like discrete layouts where the node position would change anyway
10715
10716
10717 elesfn$k.boundingBoxAt = function (fn) {
10718 var nodes = this.nodes();
10719 var cy = this.cy();
10720 var hasCompoundNodes = cy.hasCompoundNodes();
10721
10722 if (hasCompoundNodes) {
10723 nodes = nodes.filter(function (node) {
10724 return !node.isParent();
10725 });
10726 }
10727
10728 if (plainObject(fn)) {
10729 var obj = fn;
10730
10731 fn = function fn() {
10732 return obj;
10733 };
10734 }
10735
10736 var storeOldPos = function storeOldPos(node, i) {
10737 return node._private.bbAtOldPos = fn(node, i);
10738 };
10739
10740 var getOldPos = function getOldPos(node) {
10741 return node._private.bbAtOldPos;
10742 };
10743
10744 cy.startBatch();
10745 nodes.forEach(storeOldPos).silentPositions(fn);
10746
10747 if (hasCompoundNodes) {
10748 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10749 }
10750
10751 var bb = copyBoundingBox(this.boundingBox({
10752 useCache: false
10753 }));
10754 nodes.silentPositions(getOldPos);
10755 cy.endBatch();
10756 return bb;
10757 };
10758
10759 fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10760 fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10761 var bounds = elesfn$k;
10762
10763 var fn$4, elesfn$l;
10764 fn$4 = elesfn$l = {};
10765
10766 var defineDimFns = function defineDimFns(opts) {
10767 opts.uppercaseName = capitalize(opts.name);
10768 opts.autoName = 'auto' + opts.uppercaseName;
10769 opts.labelName = 'label' + opts.uppercaseName;
10770 opts.outerName = 'outer' + opts.uppercaseName;
10771 opts.uppercaseOuterName = capitalize(opts.outerName);
10772
10773 fn$4[opts.name] = function dimImpl() {
10774 var ele = this[0];
10775 var _p = ele._private;
10776 var cy = _p.cy;
10777 var styleEnabled = cy._private.styleEnabled;
10778
10779 if (ele) {
10780 if (styleEnabled) {
10781 if (ele.isParent()) {
10782 ele.updateCompoundBounds();
10783 return _p[opts.autoName] || 0;
10784 }
10785
10786 var d = ele.pstyle(opts.name);
10787
10788 switch (d.strValue) {
10789 case 'label':
10790 ele.recalculateRenderedStyle();
10791 return _p.rstyle[opts.labelName] || 0;
10792
10793 default:
10794 return d.pfValue;
10795 }
10796 } else {
10797 return 1;
10798 }
10799 }
10800 };
10801
10802 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10803 var ele = this[0];
10804 var _p = ele._private;
10805 var cy = _p.cy;
10806 var styleEnabled = cy._private.styleEnabled;
10807
10808 if (ele) {
10809 if (styleEnabled) {
10810 var dim = ele[opts.name]();
10811 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10812
10813 var padding = 2 * ele.padding();
10814 return dim + border + padding;
10815 } else {
10816 return 1;
10817 }
10818 }
10819 };
10820
10821 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10822 var ele = this[0];
10823
10824 if (ele) {
10825 var d = ele[opts.name]();
10826 return d * this.cy().zoom();
10827 }
10828 };
10829
10830 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10831 var ele = this[0];
10832
10833 if (ele) {
10834 var od = ele[opts.outerName]();
10835 return od * this.cy().zoom();
10836 }
10837 };
10838 };
10839
10840 defineDimFns({
10841 name: 'width'
10842 });
10843 defineDimFns({
10844 name: 'height'
10845 });
10846
10847 elesfn$l.padding = function () {
10848 var ele = this[0];
10849 var _p = ele._private;
10850
10851 if (ele.isParent()) {
10852 ele.updateCompoundBounds();
10853
10854 if (_p.autoPadding !== undefined) {
10855 return _p.autoPadding;
10856 } else {
10857 return ele.pstyle('padding').pfValue;
10858 }
10859 } else {
10860 return ele.pstyle('padding').pfValue;
10861 }
10862 };
10863
10864 elesfn$l.paddedHeight = function () {
10865 var ele = this[0];
10866 return ele.height() + 2 * ele.padding();
10867 };
10868
10869 elesfn$l.paddedWidth = function () {
10870 var ele = this[0];
10871 return ele.width() + 2 * ele.padding();
10872 };
10873
10874 var widthHeight = elesfn$l;
10875
10876 var ifEdge = function ifEdge(ele, getValue) {
10877 if (ele.isEdge()) {
10878 return getValue(ele);
10879 }
10880 };
10881
10882 var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10883 if (ele.isEdge()) {
10884 var cy = ele.cy();
10885 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10886 }
10887 };
10888
10889 var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10890 if (ele.isEdge()) {
10891 var cy = ele.cy();
10892 var pan = cy.pan();
10893 var zoom = cy.zoom();
10894 return getPoints(ele).map(function (p) {
10895 return modelToRenderedPosition(p, zoom, pan);
10896 });
10897 }
10898 };
10899
10900 var controlPoints = function controlPoints(ele) {
10901 return ele.renderer().getControlPoints(ele);
10902 };
10903
10904 var segmentPoints = function segmentPoints(ele) {
10905 return ele.renderer().getSegmentPoints(ele);
10906 };
10907
10908 var sourceEndpoint = function sourceEndpoint(ele) {
10909 return ele.renderer().getSourceEndpoint(ele);
10910 };
10911
10912 var targetEndpoint = function targetEndpoint(ele) {
10913 return ele.renderer().getTargetEndpoint(ele);
10914 };
10915
10916 var midpoint = function midpoint(ele) {
10917 return ele.renderer().getEdgeMidpoint(ele);
10918 };
10919
10920 var pts = {
10921 controlPoints: {
10922 get: controlPoints,
10923 mult: true
10924 },
10925 segmentPoints: {
10926 get: segmentPoints,
10927 mult: true
10928 },
10929 sourceEndpoint: {
10930 get: sourceEndpoint
10931 },
10932 targetEndpoint: {
10933 get: targetEndpoint
10934 },
10935 midpoint: {
10936 get: midpoint
10937 }
10938 };
10939
10940 var renderedName = function renderedName(name) {
10941 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10942 };
10943
10944 var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10945 var spec = pts[name];
10946 var rName = renderedName(name);
10947
10948 obj[name] = function () {
10949 return ifEdge(this, spec.get);
10950 };
10951
10952 if (spec.mult) {
10953 obj[rName] = function () {
10954 return ifEdgeRenderedPositions(this, spec.get);
10955 };
10956 } else {
10957 obj[rName] = function () {
10958 return ifEdgeRenderedPosition(this, spec.get);
10959 };
10960 }
10961
10962 return obj;
10963 }, {});
10964
10965 var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10966
10967 /*!
10968 Event object based on jQuery events, MIT license
10969
10970 https://jquery.org/license/
10971 https://tldrlegal.com/license/mit-license
10972 https://github.com/jquery/jquery/blob/master/src/event.js
10973 */
10974 var Event = function Event(src, props) {
10975 this.recycle(src, props);
10976 };
10977
10978 function returnFalse() {
10979 return false;
10980 }
10981
10982 function returnTrue() {
10983 return true;
10984 } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10985
10986
10987 Event.prototype = {
10988 instanceString: function instanceString() {
10989 return 'event';
10990 },
10991 recycle: function recycle(src, props) {
10992 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10993
10994 if (src != null && src.preventDefault) {
10995 // Browser Event object
10996 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10997 // by a handler lower down the tree; reflect the correct value.
10998
10999 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
11000 } else if (src != null && src.type) {
11001 // Plain object containing all event details
11002 props = src;
11003 } else {
11004 // Event string
11005 this.type = src;
11006 } // Put explicitly provided properties onto the event object
11007
11008
11009 if (props != null) {
11010 // more efficient to manually copy fields we use
11011 this.originalEvent = props.originalEvent;
11012 this.type = props.type != null ? props.type : this.type;
11013 this.cy = props.cy;
11014 this.target = props.target;
11015 this.position = props.position;
11016 this.renderedPosition = props.renderedPosition;
11017 this.namespace = props.namespace;
11018 this.layout = props.layout;
11019 }
11020
11021 if (this.cy != null && this.position != null && this.renderedPosition == null) {
11022 // create a rendered position based on the passed position
11023 var pos = this.position;
11024 var zoom = this.cy.zoom();
11025 var pan = this.cy.pan();
11026 this.renderedPosition = {
11027 x: pos.x * zoom + pan.x,
11028 y: pos.y * zoom + pan.y
11029 };
11030 } // Create a timestamp if incoming event doesn't have one
11031
11032
11033 this.timeStamp = src && src.timeStamp || Date.now();
11034 },
11035 preventDefault: function preventDefault() {
11036 this.isDefaultPrevented = returnTrue;
11037 var e = this.originalEvent;
11038
11039 if (!e) {
11040 return;
11041 } // if preventDefault exists run it on the original event
11042
11043
11044 if (e.preventDefault) {
11045 e.preventDefault();
11046 }
11047 },
11048 stopPropagation: function stopPropagation() {
11049 this.isPropagationStopped = returnTrue;
11050 var e = this.originalEvent;
11051
11052 if (!e) {
11053 return;
11054 } // if stopPropagation exists run it on the original event
11055
11056
11057 if (e.stopPropagation) {
11058 e.stopPropagation();
11059 }
11060 },
11061 stopImmediatePropagation: function stopImmediatePropagation() {
11062 this.isImmediatePropagationStopped = returnTrue;
11063 this.stopPropagation();
11064 },
11065 isDefaultPrevented: returnFalse,
11066 isPropagationStopped: returnFalse,
11067 isImmediatePropagationStopped: returnFalse
11068 };
11069
11070 var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
11071
11072 var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
11073
11074 var defaults$8 = {
11075 qualifierCompare: function qualifierCompare(q1, q2) {
11076 return q1 === q2;
11077 },
11078 eventMatches: function eventMatches()
11079 /*context, listener, eventObj*/
11080 {
11081 return true;
11082 },
11083 addEventFields: function addEventFields()
11084 /*context, evt*/
11085 {},
11086 callbackContext: function callbackContext(context
11087 /*, listener, eventObj*/
11088 ) {
11089 return context;
11090 },
11091 beforeEmit: function beforeEmit()
11092 /* context, listener, eventObj */
11093 {},
11094 afterEmit: function afterEmit()
11095 /* context, listener, eventObj */
11096 {},
11097 bubble: function bubble()
11098 /*context*/
11099 {
11100 return false;
11101 },
11102 parent: function parent()
11103 /*context*/
11104 {
11105 return null;
11106 },
11107 context: null
11108 };
11109 var defaultsKeys = Object.keys(defaults$8);
11110 var emptyOpts = {};
11111
11112 function Emitter() {
11113 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
11114 var context = arguments.length > 1 ? arguments[1] : undefined;
11115
11116 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
11117 for (var i = 0; i < defaultsKeys.length; i++) {
11118 var key = defaultsKeys[i];
11119 this[key] = opts[key] || defaults$8[key];
11120 }
11121
11122 this.context = context || this.context;
11123 this.listeners = [];
11124 this.emitting = 0;
11125 }
11126
11127 var p = Emitter.prototype;
11128
11129 var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
11130 if (fn(qualifier)) {
11131 callback = qualifier;
11132 qualifier = null;
11133 }
11134
11135 if (confOverrides) {
11136 if (conf == null) {
11137 conf = confOverrides;
11138 } else {
11139 conf = extend({}, conf, confOverrides);
11140 }
11141 }
11142
11143 var eventList = array(events) ? events : events.split(/\s+/);
11144
11145 for (var i = 0; i < eventList.length; i++) {
11146 var evt = eventList[i];
11147
11148 if (emptyString(evt)) {
11149 continue;
11150 }
11151
11152 var match = evt.match(eventRegex); // type[.namespace]
11153
11154 if (match) {
11155 var type = match[1];
11156 var namespace = match[2] ? match[2] : null;
11157 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
11158
11159 if (ret === false) {
11160 break;
11161 } // allow exiting early
11162
11163 }
11164 }
11165 };
11166
11167 var makeEventObj = function makeEventObj(self, obj) {
11168 self.addEventFields(self.context, obj);
11169 return new Event(obj.type, obj);
11170 };
11171
11172 var forEachEventObj = function forEachEventObj(self, handler, events) {
11173 if (event(events)) {
11174 handler(self, events);
11175 return;
11176 } else if (plainObject(events)) {
11177 handler(self, makeEventObj(self, events));
11178 return;
11179 }
11180
11181 var eventList = array(events) ? events : events.split(/\s+/);
11182
11183 for (var i = 0; i < eventList.length; i++) {
11184 var evt = eventList[i];
11185
11186 if (emptyString(evt)) {
11187 continue;
11188 }
11189
11190 var match = evt.match(eventRegex); // type[.namespace]
11191
11192 if (match) {
11193 var type = match[1];
11194 var namespace = match[2] ? match[2] : null;
11195 var eventObj = makeEventObj(self, {
11196 type: type,
11197 namespace: namespace,
11198 target: self.context
11199 });
11200 handler(self, eventObj);
11201 }
11202 }
11203 };
11204
11205 p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
11206 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
11207 if (fn(callback)) {
11208 self.listeners.push({
11209 event: event,
11210 // full event string
11211 callback: callback,
11212 // callback to run
11213 type: type,
11214 // the event type (e.g. 'click')
11215 namespace: namespace,
11216 // the event namespace (e.g. ".foo")
11217 qualifier: qualifier,
11218 // a restriction on whether to match this emitter
11219 conf: conf // additional configuration
11220
11221 });
11222 }
11223 }, events, qualifier, callback, conf, confOverrides);
11224 return this;
11225 };
11226
11227 p.one = function (events, qualifier, callback, conf) {
11228 return this.on(events, qualifier, callback, conf, {
11229 one: true
11230 });
11231 };
11232
11233 p.removeListener = p.off = function (events, qualifier, callback, conf) {
11234 var _this = this;
11235
11236 if (this.emitting !== 0) {
11237 this.listeners = copyArray(this.listeners);
11238 }
11239
11240 var listeners = this.listeners;
11241
11242 var _loop = function _loop(i) {
11243 var listener = listeners[i];
11244 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
11245 /*, conf*/
11246 ) {
11247 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
11248 listeners.splice(i, 1);
11249 return false;
11250 }
11251 }, events, qualifier, callback, conf);
11252 };
11253
11254 for (var i = listeners.length - 1; i >= 0; i--) {
11255 _loop(i);
11256 }
11257
11258 return this;
11259 };
11260
11261 p.removeAllListeners = function () {
11262 return this.removeListener('*');
11263 };
11264
11265 p.emit = p.trigger = function (events, extraParams, manualCallback) {
11266 var listeners = this.listeners;
11267 var numListenersBeforeEmit = listeners.length;
11268 this.emitting++;
11269
11270 if (!array(extraParams)) {
11271 extraParams = [extraParams];
11272 }
11273
11274 forEachEventObj(this, function (self, eventObj) {
11275 if (manualCallback != null) {
11276 listeners = [{
11277 event: eventObj.event,
11278 type: eventObj.type,
11279 namespace: eventObj.namespace,
11280 callback: manualCallback
11281 }];
11282 numListenersBeforeEmit = listeners.length;
11283 }
11284
11285 var _loop2 = function _loop2(i) {
11286 var listener = listeners[i];
11287
11288 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
11289 var args = [eventObj];
11290
11291 if (extraParams != null) {
11292 push(args, extraParams);
11293 }
11294
11295 self.beforeEmit(self.context, listener, eventObj);
11296
11297 if (listener.conf && listener.conf.one) {
11298 self.listeners = self.listeners.filter(function (l) {
11299 return l !== listener;
11300 });
11301 }
11302
11303 var context = self.callbackContext(self.context, listener, eventObj);
11304 var ret = listener.callback.apply(context, args);
11305 self.afterEmit(self.context, listener, eventObj);
11306
11307 if (ret === false) {
11308 eventObj.stopPropagation();
11309 eventObj.preventDefault();
11310 }
11311 } // if listener matches
11312
11313 };
11314
11315 for (var i = 0; i < numListenersBeforeEmit; i++) {
11316 _loop2(i);
11317 } // for listener
11318
11319
11320 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
11321 self.parent(self.context).emit(eventObj, extraParams);
11322 }
11323 }, events);
11324 this.emitting--;
11325 return this;
11326 };
11327
11328 var emitterOptions = {
11329 qualifierCompare: function qualifierCompare(selector1, selector2) {
11330 if (selector1 == null || selector2 == null) {
11331 return selector1 == null && selector2 == null;
11332 } else {
11333 return selector1.sameText(selector2);
11334 }
11335 },
11336 eventMatches: function eventMatches(ele, listener, eventObj) {
11337 var selector = listener.qualifier;
11338
11339 if (selector != null) {
11340 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
11341 }
11342
11343 return true;
11344 },
11345 addEventFields: function addEventFields(ele, evt) {
11346 evt.cy = ele.cy();
11347 evt.target = ele;
11348 },
11349 callbackContext: function callbackContext(ele, listener, eventObj) {
11350 return listener.qualifier != null ? eventObj.target : ele;
11351 },
11352 beforeEmit: function beforeEmit(context, listener
11353 /*, eventObj*/
11354 ) {
11355 if (listener.conf && listener.conf.once) {
11356 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
11357 }
11358 },
11359 bubble: function bubble() {
11360 return true;
11361 },
11362 parent: function parent(ele) {
11363 return ele.isChild() ? ele.parent() : ele.cy();
11364 }
11365 };
11366
11367 var argSelector = function argSelector(arg) {
11368 if (string(arg)) {
11369 return new Selector(arg);
11370 } else {
11371 return arg;
11372 }
11373 };
11374
11375 var elesfn$m = {
11376 createEmitter: function createEmitter() {
11377 for (var i = 0; i < this.length; i++) {
11378 var ele = this[i];
11379 var _p = ele._private;
11380
11381 if (!_p.emitter) {
11382 _p.emitter = new Emitter(emitterOptions, ele);
11383 }
11384 }
11385
11386 return this;
11387 },
11388 emitter: function emitter() {
11389 return this._private.emitter;
11390 },
11391 on: function on(events, selector, callback) {
11392 var argSel = argSelector(selector);
11393
11394 for (var i = 0; i < this.length; i++) {
11395 var ele = this[i];
11396 ele.emitter().on(events, argSel, callback);
11397 }
11398
11399 return this;
11400 },
11401 removeListener: function removeListener(events, selector, callback) {
11402 var argSel = argSelector(selector);
11403
11404 for (var i = 0; i < this.length; i++) {
11405 var ele = this[i];
11406 ele.emitter().removeListener(events, argSel, callback);
11407 }
11408
11409 return this;
11410 },
11411 removeAllListeners: function removeAllListeners() {
11412 for (var i = 0; i < this.length; i++) {
11413 var ele = this[i];
11414 ele.emitter().removeAllListeners();
11415 }
11416
11417 return this;
11418 },
11419 one: function one(events, selector, callback) {
11420 var argSel = argSelector(selector);
11421
11422 for (var i = 0; i < this.length; i++) {
11423 var ele = this[i];
11424 ele.emitter().one(events, argSel, callback);
11425 }
11426
11427 return this;
11428 },
11429 once: function once(events, selector, callback) {
11430 var argSel = argSelector(selector);
11431
11432 for (var i = 0; i < this.length; i++) {
11433 var ele = this[i];
11434 ele.emitter().on(events, argSel, callback, {
11435 once: true,
11436 onceCollection: this
11437 });
11438 }
11439 },
11440 emit: function emit(events, extraParams) {
11441 for (var i = 0; i < this.length; i++) {
11442 var ele = this[i];
11443 ele.emitter().emit(events, extraParams);
11444 }
11445
11446 return this;
11447 },
11448 emitAndNotify: function emitAndNotify(event, extraParams) {
11449 // for internal use only
11450 if (this.length === 0) {
11451 return;
11452 } // empty collections don't need to notify anything
11453 // notify renderer
11454
11455
11456 this.cy().notify(event, this);
11457 this.emit(event, extraParams);
11458 return this;
11459 }
11460 };
11461 define$3.eventAliasesOn(elesfn$m);
11462
11463 var elesfn$n = {
11464 nodes: function nodes(selector) {
11465 return this.filter(function (ele) {
11466 return ele.isNode();
11467 }).filter(selector);
11468 },
11469 edges: function edges(selector) {
11470 return this.filter(function (ele) {
11471 return ele.isEdge();
11472 }).filter(selector);
11473 },
11474 // internal helper to get nodes and edges as separate collections with single iteration over elements
11475 byGroup: function byGroup() {
11476 var nodes = this.spawn();
11477 var edges = this.spawn();
11478
11479 for (var i = 0; i < this.length; i++) {
11480 var ele = this[i];
11481
11482 if (ele.isNode()) {
11483 nodes.push(ele);
11484 } else {
11485 edges.push(ele);
11486 }
11487 }
11488
11489 return {
11490 nodes: nodes,
11491 edges: edges
11492 };
11493 },
11494 filter: function filter(_filter, thisArg) {
11495 if (_filter === undefined) {
11496 // check this first b/c it's the most common/performant case
11497 return this;
11498 } else if (string(_filter) || elementOrCollection(_filter)) {
11499 return new Selector(_filter).filter(this);
11500 } else if (fn(_filter)) {
11501 var filterEles = this.spawn();
11502 var eles = this;
11503
11504 for (var i = 0; i < eles.length; i++) {
11505 var ele = eles[i];
11506 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
11507
11508 if (include) {
11509 filterEles.push(ele);
11510 }
11511 }
11512
11513 return filterEles;
11514 }
11515
11516 return this.spawn(); // if not handled by above, give 'em an empty collection
11517 },
11518 not: function not(toRemove) {
11519 if (!toRemove) {
11520 return this;
11521 } else {
11522 if (string(toRemove)) {
11523 toRemove = this.filter(toRemove);
11524 }
11525
11526 var elements = this.spawn();
11527
11528 for (var i = 0; i < this.length; i++) {
11529 var element = this[i];
11530 var remove = toRemove.has(element);
11531
11532 if (!remove) {
11533 elements.push(element);
11534 }
11535 }
11536
11537 return elements;
11538 }
11539 },
11540 absoluteComplement: function absoluteComplement() {
11541 var cy = this.cy();
11542 return cy.mutableElements().not(this);
11543 },
11544 intersect: function intersect(other) {
11545 // if a selector is specified, then filter by it instead
11546 if (string(other)) {
11547 var selector = other;
11548 return this.filter(selector);
11549 }
11550
11551 var elements = this.spawn();
11552 var col1 = this;
11553 var col2 = other;
11554 var col1Smaller = this.length < other.length;
11555 var colS = col1Smaller ? col1 : col2;
11556 var colL = col1Smaller ? col2 : col1;
11557
11558 for (var i = 0; i < colS.length; i++) {
11559 var ele = colS[i];
11560
11561 if (colL.has(ele)) {
11562 elements.push(ele);
11563 }
11564 }
11565
11566 return elements;
11567 },
11568 xor: function xor(other) {
11569 var cy = this._private.cy;
11570
11571 if (string(other)) {
11572 other = cy.$(other);
11573 }
11574
11575 var elements = this.spawn();
11576 var col1 = this;
11577 var col2 = other;
11578
11579 var add = function add(col, other) {
11580 for (var i = 0; i < col.length; i++) {
11581 var ele = col[i];
11582 var id = ele._private.data.id;
11583 var inOther = other.hasElementWithId(id);
11584
11585 if (!inOther) {
11586 elements.push(ele);
11587 }
11588 }
11589 };
11590
11591 add(col1, col2);
11592 add(col2, col1);
11593 return elements;
11594 },
11595 diff: function diff(other) {
11596 var cy = this._private.cy;
11597
11598 if (string(other)) {
11599 other = cy.$(other);
11600 }
11601
11602 var left = this.spawn();
11603 var right = this.spawn();
11604 var both = this.spawn();
11605 var col1 = this;
11606 var col2 = other;
11607
11608 var add = function add(col, other, retEles) {
11609 for (var i = 0; i < col.length; i++) {
11610 var ele = col[i];
11611 var id = ele._private.data.id;
11612 var inOther = other.hasElementWithId(id);
11613
11614 if (inOther) {
11615 both.merge(ele);
11616 } else {
11617 retEles.push(ele);
11618 }
11619 }
11620 };
11621
11622 add(col1, col2, left);
11623 add(col2, col1, right);
11624 return {
11625 left: left,
11626 right: right,
11627 both: both
11628 };
11629 },
11630 add: function add(toAdd) {
11631 var cy = this._private.cy;
11632
11633 if (!toAdd) {
11634 return this;
11635 }
11636
11637 if (string(toAdd)) {
11638 var selector = toAdd;
11639 toAdd = cy.mutableElements().filter(selector);
11640 }
11641
11642 var elements = this.spawnSelf();
11643
11644 for (var i = 0; i < toAdd.length; i++) {
11645 var ele = toAdd[i];
11646 var add = !this.has(ele);
11647
11648 if (add) {
11649 elements.push(ele);
11650 }
11651 }
11652
11653 return elements;
11654 },
11655 // in place merge on calling collection
11656 merge: function merge(toAdd) {
11657 var _p = this._private;
11658 var cy = _p.cy;
11659
11660 if (!toAdd) {
11661 return this;
11662 }
11663
11664 if (toAdd && string(toAdd)) {
11665 var selector = toAdd;
11666 toAdd = cy.mutableElements().filter(selector);
11667 }
11668
11669 var map = _p.map;
11670
11671 for (var i = 0; i < toAdd.length; i++) {
11672 var toAddEle = toAdd[i];
11673 var id = toAddEle._private.data.id;
11674 var add = !map.has(id);
11675
11676 if (add) {
11677 var index = this.length++;
11678 this[index] = toAddEle;
11679 map.set(id, {
11680 ele: toAddEle,
11681 index: index
11682 });
11683 }
11684 }
11685
11686 return this; // chaining
11687 },
11688 unmergeAt: function unmergeAt(i) {
11689 var ele = this[i];
11690 var id = ele.id();
11691 var _p = this._private;
11692 var map = _p.map; // remove ele
11693
11694 this[i] = undefined;
11695 map["delete"](id);
11696 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
11697
11698 if (this.length > 1 && !unmergedLastEle) {
11699 var lastEleI = this.length - 1;
11700 var lastEle = this[lastEleI];
11701 var lastEleId = lastEle._private.data.id;
11702 this[lastEleI] = undefined;
11703 this[i] = lastEle;
11704 map.set(lastEleId, {
11705 ele: lastEle,
11706 index: i
11707 });
11708 } // the collection is now 1 ele smaller
11709
11710
11711 this.length--;
11712 return this;
11713 },
11714 // remove single ele in place in calling collection
11715 unmergeOne: function unmergeOne(ele) {
11716 ele = ele[0];
11717 var _p = this._private;
11718 var id = ele._private.data.id;
11719 var map = _p.map;
11720 var entry = map.get(id);
11721
11722 if (!entry) {
11723 return this; // no need to remove
11724 }
11725
11726 var i = entry.index;
11727 this.unmergeAt(i);
11728 return this;
11729 },
11730 // remove eles in place on calling collection
11731 unmerge: function unmerge(toRemove) {
11732 var cy = this._private.cy;
11733
11734 if (!toRemove) {
11735 return this;
11736 }
11737
11738 if (toRemove && string(toRemove)) {
11739 var selector = toRemove;
11740 toRemove = cy.mutableElements().filter(selector);
11741 }
11742
11743 for (var i = 0; i < toRemove.length; i++) {
11744 this.unmergeOne(toRemove[i]);
11745 }
11746
11747 return this; // chaining
11748 },
11749 unmergeBy: function unmergeBy(toRmFn) {
11750 for (var i = this.length - 1; i >= 0; i--) {
11751 var ele = this[i];
11752
11753 if (toRmFn(ele)) {
11754 this.unmergeAt(i);
11755 }
11756 }
11757
11758 return this;
11759 },
11760 map: function map(mapFn, thisArg) {
11761 var arr = [];
11762 var eles = this;
11763
11764 for (var i = 0; i < eles.length; i++) {
11765 var ele = eles[i];
11766 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11767 arr.push(ret);
11768 }
11769
11770 return arr;
11771 },
11772 reduce: function reduce(fn, initialValue) {
11773 var val = initialValue;
11774 var eles = this;
11775
11776 for (var i = 0; i < eles.length; i++) {
11777 val = fn(val, eles[i], i, eles);
11778 }
11779
11780 return val;
11781 },
11782 max: function max(valFn, thisArg) {
11783 var max = -Infinity;
11784 var maxEle;
11785 var eles = this;
11786
11787 for (var i = 0; i < eles.length; i++) {
11788 var ele = eles[i];
11789 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11790
11791 if (val > max) {
11792 max = val;
11793 maxEle = ele;
11794 }
11795 }
11796
11797 return {
11798 value: max,
11799 ele: maxEle
11800 };
11801 },
11802 min: function min(valFn, thisArg) {
11803 var min = Infinity;
11804 var minEle;
11805 var eles = this;
11806
11807 for (var i = 0; i < eles.length; i++) {
11808 var ele = eles[i];
11809 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11810
11811 if (val < min) {
11812 min = val;
11813 minEle = ele;
11814 }
11815 }
11816
11817 return {
11818 value: min,
11819 ele: minEle
11820 };
11821 }
11822 }; // aliases
11823
11824 var fn$5 = elesfn$n;
11825 fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11826 fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11827 fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11828 fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11829 fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11830 fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11831
11832 var elesfn$o = {
11833 isNode: function isNode() {
11834 return this.group() === 'nodes';
11835 },
11836 isEdge: function isEdge() {
11837 return this.group() === 'edges';
11838 },
11839 isLoop: function isLoop() {
11840 return this.isEdge() && this.source()[0] === this.target()[0];
11841 },
11842 isSimple: function isSimple() {
11843 return this.isEdge() && this.source()[0] !== this.target()[0];
11844 },
11845 group: function group() {
11846 var ele = this[0];
11847
11848 if (ele) {
11849 return ele._private.group;
11850 }
11851 }
11852 };
11853
11854 /**
11855 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11856 * and z-index (low to high). These styles affect how this applies:
11857 *
11858 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11859 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11860 * root to leaves of the compound graph. The last drawn is `top`.
11861 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11862 * `manual` ignores this convention and draws based on the `z-index` value setting.
11863 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11864 * `z-index` will be drawn on top of an element with a lower `z-index`.
11865 */
11866
11867 var zIndexSort = function zIndexSort(a, b) {
11868 var cy = a.cy();
11869 var hasCompoundNodes = cy.hasCompoundNodes();
11870
11871 function getDepth(ele) {
11872 var style = ele.pstyle('z-compound-depth');
11873
11874 if (style.value === 'auto') {
11875 return hasCompoundNodes ? ele.zDepth() : 0;
11876 } else if (style.value === 'bottom') {
11877 return -1;
11878 } else if (style.value === 'top') {
11879 return MAX_INT;
11880 } // 'orphan'
11881
11882
11883 return 0;
11884 }
11885
11886 var depthDiff = getDepth(a) - getDepth(b);
11887
11888 if (depthDiff !== 0) {
11889 return depthDiff;
11890 }
11891
11892 function getEleDepth(ele) {
11893 var style = ele.pstyle('z-index-compare');
11894
11895 if (style.value === 'auto') {
11896 return ele.isNode() ? 1 : 0;
11897 } // 'manual'
11898
11899
11900 return 0;
11901 }
11902
11903 var eleDiff = getEleDepth(a) - getEleDepth(b);
11904
11905 if (eleDiff !== 0) {
11906 return eleDiff;
11907 }
11908
11909 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11910
11911 if (zDiff !== 0) {
11912 return zDiff;
11913 } // compare indices in the core (order added to graph w/ last on top)
11914
11915
11916 return a.poolIndex() - b.poolIndex();
11917 };
11918
11919 var elesfn$p = {
11920 forEach: function forEach(fn$1, thisArg) {
11921 if (fn(fn$1)) {
11922 var N = this.length;
11923
11924 for (var i = 0; i < N; i++) {
11925 var ele = this[i];
11926 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11927
11928 if (ret === false) {
11929 break;
11930 } // exit each early on return false
11931
11932 }
11933 }
11934
11935 return this;
11936 },
11937 toArray: function toArray() {
11938 var array = [];
11939
11940 for (var i = 0; i < this.length; i++) {
11941 array.push(this[i]);
11942 }
11943
11944 return array;
11945 },
11946 slice: function slice(start, end) {
11947 var array = [];
11948 var thisSize = this.length;
11949
11950 if (end == null) {
11951 end = thisSize;
11952 }
11953
11954 if (start == null) {
11955 start = 0;
11956 }
11957
11958 if (start < 0) {
11959 start = thisSize + start;
11960 }
11961
11962 if (end < 0) {
11963 end = thisSize + end;
11964 }
11965
11966 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11967 array.push(this[i]);
11968 }
11969
11970 return this.spawn(array);
11971 },
11972 size: function size() {
11973 return this.length;
11974 },
11975 eq: function eq(i) {
11976 return this[i] || this.spawn();
11977 },
11978 first: function first() {
11979 return this[0] || this.spawn();
11980 },
11981 last: function last() {
11982 return this[this.length - 1] || this.spawn();
11983 },
11984 empty: function empty() {
11985 return this.length === 0;
11986 },
11987 nonempty: function nonempty() {
11988 return !this.empty();
11989 },
11990 sort: function sort(sortFn) {
11991 if (!fn(sortFn)) {
11992 return this;
11993 }
11994
11995 var sorted = this.toArray().sort(sortFn);
11996 return this.spawn(sorted);
11997 },
11998 sortByZIndex: function sortByZIndex() {
11999 return this.sort(zIndexSort);
12000 },
12001 zDepth: function zDepth() {
12002 var ele = this[0];
12003
12004 if (!ele) {
12005 return undefined;
12006 } // let cy = ele.cy();
12007
12008
12009 var _p = ele._private;
12010 var group = _p.group;
12011
12012 if (group === 'nodes') {
12013 var depth = _p.data.parent ? ele.parents().size() : 0;
12014
12015 if (!ele.isParent()) {
12016 return MAX_INT - 1; // childless nodes always on top
12017 }
12018
12019 return depth;
12020 } else {
12021 var src = _p.source;
12022 var tgt = _p.target;
12023 var srcDepth = src.zDepth();
12024 var tgtDepth = tgt.zDepth();
12025 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
12026 }
12027 }
12028 };
12029 elesfn$p.each = elesfn$p.forEach;
12030
12031 var defineSymbolIterator = function defineSymbolIterator() {
12032 var typeofUndef = "undefined" ;
12033 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
12034
12035 if (isIteratorSupported) {
12036 elesfn$p[Symbol.iterator] = function () {
12037 var _this = this;
12038
12039 // eslint-disable-line no-undef
12040 var entry = {
12041 value: undefined,
12042 done: false
12043 };
12044 var i = 0;
12045 var length = this.length;
12046 return _defineProperty({
12047 next: function next() {
12048 if (i < length) {
12049 entry.value = _this[i++];
12050 } else {
12051 entry.value = undefined;
12052 entry.done = true;
12053 }
12054
12055 return entry;
12056 }
12057 }, Symbol.iterator, function () {
12058 // eslint-disable-line no-undef
12059 return this;
12060 });
12061 };
12062 }
12063 };
12064
12065 defineSymbolIterator();
12066
12067 var getLayoutDimensionOptions = defaults({
12068 nodeDimensionsIncludeLabels: false
12069 });
12070 var elesfn$q = {
12071 // Calculates and returns node dimensions { x, y } based on options given
12072 layoutDimensions: function layoutDimensions(options) {
12073 options = getLayoutDimensionOptions(options);
12074 var dims;
12075
12076 if (!this.takesUpSpace()) {
12077 dims = {
12078 w: 0,
12079 h: 0
12080 };
12081 } else if (options.nodeDimensionsIncludeLabels) {
12082 var bbDim = this.boundingBox();
12083 dims = {
12084 w: bbDim.w,
12085 h: bbDim.h
12086 };
12087 } else {
12088 dims = {
12089 w: this.outerWidth(),
12090 h: this.outerHeight()
12091 };
12092 } // sanitise the dimensions for external layouts (avoid division by zero)
12093
12094
12095 if (dims.w === 0 || dims.h === 0) {
12096 dims.w = dims.h = 1;
12097 }
12098
12099 return dims;
12100 },
12101 // using standard layout options, apply position function (w/ or w/o animation)
12102 layoutPositions: function layoutPositions(layout, options, fn) {
12103 var nodes = this.nodes();
12104 var cy = this.cy();
12105 var layoutEles = options.eles; // nodes & edges
12106
12107 var getMemoizeKey = function getMemoizeKey(node) {
12108 return node.id();
12109 };
12110
12111 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
12112
12113 layout.emit({
12114 type: 'layoutstart',
12115 layout: layout
12116 });
12117 layout.animations = [];
12118
12119 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
12120 var center = {
12121 x: nodesBb.x1 + nodesBb.w / 2,
12122 y: nodesBb.y1 + nodesBb.h / 2
12123 };
12124 var spacingVector = {
12125 // scale from center of bounding box (not necessarily 0,0)
12126 x: (pos.x - center.x) * spacing,
12127 y: (pos.y - center.y) * spacing
12128 };
12129 return {
12130 x: center.x + spacingVector.x,
12131 y: center.y + spacingVector.y
12132 };
12133 };
12134
12135 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
12136
12137 var spacingBb = function spacingBb() {
12138 if (!useSpacingFactor) {
12139 return null;
12140 }
12141
12142 var bb = makeBoundingBox();
12143
12144 for (var i = 0; i < nodes.length; i++) {
12145 var node = nodes[i];
12146 var pos = fnMem(node, i);
12147 expandBoundingBoxByPoint(bb, pos.x, pos.y);
12148 }
12149
12150 return bb;
12151 };
12152
12153 var bb = spacingBb();
12154 var getFinalPos = memoize(function (node, i) {
12155 var newPos = fnMem(node, i);
12156
12157 if (useSpacingFactor) {
12158 var spacing = Math.abs(options.spacingFactor);
12159 newPos = calculateSpacing(spacing, bb, newPos);
12160 }
12161
12162 if (options.transform != null) {
12163 newPos = options.transform(node, newPos);
12164 }
12165
12166 return newPos;
12167 }, getMemoizeKey);
12168
12169 if (options.animate) {
12170 for (var i = 0; i < nodes.length; i++) {
12171 var node = nodes[i];
12172 var newPos = getFinalPos(node, i);
12173 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
12174
12175 if (animateNode) {
12176 var ani = node.animation({
12177 position: newPos,
12178 duration: options.animationDuration,
12179 easing: options.animationEasing
12180 });
12181 layout.animations.push(ani);
12182 } else {
12183 node.position(newPos);
12184 }
12185 }
12186
12187 if (options.fit) {
12188 var fitAni = cy.animation({
12189 fit: {
12190 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
12191 padding: options.padding
12192 },
12193 duration: options.animationDuration,
12194 easing: options.animationEasing
12195 });
12196 layout.animations.push(fitAni);
12197 } else if (options.zoom !== undefined && options.pan !== undefined) {
12198 var zoomPanAni = cy.animation({
12199 zoom: options.zoom,
12200 pan: options.pan,
12201 duration: options.animationDuration,
12202 easing: options.animationEasing
12203 });
12204 layout.animations.push(zoomPanAni);
12205 }
12206
12207 layout.animations.forEach(function (ani) {
12208 return ani.play();
12209 });
12210 layout.one('layoutready', options.ready);
12211 layout.emit({
12212 type: 'layoutready',
12213 layout: layout
12214 });
12215 Promise$1.all(layout.animations.map(function (ani) {
12216 return ani.promise();
12217 })).then(function () {
12218 layout.one('layoutstop', options.stop);
12219 layout.emit({
12220 type: 'layoutstop',
12221 layout: layout
12222 });
12223 });
12224 } else {
12225 nodes.positions(getFinalPos);
12226
12227 if (options.fit) {
12228 cy.fit(options.eles, options.padding);
12229 }
12230
12231 if (options.zoom != null) {
12232 cy.zoom(options.zoom);
12233 }
12234
12235 if (options.pan) {
12236 cy.pan(options.pan);
12237 }
12238
12239 layout.one('layoutready', options.ready);
12240 layout.emit({
12241 type: 'layoutready',
12242 layout: layout
12243 });
12244 layout.one('layoutstop', options.stop);
12245 layout.emit({
12246 type: 'layoutstop',
12247 layout: layout
12248 });
12249 }
12250
12251 return this; // chaining
12252 },
12253 layout: function layout(options) {
12254 var cy = this.cy();
12255 return cy.makeLayout(extend({}, options, {
12256 eles: this
12257 }));
12258 }
12259 }; // aliases:
12260
12261 elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
12262
12263 function styleCache(key, fn, ele) {
12264 var _p = ele._private;
12265 var cache = _p.styleCache = _p.styleCache || [];
12266 var val;
12267
12268 if ((val = cache[key]) != null) {
12269 return val;
12270 } else {
12271 val = cache[key] = fn(ele);
12272 return val;
12273 }
12274 }
12275
12276 function cacheStyleFunction(key, fn) {
12277 key = hashString(key);
12278 return function cachedStyleFunction(ele) {
12279 return styleCache(key, fn, ele);
12280 };
12281 }
12282
12283 function cachePrototypeStyleFunction(key, fn) {
12284 key = hashString(key);
12285
12286 var selfFn = function selfFn(ele) {
12287 return fn.call(ele);
12288 };
12289
12290 return function cachedPrototypeStyleFunction() {
12291 var ele = this[0];
12292
12293 if (ele) {
12294 return styleCache(key, selfFn, ele);
12295 }
12296 };
12297 }
12298
12299 var elesfn$r = {
12300 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
12301 var cy = this.cy();
12302 var renderer = cy.renderer();
12303 var styleEnabled = cy.styleEnabled();
12304
12305 if (renderer && styleEnabled) {
12306 renderer.recalculateRenderedStyle(this, useCache);
12307 }
12308
12309 return this;
12310 },
12311 dirtyStyleCache: function dirtyStyleCache() {
12312 var cy = this.cy();
12313
12314 var dirty = function dirty(ele) {
12315 return ele._private.styleCache = null;
12316 };
12317
12318 if (cy.hasCompoundNodes()) {
12319 var eles;
12320 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12321 eles.merge(eles.connectedEdges());
12322 eles.forEach(dirty);
12323 } else {
12324 this.forEach(function (ele) {
12325 dirty(ele);
12326 ele.connectedEdges().forEach(dirty);
12327 });
12328 }
12329
12330 return this;
12331 },
12332 // fully updates (recalculates) the style for the elements
12333 updateStyle: function updateStyle(notifyRenderer) {
12334 var cy = this._private.cy;
12335
12336 if (!cy.styleEnabled()) {
12337 return this;
12338 }
12339
12340 if (cy.batching()) {
12341 var bEles = cy._private.batchStyleEles;
12342 bEles.merge(this);
12343 return this; // chaining and exit early when batching
12344 }
12345
12346 var hasCompounds = cy.hasCompoundNodes();
12347 var updatedEles = this;
12348 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
12349
12350 if (hasCompounds) {
12351 // then add everything up and down for compound selector checks
12352 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12353 } // let changedEles = style.apply( updatedEles );
12354
12355
12356 var changedEles = updatedEles;
12357
12358 if (notifyRenderer) {
12359 changedEles.emitAndNotify('style'); // let renderer know we changed style
12360 } else {
12361 changedEles.emit('style'); // just fire the event
12362 }
12363
12364 updatedEles.forEach(function (ele) {
12365 return ele._private.styleDirty = true;
12366 });
12367 return this; // chaining
12368 },
12369 // get the internal parsed style object for the specified property
12370 parsedStyle: function parsedStyle(property) {
12371 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12372 var ele = this[0];
12373 var cy = ele.cy();
12374
12375 if (!cy.styleEnabled()) {
12376 return;
12377 }
12378
12379 if (ele) {
12380 if (ele._private.styleDirty) {
12381 ele._private.styleDirty = false;
12382 cy.style().apply(ele);
12383 ele.emitAndNotify('style');
12384 }
12385
12386 var overriddenStyle = ele._private.style[property];
12387
12388 if (overriddenStyle != null) {
12389 return overriddenStyle;
12390 } else if (includeNonDefault) {
12391 return cy.style().getDefaultProperty(property);
12392 } else {
12393 return null;
12394 }
12395 }
12396 },
12397 numericStyle: function numericStyle(property) {
12398 var ele = this[0];
12399
12400 if (!ele.cy().styleEnabled()) {
12401 return;
12402 }
12403
12404 if (ele) {
12405 var pstyle = ele.pstyle(property);
12406 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
12407 }
12408 },
12409 numericStyleUnits: function numericStyleUnits(property) {
12410 var ele = this[0];
12411
12412 if (!ele.cy().styleEnabled()) {
12413 return;
12414 }
12415
12416 if (ele) {
12417 return ele.pstyle(property).units;
12418 }
12419 },
12420 // get the specified css property as a rendered value (i.e. on-screen value)
12421 // or get the whole rendered style if no property specified (NB doesn't allow setting)
12422 renderedStyle: function renderedStyle(property) {
12423 var cy = this.cy();
12424
12425 if (!cy.styleEnabled()) {
12426 return this;
12427 }
12428
12429 var ele = this[0];
12430
12431 if (ele) {
12432 return cy.style().getRenderedStyle(ele, property);
12433 }
12434 },
12435 // read the calculated css style of the element or override the style (via a bypass)
12436 style: function style(name, value) {
12437 var cy = this.cy();
12438
12439 if (!cy.styleEnabled()) {
12440 return this;
12441 }
12442
12443 var updateTransitions = false;
12444 var style = cy.style();
12445
12446 if (plainObject(name)) {
12447 // then extend the bypass
12448 var props = name;
12449 style.applyBypass(this, props, updateTransitions);
12450 this.emitAndNotify('style'); // let the renderer know we've updated style
12451 } else if (string(name)) {
12452 if (value === undefined) {
12453 // then get the property from the style
12454 var ele = this[0];
12455
12456 if (ele) {
12457 return style.getStylePropertyValue(ele, name);
12458 } else {
12459 // empty collection => can't get any value
12460 return;
12461 }
12462 } else {
12463 // then set the bypass with the property value
12464 style.applyBypass(this, name, value, updateTransitions);
12465 this.emitAndNotify('style'); // let the renderer know we've updated style
12466 }
12467 } else if (name === undefined) {
12468 var _ele = this[0];
12469
12470 if (_ele) {
12471 return style.getRawStyle(_ele);
12472 } else {
12473 // empty collection => can't get any value
12474 return;
12475 }
12476 }
12477
12478 return this; // chaining
12479 },
12480 removeStyle: function removeStyle(names) {
12481 var cy = this.cy();
12482
12483 if (!cy.styleEnabled()) {
12484 return this;
12485 }
12486
12487 var updateTransitions = false;
12488 var style = cy.style();
12489 var eles = this;
12490
12491 if (names === undefined) {
12492 for (var i = 0; i < eles.length; i++) {
12493 var ele = eles[i];
12494 style.removeAllBypasses(ele, updateTransitions);
12495 }
12496 } else {
12497 names = names.split(/\s+/);
12498
12499 for (var _i = 0; _i < eles.length; _i++) {
12500 var _ele2 = eles[_i];
12501 style.removeBypasses(_ele2, names, updateTransitions);
12502 }
12503 }
12504
12505 this.emitAndNotify('style'); // let the renderer know we've updated style
12506
12507 return this; // chaining
12508 },
12509 show: function show() {
12510 this.css('display', 'element');
12511 return this; // chaining
12512 },
12513 hide: function hide() {
12514 this.css('display', 'none');
12515 return this; // chaining
12516 },
12517 effectiveOpacity: function effectiveOpacity() {
12518 var cy = this.cy();
12519
12520 if (!cy.styleEnabled()) {
12521 return 1;
12522 }
12523
12524 var hasCompoundNodes = cy.hasCompoundNodes();
12525 var ele = this[0];
12526
12527 if (ele) {
12528 var _p = ele._private;
12529 var parentOpacity = ele.pstyle('opacity').value;
12530
12531 if (!hasCompoundNodes) {
12532 return parentOpacity;
12533 }
12534
12535 var parents = !_p.data.parent ? null : ele.parents();
12536
12537 if (parents) {
12538 for (var i = 0; i < parents.length; i++) {
12539 var parent = parents[i];
12540 var opacity = parent.pstyle('opacity').value;
12541 parentOpacity = opacity * parentOpacity;
12542 }
12543 }
12544
12545 return parentOpacity;
12546 }
12547 },
12548 transparent: function transparent() {
12549 var cy = this.cy();
12550
12551 if (!cy.styleEnabled()) {
12552 return false;
12553 }
12554
12555 var ele = this[0];
12556 var hasCompoundNodes = ele.cy().hasCompoundNodes();
12557
12558 if (ele) {
12559 if (!hasCompoundNodes) {
12560 return ele.pstyle('opacity').value === 0;
12561 } else {
12562 return ele.effectiveOpacity() === 0;
12563 }
12564 }
12565 },
12566 backgrounding: function backgrounding() {
12567 var cy = this.cy();
12568
12569 if (!cy.styleEnabled()) {
12570 return false;
12571 }
12572
12573 var ele = this[0];
12574 return ele._private.backgrounding ? true : false;
12575 }
12576 };
12577
12578 function checkCompound(ele, parentOk) {
12579 var _p = ele._private;
12580 var parents = _p.data.parent ? ele.parents() : null;
12581
12582 if (parents) {
12583 for (var i = 0; i < parents.length; i++) {
12584 var parent = parents[i];
12585
12586 if (!parentOk(parent)) {
12587 return false;
12588 }
12589 }
12590 }
12591
12592 return true;
12593 }
12594
12595 function defineDerivedStateFunction(specs) {
12596 var ok = specs.ok;
12597 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
12598 var parentOk = specs.parentOk || specs.ok;
12599 return function () {
12600 var cy = this.cy();
12601
12602 if (!cy.styleEnabled()) {
12603 return true;
12604 }
12605
12606 var ele = this[0];
12607 var hasCompoundNodes = cy.hasCompoundNodes();
12608
12609 if (ele) {
12610 var _p = ele._private;
12611
12612 if (!ok(ele)) {
12613 return false;
12614 }
12615
12616 if (ele.isNode()) {
12617 return !hasCompoundNodes || checkCompound(ele, parentOk);
12618 } else {
12619 var src = _p.source;
12620 var tgt = _p.target;
12621 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
12622 }
12623 }
12624 };
12625 }
12626
12627 var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
12628 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
12629 });
12630 elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
12631 ok: eleTakesUpSpace
12632 }));
12633 var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
12634 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
12635 });
12636 var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
12637 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
12638 });
12639 elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
12640 ok: eleInteractive,
12641 parentOk: parentInteractive,
12642 edgeOkViaNode: eleTakesUpSpace
12643 }));
12644
12645 elesfn$r.noninteractive = function () {
12646 var ele = this[0];
12647
12648 if (ele) {
12649 return !ele.interactive();
12650 }
12651 };
12652
12653 var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
12654 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
12655 });
12656 var edgeVisibleViaNode = eleTakesUpSpace;
12657 elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
12658 ok: eleVisible,
12659 edgeOkViaNode: edgeVisibleViaNode
12660 }));
12661
12662 elesfn$r.hidden = function () {
12663 var ele = this[0];
12664
12665 if (ele) {
12666 return !ele.visible();
12667 }
12668 };
12669
12670 elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
12671 if (!this.cy().styleEnabled()) {
12672 return false;
12673 }
12674
12675 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
12676 });
12677 elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
12678 elesfn$r.renderedCss = elesfn$r.renderedStyle;
12679 elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
12680 elesfn$r.pstyle = elesfn$r.parsedStyle;
12681
12682 var elesfn$s = {};
12683
12684 function defineSwitchFunction(params) {
12685 return function () {
12686 var args = arguments;
12687 var changedEles = []; // e.g. cy.nodes().select( data, handler )
12688
12689 if (args.length === 2) {
12690 var data = args[0];
12691 var handler = args[1];
12692 this.on(params.event, data, handler);
12693 } // e.g. cy.nodes().select( handler )
12694 else if (args.length === 1 && fn(args[0])) {
12695 var _handler = args[0];
12696 this.on(params.event, _handler);
12697 } // e.g. cy.nodes().select()
12698 // e.g. (private) cy.nodes().select(['tapselect'])
12699 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12700 var addlEvents = args.length === 1 ? args[0] : null;
12701
12702 for (var i = 0; i < this.length; i++) {
12703 var ele = this[i];
12704 var able = !params.ableField || ele._private[params.ableField];
12705 var changed = ele._private[params.field] != params.value;
12706
12707 if (params.overrideAble) {
12708 var overrideAble = params.overrideAble(ele);
12709
12710 if (overrideAble !== undefined) {
12711 able = overrideAble;
12712
12713 if (!overrideAble) {
12714 return this;
12715 } // to save cycles assume not able for all on override
12716
12717 }
12718 }
12719
12720 if (able) {
12721 ele._private[params.field] = params.value;
12722
12723 if (changed) {
12724 changedEles.push(ele);
12725 }
12726 }
12727 }
12728
12729 var changedColl = this.spawn(changedEles);
12730 changedColl.updateStyle(); // change of state => possible change of style
12731
12732 changedColl.emit(params.event);
12733
12734 if (addlEvents) {
12735 changedColl.emit(addlEvents);
12736 }
12737 }
12738
12739 return this;
12740 };
12741 }
12742
12743 function defineSwitchSet(params) {
12744 elesfn$s[params.field] = function () {
12745 var ele = this[0];
12746
12747 if (ele) {
12748 if (params.overrideField) {
12749 var val = params.overrideField(ele);
12750
12751 if (val !== undefined) {
12752 return val;
12753 }
12754 }
12755
12756 return ele._private[params.field];
12757 }
12758 };
12759
12760 elesfn$s[params.on] = defineSwitchFunction({
12761 event: params.on,
12762 field: params.field,
12763 ableField: params.ableField,
12764 overrideAble: params.overrideAble,
12765 value: true
12766 });
12767 elesfn$s[params.off] = defineSwitchFunction({
12768 event: params.off,
12769 field: params.field,
12770 ableField: params.ableField,
12771 overrideAble: params.overrideAble,
12772 value: false
12773 });
12774 }
12775
12776 defineSwitchSet({
12777 field: 'locked',
12778 overrideField: function overrideField(ele) {
12779 return ele.cy().autolock() ? true : undefined;
12780 },
12781 on: 'lock',
12782 off: 'unlock'
12783 });
12784 defineSwitchSet({
12785 field: 'grabbable',
12786 overrideField: function overrideField(ele) {
12787 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12788 },
12789 on: 'grabify',
12790 off: 'ungrabify'
12791 });
12792 defineSwitchSet({
12793 field: 'selected',
12794 ableField: 'selectable',
12795 overrideAble: function overrideAble(ele) {
12796 return ele.cy().autounselectify() ? false : undefined;
12797 },
12798 on: 'select',
12799 off: 'unselect'
12800 });
12801 defineSwitchSet({
12802 field: 'selectable',
12803 overrideField: function overrideField(ele) {
12804 return ele.cy().autounselectify() ? false : undefined;
12805 },
12806 on: 'selectify',
12807 off: 'unselectify'
12808 });
12809 elesfn$s.deselect = elesfn$s.unselect;
12810
12811 elesfn$s.grabbed = function () {
12812 var ele = this[0];
12813
12814 if (ele) {
12815 return ele._private.grabbed;
12816 }
12817 };
12818
12819 defineSwitchSet({
12820 field: 'active',
12821 on: 'activate',
12822 off: 'unactivate'
12823 });
12824 defineSwitchSet({
12825 field: 'pannable',
12826 on: 'panify',
12827 off: 'unpanify'
12828 });
12829
12830 elesfn$s.inactive = function () {
12831 var ele = this[0];
12832
12833 if (ele) {
12834 return !ele._private.active;
12835 }
12836 };
12837
12838 var elesfn$t = {}; // DAG functions
12839 ////////////////
12840
12841 var defineDagExtremity = function defineDagExtremity(params) {
12842 return function dagExtremityImpl(selector) {
12843 var eles = this;
12844 var ret = [];
12845
12846 for (var i = 0; i < eles.length; i++) {
12847 var ele = eles[i];
12848
12849 if (!ele.isNode()) {
12850 continue;
12851 }
12852
12853 var disqualified = false;
12854 var edges = ele.connectedEdges();
12855
12856 for (var j = 0; j < edges.length; j++) {
12857 var edge = edges[j];
12858 var src = edge.source();
12859 var tgt = edge.target();
12860
12861 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12862 disqualified = true;
12863 break;
12864 }
12865 }
12866
12867 if (!disqualified) {
12868 ret.push(ele);
12869 }
12870 }
12871
12872 return this.spawn(ret, true).filter(selector);
12873 };
12874 };
12875
12876 var defineDagOneHop = function defineDagOneHop(params) {
12877 return function (selector) {
12878 var eles = this;
12879 var oEles = [];
12880
12881 for (var i = 0; i < eles.length; i++) {
12882 var ele = eles[i];
12883
12884 if (!ele.isNode()) {
12885 continue;
12886 }
12887
12888 var edges = ele.connectedEdges();
12889
12890 for (var j = 0; j < edges.length; j++) {
12891 var edge = edges[j];
12892 var src = edge.source();
12893 var tgt = edge.target();
12894
12895 if (params.outgoing && src === ele) {
12896 oEles.push(edge);
12897 oEles.push(tgt);
12898 } else if (params.incoming && tgt === ele) {
12899 oEles.push(edge);
12900 oEles.push(src);
12901 }
12902 }
12903 }
12904
12905 return this.spawn(oEles, true).filter(selector);
12906 };
12907 };
12908
12909 var defineDagAllHops = function defineDagAllHops(params) {
12910 return function (selector) {
12911 var eles = this;
12912 var sEles = [];
12913 var sElesIds = {};
12914
12915 for (;;) {
12916 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12917
12918 if (next.length === 0) {
12919 break;
12920 } // done if none left
12921
12922
12923 var newNext = false;
12924
12925 for (var i = 0; i < next.length; i++) {
12926 var n = next[i];
12927 var nid = n.id();
12928
12929 if (!sElesIds[nid]) {
12930 sElesIds[nid] = true;
12931 sEles.push(n);
12932 newNext = true;
12933 }
12934 }
12935
12936 if (!newNext) {
12937 break;
12938 } // done if touched all outgoers already
12939
12940
12941 eles = next;
12942 }
12943
12944 return this.spawn(sEles, true).filter(selector);
12945 };
12946 };
12947
12948 elesfn$t.clearTraversalCache = function () {
12949 for (var i = 0; i < this.length; i++) {
12950 this[i]._private.traversalCache = null;
12951 }
12952 };
12953
12954 extend(elesfn$t, {
12955 // get the root nodes in the DAG
12956 roots: defineDagExtremity({
12957 noIncomingEdges: true
12958 }),
12959 // get the leaf nodes in the DAG
12960 leaves: defineDagExtremity({
12961 noOutgoingEdges: true
12962 }),
12963 // normally called children in graph theory
12964 // these nodes =edges=> outgoing nodes
12965 outgoers: cache(defineDagOneHop({
12966 outgoing: true
12967 }), 'outgoers'),
12968 // aka DAG descendants
12969 successors: defineDagAllHops({
12970 outgoing: true
12971 }),
12972 // normally called parents in graph theory
12973 // these nodes <=edges= incoming nodes
12974 incomers: cache(defineDagOneHop({
12975 incoming: true
12976 }), 'incomers'),
12977 // aka DAG ancestors
12978 predecessors: defineDagAllHops({
12979 incoming: true
12980 })
12981 }); // Neighbourhood functions
12982 //////////////////////////
12983
12984 extend(elesfn$t, {
12985 neighborhood: cache(function (selector) {
12986 var elements = [];
12987 var nodes = this.nodes();
12988
12989 for (var i = 0; i < nodes.length; i++) {
12990 // for all nodes
12991 var node = nodes[i];
12992 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12993
12994 for (var j = 0; j < connectedEdges.length; j++) {
12995 var edge = connectedEdges[j];
12996 var src = edge.source();
12997 var tgt = edge.target();
12998 var otherNode = node === src ? tgt : src; // need check in case of loop
12999
13000 if (otherNode.length > 0) {
13001 elements.push(otherNode[0]); // add node 1 hop away
13002 } // add connected edge
13003
13004
13005 elements.push(edge[0]);
13006 }
13007 }
13008
13009 return this.spawn(elements, true).filter(selector);
13010 }, 'neighborhood'),
13011 closedNeighborhood: function closedNeighborhood(selector) {
13012 return this.neighborhood().add(this).filter(selector);
13013 },
13014 openNeighborhood: function openNeighborhood(selector) {
13015 return this.neighborhood(selector);
13016 }
13017 }); // aliases
13018
13019 elesfn$t.neighbourhood = elesfn$t.neighborhood;
13020 elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
13021 elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
13022 /////////////////
13023
13024 extend(elesfn$t, {
13025 source: cache(function sourceImpl(selector) {
13026 var ele = this[0];
13027 var src;
13028
13029 if (ele) {
13030 src = ele._private.source || ele.cy().collection();
13031 }
13032
13033 return src && selector ? src.filter(selector) : src;
13034 }, 'source'),
13035 target: cache(function targetImpl(selector) {
13036 var ele = this[0];
13037 var tgt;
13038
13039 if (ele) {
13040 tgt = ele._private.target || ele.cy().collection();
13041 }
13042
13043 return tgt && selector ? tgt.filter(selector) : tgt;
13044 }, 'target'),
13045 sources: defineSourceFunction({
13046 attr: 'source'
13047 }),
13048 targets: defineSourceFunction({
13049 attr: 'target'
13050 })
13051 });
13052
13053 function defineSourceFunction(params) {
13054 return function sourceImpl(selector) {
13055 var sources = [];
13056
13057 for (var i = 0; i < this.length; i++) {
13058 var ele = this[i];
13059 var src = ele._private[params.attr];
13060
13061 if (src) {
13062 sources.push(src);
13063 }
13064 }
13065
13066 return this.spawn(sources, true).filter(selector);
13067 };
13068 }
13069
13070 extend(elesfn$t, {
13071 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
13072 edgesTo: cache(defineEdgesWithFunction({
13073 thisIsSrc: true
13074 }), 'edgesTo')
13075 });
13076
13077 function defineEdgesWithFunction(params) {
13078 return function edgesWithImpl(otherNodes) {
13079 var elements = [];
13080 var cy = this._private.cy;
13081 var p = params || {}; // get elements if a selector is specified
13082
13083 if (string(otherNodes)) {
13084 otherNodes = cy.$(otherNodes);
13085 }
13086
13087 for (var h = 0; h < otherNodes.length; h++) {
13088 var edges = otherNodes[h]._private.edges;
13089
13090 for (var i = 0; i < edges.length; i++) {
13091 var edge = edges[i];
13092 var edgeData = edge._private.data;
13093 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
13094 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
13095 var edgeConnectsThisAndOther = thisToOther || otherToThis;
13096
13097 if (!edgeConnectsThisAndOther) {
13098 continue;
13099 }
13100
13101 if (p.thisIsSrc || p.thisIsTgt) {
13102 if (p.thisIsSrc && !thisToOther) {
13103 continue;
13104 }
13105
13106 if (p.thisIsTgt && !otherToThis) {
13107 continue;
13108 }
13109 }
13110
13111 elements.push(edge);
13112 }
13113 }
13114
13115 return this.spawn(elements, true);
13116 };
13117 }
13118
13119 extend(elesfn$t, {
13120 connectedEdges: cache(function (selector) {
13121 var retEles = [];
13122 var eles = this;
13123
13124 for (var i = 0; i < eles.length; i++) {
13125 var node = eles[i];
13126
13127 if (!node.isNode()) {
13128 continue;
13129 }
13130
13131 var edges = node._private.edges;
13132
13133 for (var j = 0; j < edges.length; j++) {
13134 var edge = edges[j];
13135 retEles.push(edge);
13136 }
13137 }
13138
13139 return this.spawn(retEles, true).filter(selector);
13140 }, 'connectedEdges'),
13141 connectedNodes: cache(function (selector) {
13142 var retEles = [];
13143 var eles = this;
13144
13145 for (var i = 0; i < eles.length; i++) {
13146 var edge = eles[i];
13147
13148 if (!edge.isEdge()) {
13149 continue;
13150 }
13151
13152 retEles.push(edge.source()[0]);
13153 retEles.push(edge.target()[0]);
13154 }
13155
13156 return this.spawn(retEles, true).filter(selector);
13157 }, 'connectedNodes'),
13158 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
13159 codirectedEdges: cache(defineParallelEdgesFunction({
13160 codirected: true
13161 }), 'codirectedEdges')
13162 });
13163
13164 function defineParallelEdgesFunction(params) {
13165 var defaults = {
13166 codirected: false
13167 };
13168 params = extend({}, defaults, params);
13169 return function parallelEdgesImpl(selector) {
13170 // micro-optimised for renderer
13171 var elements = [];
13172 var edges = this.edges();
13173 var p = params; // look at all the edges in the collection
13174
13175 for (var i = 0; i < edges.length; i++) {
13176 var edge1 = edges[i];
13177 var edge1_p = edge1._private;
13178 var src1 = edge1_p.source;
13179 var srcid1 = src1._private.data.id;
13180 var tgtid1 = edge1_p.data.target;
13181 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
13182
13183 for (var j = 0; j < srcEdges1.length; j++) {
13184 var edge2 = srcEdges1[j];
13185 var edge2data = edge2._private.data;
13186 var tgtid2 = edge2data.target;
13187 var srcid2 = edge2data.source;
13188 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
13189 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
13190
13191 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
13192 elements.push(edge2);
13193 }
13194 }
13195 }
13196
13197 return this.spawn(elements, true).filter(selector);
13198 };
13199 } // Misc functions
13200 /////////////////
13201
13202
13203 extend(elesfn$t, {
13204 components: function components(root) {
13205 var self = this;
13206 var cy = self.cy();
13207 var visited = cy.collection();
13208 var unvisited = root == null ? self.nodes() : root.nodes();
13209 var components = [];
13210
13211 if (root != null && unvisited.empty()) {
13212 // root may contain only edges
13213 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
13214 }
13215
13216 var visitInComponent = function visitInComponent(node, component) {
13217 visited.merge(node);
13218 unvisited.unmerge(node);
13219 component.merge(node);
13220 };
13221
13222 if (unvisited.empty()) {
13223 return self.spawn();
13224 }
13225
13226 var _loop = function _loop() {
13227 // each iteration yields a component
13228 var cmpt = cy.collection();
13229 components.push(cmpt);
13230 var root = unvisited[0];
13231 visitInComponent(root, cmpt);
13232 self.bfs({
13233 directed: false,
13234 roots: root,
13235 visit: function visit(v) {
13236 return visitInComponent(v, cmpt);
13237 }
13238 });
13239 cmpt.forEach(function (node) {
13240 node.connectedEdges().forEach(function (e) {
13241 // connectedEdges() usually cached
13242 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
13243 // has() is cheap
13244 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
13245 }
13246 });
13247 });
13248 };
13249
13250 do {
13251 _loop();
13252 } while (unvisited.length > 0);
13253
13254 return components;
13255 },
13256 component: function component() {
13257 var ele = this[0];
13258 return ele.cy().mutableElements().components(ele)[0];
13259 }
13260 });
13261 elesfn$t.componentsOf = elesfn$t.components;
13262
13263 var Collection = function Collection(cy, elements) {
13264 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13265
13266 if (cy === undefined) {
13267 error('A collection must have a reference to the core');
13268 return;
13269 }
13270
13271 var map = new Map$1();
13272 var createdElements = false;
13273
13274 if (!elements) {
13275 elements = [];
13276 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
13277 createdElements = true; // make elements from json and restore all at once later
13278
13279 var eles = [];
13280 var elesIds = new Set$1();
13281
13282 for (var i = 0, l = elements.length; i < l; i++) {
13283 var json = elements[i];
13284
13285 if (json.data == null) {
13286 json.data = {};
13287 }
13288
13289 var _data = json.data; // make sure newly created elements have valid ids
13290
13291 if (_data.id == null) {
13292 _data.id = uuid();
13293 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
13294 continue; // can't create element if prior id already exists
13295 }
13296
13297 var ele = new Element(cy, json, false);
13298 eles.push(ele);
13299 elesIds.add(_data.id);
13300 }
13301
13302 elements = eles;
13303 }
13304
13305 this.length = 0;
13306
13307 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
13308 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
13309
13310 if (element$1 == null) {
13311 continue;
13312 }
13313
13314 var id = element$1._private.data.id;
13315
13316 if (!unique || !map.has(id)) {
13317 if (unique) {
13318 map.set(id, {
13319 index: this.length,
13320 ele: element$1
13321 });
13322 }
13323
13324 this[this.length] = element$1;
13325 this.length++;
13326 }
13327 }
13328
13329 this._private = {
13330 eles: this,
13331 cy: cy,
13332
13333 get map() {
13334 if (this.lazyMap == null) {
13335 this.rebuildMap();
13336 }
13337
13338 return this.lazyMap;
13339 },
13340
13341 set map(m) {
13342 this.lazyMap = m;
13343 },
13344
13345 rebuildMap: function rebuildMap() {
13346 var m = this.lazyMap = new Map$1();
13347 var eles = this.eles;
13348
13349 for (var _i2 = 0; _i2 < eles.length; _i2++) {
13350 var _ele = eles[_i2];
13351 m.set(_ele.id(), {
13352 index: _i2,
13353 ele: _ele
13354 });
13355 }
13356 }
13357 };
13358
13359 if (unique) {
13360 this._private.map = map;
13361 } // restore the elements if we created them from json
13362
13363
13364 if (createdElements) {
13365 this.restore();
13366 }
13367 }; // Functions
13368 ////////////////////////////////////////////////////////////////////////////////////////////////////
13369 // keep the prototypes in sync (an element has the same functions as a collection)
13370 // and use elefn and elesfn as shorthands to the prototypes
13371
13372
13373 var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
13374
13375 elesfn$u.instanceString = function () {
13376 return 'collection';
13377 };
13378
13379 elesfn$u.spawn = function (eles, unique) {
13380 return new Collection(this.cy(), eles, unique);
13381 };
13382
13383 elesfn$u.spawnSelf = function () {
13384 return this.spawn(this);
13385 };
13386
13387 elesfn$u.cy = function () {
13388 return this._private.cy;
13389 };
13390
13391 elesfn$u.renderer = function () {
13392 return this._private.cy.renderer();
13393 };
13394
13395 elesfn$u.element = function () {
13396 return this[0];
13397 };
13398
13399 elesfn$u.collection = function () {
13400 if (collection(this)) {
13401 return this;
13402 } else {
13403 // an element
13404 return new Collection(this._private.cy, [this]);
13405 }
13406 };
13407
13408 elesfn$u.unique = function () {
13409 return new Collection(this._private.cy, this, true);
13410 };
13411
13412 elesfn$u.hasElementWithId = function (id) {
13413 id = '' + id; // id must be string
13414
13415 return this._private.map.has(id);
13416 };
13417
13418 elesfn$u.getElementById = function (id) {
13419 id = '' + id; // id must be string
13420
13421 var cy = this._private.cy;
13422
13423 var entry = this._private.map.get(id);
13424
13425 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
13426 };
13427
13428 elesfn$u.$id = elesfn$u.getElementById;
13429
13430 elesfn$u.poolIndex = function () {
13431 var cy = this._private.cy;
13432 var eles = cy._private.elements;
13433 var id = this[0]._private.data.id;
13434 return eles._private.map.get(id).index;
13435 };
13436
13437 elesfn$u.indexOf = function (ele) {
13438 var id = ele[0]._private.data.id;
13439 return this._private.map.get(id).index;
13440 };
13441
13442 elesfn$u.indexOfId = function (id) {
13443 id = '' + id; // id must be string
13444
13445 return this._private.map.get(id).index;
13446 };
13447
13448 elesfn$u.json = function (obj) {
13449 var ele = this.element();
13450 var cy = this.cy();
13451
13452 if (ele == null && obj) {
13453 return this;
13454 } // can't set to no eles
13455
13456
13457 if (ele == null) {
13458 return undefined;
13459 } // can't get from no eles
13460
13461
13462 var p = ele._private;
13463
13464 if (plainObject(obj)) {
13465 // set
13466 cy.startBatch();
13467
13468 if (obj.data) {
13469 ele.data(obj.data);
13470 var _data2 = p.data;
13471
13472 if (ele.isEdge()) {
13473 // source and target are immutable via data()
13474 var move = false;
13475 var spec = {};
13476 var src = obj.data.source;
13477 var tgt = obj.data.target;
13478
13479 if (src != null && src != _data2.source) {
13480 spec.source = '' + src; // id must be string
13481
13482 move = true;
13483 }
13484
13485 if (tgt != null && tgt != _data2.target) {
13486 spec.target = '' + tgt; // id must be string
13487
13488 move = true;
13489 }
13490
13491 if (move) {
13492 ele = ele.move(spec);
13493 }
13494 } else {
13495 // parent is immutable via data()
13496 var newParentValSpecd = 'parent' in obj.data;
13497 var parent = obj.data.parent;
13498
13499 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
13500 if (parent === undefined) {
13501 // can't set undefined imperatively, so use null
13502 parent = null;
13503 }
13504
13505 if (parent != null) {
13506 parent = '' + parent; // id must be string
13507 }
13508
13509 ele = ele.move({
13510 parent: parent
13511 });
13512 }
13513 }
13514 }
13515
13516 if (obj.position) {
13517 ele.position(obj.position);
13518 } // ignore group -- immutable
13519
13520
13521 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
13522 var obj_k = obj[k];
13523
13524 if (obj_k != null && obj_k !== p[k]) {
13525 if (obj_k) {
13526 ele[trueFnName]();
13527 } else {
13528 ele[falseFnName]();
13529 }
13530 }
13531 };
13532
13533 checkSwitch('removed', 'remove', 'restore');
13534 checkSwitch('selected', 'select', 'unselect');
13535 checkSwitch('selectable', 'selectify', 'unselectify');
13536 checkSwitch('locked', 'lock', 'unlock');
13537 checkSwitch('grabbable', 'grabify', 'ungrabify');
13538 checkSwitch('pannable', 'panify', 'unpanify');
13539
13540 if (obj.classes != null) {
13541 ele.classes(obj.classes);
13542 }
13543
13544 cy.endBatch();
13545 return this;
13546 } else if (obj === undefined) {
13547 // get
13548 var json = {
13549 data: copy(p.data),
13550 position: copy(p.position),
13551 group: p.group,
13552 removed: p.removed,
13553 selected: p.selected,
13554 selectable: p.selectable,
13555 locked: p.locked,
13556 grabbable: p.grabbable,
13557 pannable: p.pannable,
13558 classes: null
13559 };
13560 json.classes = '';
13561 var i = 0;
13562 p.classes.forEach(function (cls) {
13563 return json.classes += i++ === 0 ? cls : ' ' + cls;
13564 });
13565 return json;
13566 }
13567 };
13568
13569 elesfn$u.jsons = function () {
13570 var jsons = [];
13571
13572 for (var i = 0; i < this.length; i++) {
13573 var ele = this[i];
13574 var json = ele.json();
13575 jsons.push(json);
13576 }
13577
13578 return jsons;
13579 };
13580
13581 elesfn$u.clone = function () {
13582 var cy = this.cy();
13583 var elesArr = [];
13584
13585 for (var i = 0; i < this.length; i++) {
13586 var ele = this[i];
13587 var json = ele.json();
13588 var clone = new Element(cy, json, false); // NB no restore
13589
13590 elesArr.push(clone);
13591 }
13592
13593 return new Collection(cy, elesArr);
13594 };
13595
13596 elesfn$u.copy = elesfn$u.clone;
13597
13598 elesfn$u.restore = function () {
13599 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13600 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13601 var self = this;
13602 var cy = self.cy();
13603 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
13604 // restore the nodes first
13605
13606 var nodes = [];
13607 var edges = [];
13608 var elements;
13609
13610 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
13611 var ele = self[_i3];
13612
13613 if (addToPool && !ele.removed()) {
13614 // don't need to handle this ele
13615 continue;
13616 } // keep nodes first in the array and edges after
13617
13618
13619 if (ele.isNode()) {
13620 // put to front of array if node
13621 nodes.push(ele);
13622 } else {
13623 // put to end of array if edge
13624 edges.push(ele);
13625 }
13626 }
13627
13628 elements = nodes.concat(edges);
13629 var i;
13630
13631 var removeFromElements = function removeFromElements() {
13632 elements.splice(i, 1);
13633 i--;
13634 }; // now, restore each element
13635
13636
13637 for (i = 0; i < elements.length; i++) {
13638 var _ele2 = elements[i];
13639 var _private = _ele2._private;
13640 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
13641
13642 _ele2.clearTraversalCache(); // set id and validate
13643
13644
13645 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
13646 _data3.id = uuid();
13647 } else if (number(_data3.id)) {
13648 _data3.id = '' + _data3.id; // now it's a string
13649 } else if (emptyString(_data3.id) || !string(_data3.id)) {
13650 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
13651
13652 removeFromElements();
13653 continue;
13654 } else if (cy.hasElementWithId(_data3.id)) {
13655 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
13656
13657 removeFromElements();
13658 continue;
13659 }
13660
13661 var id = _data3.id; // id is finalised, now let's keep a ref
13662
13663 if (_ele2.isNode()) {
13664 // extra checks for nodes
13665 var pos = _private.position; // make sure the nodes have a defined position
13666
13667 if (pos.x == null) {
13668 pos.x = 0;
13669 }
13670
13671 if (pos.y == null) {
13672 pos.y = 0;
13673 }
13674 }
13675
13676 if (_ele2.isEdge()) {
13677 // extra checks for edges
13678 var edge = _ele2;
13679 var fields = ['source', 'target'];
13680 var fieldsLength = fields.length;
13681 var badSourceOrTarget = false;
13682
13683 for (var j = 0; j < fieldsLength; j++) {
13684 var field = fields[j];
13685 var val = _data3[field];
13686
13687 if (number(val)) {
13688 val = _data3[field] = '' + _data3[field]; // now string
13689 }
13690
13691 if (val == null || val === '') {
13692 // can't create if source or target is not defined properly
13693 error('Can not create edge `' + id + '` with unspecified ' + field);
13694 badSourceOrTarget = true;
13695 } else if (!cy.hasElementWithId(val)) {
13696 // can't create edge if one of its nodes doesn't exist
13697 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13698 badSourceOrTarget = true;
13699 }
13700 }
13701
13702 if (badSourceOrTarget) {
13703 removeFromElements();
13704 continue;
13705 } // can't create this
13706
13707
13708 var src = cy.getElementById(_data3.source);
13709 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13710
13711 if (src.same(tgt)) {
13712 src._private.edges.push(edge);
13713 } else {
13714 src._private.edges.push(edge);
13715
13716 tgt._private.edges.push(edge);
13717 }
13718
13719 edge._private.source = src;
13720 edge._private.target = tgt;
13721 } // if is edge
13722 // create mock ids / indexes maps for element so it can be used like collections
13723
13724
13725 _private.map = new Map$1();
13726
13727 _private.map.set(id, {
13728 ele: _ele2,
13729 index: 0
13730 });
13731
13732 _private.removed = false;
13733
13734 if (addToPool) {
13735 cy.addToPool(_ele2);
13736 }
13737 } // for each element
13738 // do compound node sanity checks
13739
13740
13741 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13742 // each node
13743 var node = nodes[_i4];
13744 var _data4 = node._private.data;
13745
13746 if (number(_data4.parent)) {
13747 // then automake string
13748 _data4.parent = '' + _data4.parent;
13749 }
13750
13751 var parentId = _data4.parent;
13752 var specifiedParent = parentId != null;
13753
13754 if (specifiedParent) {
13755 var parent = cy.getElementById(parentId);
13756
13757 if (parent.empty()) {
13758 // non-existant parent; just remove it
13759 _data4.parent = undefined;
13760 } else {
13761 var selfAsParent = false;
13762 var ancestor = parent;
13763
13764 while (!ancestor.empty()) {
13765 if (node.same(ancestor)) {
13766 // mark self as parent and remove from data
13767 selfAsParent = true;
13768 _data4.parent = undefined; // remove parent reference
13769 // exit or we loop forever
13770
13771 break;
13772 }
13773
13774 ancestor = ancestor.parent();
13775 }
13776
13777 if (!selfAsParent) {
13778 // connect with children
13779 parent[0]._private.children.push(node);
13780
13781 node._private.parent = parent[0]; // let the core know we have a compound graph
13782
13783 cy_p.hasCompoundNodes = true;
13784 }
13785 } // else
13786
13787 } // if specified parent
13788
13789 } // for each node
13790
13791
13792 if (elements.length > 0) {
13793 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13794
13795 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13796 var _ele3 = restored[_i5];
13797
13798 if (_ele3.isNode()) {
13799 continue;
13800 } // adding an edge invalidates the traversal caches for the parallel edges
13801
13802
13803 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13804
13805
13806 _ele3.source().clearTraversalCache();
13807
13808 _ele3.target().clearTraversalCache();
13809 }
13810
13811 var toUpdateStyle;
13812
13813 if (cy_p.hasCompoundNodes) {
13814 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13815 } else {
13816 toUpdateStyle = restored;
13817 }
13818
13819 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13820
13821 if (notifyRenderer) {
13822 restored.emitAndNotify('add');
13823 } else if (addToPool) {
13824 restored.emit('add');
13825 }
13826 }
13827
13828 return self; // chainability
13829 };
13830
13831 elesfn$u.removed = function () {
13832 var ele = this[0];
13833 return ele && ele._private.removed;
13834 };
13835
13836 elesfn$u.inside = function () {
13837 var ele = this[0];
13838 return ele && !ele._private.removed;
13839 };
13840
13841 elesfn$u.remove = function () {
13842 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13843 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13844 var self = this;
13845 var elesToRemove = [];
13846 var elesToRemoveIds = {};
13847 var cy = self._private.cy; // add connected edges
13848
13849 function addConnectedEdges(node) {
13850 var edges = node._private.edges;
13851
13852 for (var i = 0; i < edges.length; i++) {
13853 add(edges[i]);
13854 }
13855 } // add descendant nodes
13856
13857
13858 function addChildren(node) {
13859 var children = node._private.children;
13860
13861 for (var i = 0; i < children.length; i++) {
13862 add(children[i]);
13863 }
13864 }
13865
13866 function add(ele) {
13867 var alreadyAdded = elesToRemoveIds[ele.id()];
13868
13869 if (removeFromPool && ele.removed() || alreadyAdded) {
13870 return;
13871 } else {
13872 elesToRemoveIds[ele.id()] = true;
13873 }
13874
13875 if (ele.isNode()) {
13876 elesToRemove.push(ele); // nodes are removed last
13877
13878 addConnectedEdges(ele);
13879 addChildren(ele);
13880 } else {
13881 elesToRemove.unshift(ele); // edges are removed first
13882 }
13883 } // make the list of elements to remove
13884 // (may be removing more than specified due to connected edges etc)
13885
13886
13887 for (var i = 0, l = self.length; i < l; i++) {
13888 var ele = self[i];
13889 add(ele);
13890 }
13891
13892 function removeEdgeRef(node, edge) {
13893 var connectedEdges = node._private.edges;
13894 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13895
13896 node.clearTraversalCache();
13897 }
13898
13899 function removeParallelRef(pllEdge) {
13900 // removing an edge invalidates the traversal caches for the parallel edges
13901 pllEdge.clearTraversalCache();
13902 }
13903
13904 var alteredParents = [];
13905 alteredParents.ids = {};
13906
13907 function removeChildRef(parent, ele) {
13908 ele = ele[0];
13909 parent = parent[0];
13910 var children = parent._private.children;
13911 var pid = parent.id();
13912 removeFromArray(children, ele); // remove parent => child ref
13913
13914 ele._private.parent = null; // remove child => parent ref
13915
13916 if (!alteredParents.ids[pid]) {
13917 alteredParents.ids[pid] = true;
13918 alteredParents.push(parent);
13919 }
13920 }
13921
13922 self.dirtyCompoundBoundsCache();
13923
13924 if (removeFromPool) {
13925 cy.removeFromPool(elesToRemove); // remove from core pool
13926 }
13927
13928 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13929 var _ele4 = elesToRemove[_i6];
13930
13931 if (_ele4.isEdge()) {
13932 // remove references to this edge in its connected nodes
13933 var src = _ele4.source()[0];
13934
13935 var tgt = _ele4.target()[0];
13936
13937 removeEdgeRef(src, _ele4);
13938 removeEdgeRef(tgt, _ele4);
13939
13940 var pllEdges = _ele4.parallelEdges();
13941
13942 for (var j = 0; j < pllEdges.length; j++) {
13943 var pllEdge = pllEdges[j];
13944 removeParallelRef(pllEdge);
13945
13946 if (pllEdge.isBundledBezier()) {
13947 pllEdge.dirtyBoundingBoxCache();
13948 }
13949 }
13950 } else {
13951 // remove reference to parent
13952 var parent = _ele4.parent();
13953
13954 if (parent.length !== 0) {
13955 removeChildRef(parent, _ele4);
13956 }
13957 }
13958
13959 if (removeFromPool) {
13960 // mark as removed
13961 _ele4._private.removed = true;
13962 }
13963 } // check to see if we have a compound graph or not
13964
13965
13966 var elesStillInside = cy._private.elements;
13967 cy._private.hasCompoundNodes = false;
13968
13969 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13970 var _ele5 = elesStillInside[_i7];
13971
13972 if (_ele5.isParent()) {
13973 cy._private.hasCompoundNodes = true;
13974 break;
13975 }
13976 }
13977
13978 var removedElements = new Collection(this.cy(), elesToRemove);
13979
13980 if (removedElements.size() > 0) {
13981 // must manually notify since trigger won't do this automatically once removed
13982 if (notifyRenderer) {
13983 removedElements.emitAndNotify('remove');
13984 } else if (removeFromPool) {
13985 removedElements.emit('remove');
13986 }
13987 } // the parents who were modified by the removal need their style updated
13988
13989
13990 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13991 var _ele6 = alteredParents[_i8];
13992
13993 if (!removeFromPool || !_ele6.removed()) {
13994 _ele6.updateStyle();
13995 }
13996 }
13997
13998 return removedElements;
13999 };
14000
14001 elesfn$u.move = function (struct) {
14002 var cy = this._private.cy;
14003 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
14004 // (our calls to remove/restore do not remove from the graph or make events)
14005
14006 var notifyRenderer = false;
14007 var modifyPool = false;
14008
14009 var toString = function toString(id) {
14010 return id == null ? id : '' + id;
14011 }; // id must be string
14012
14013
14014 if (struct.source !== undefined || struct.target !== undefined) {
14015 var srcId = toString(struct.source);
14016 var tgtId = toString(struct.target);
14017 var srcExists = srcId != null && cy.hasElementWithId(srcId);
14018 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
14019
14020 if (srcExists || tgtExists) {
14021 cy.batch(function () {
14022 // avoid duplicate style updates
14023 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
14024
14025 eles.emitAndNotify('moveout');
14026
14027 for (var i = 0; i < eles.length; i++) {
14028 var ele = eles[i];
14029 var _data5 = ele._private.data;
14030
14031 if (ele.isEdge()) {
14032 if (srcExists) {
14033 _data5.source = srcId;
14034 }
14035
14036 if (tgtExists) {
14037 _data5.target = tgtId;
14038 }
14039 }
14040 }
14041
14042 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
14043 });
14044 eles.emitAndNotify('move');
14045 }
14046 } else if (struct.parent !== undefined) {
14047 // move node to new parent
14048 var parentId = toString(struct.parent);
14049 var parentExists = parentId === null || cy.hasElementWithId(parentId);
14050
14051 if (parentExists) {
14052 var pidToAssign = parentId === null ? undefined : parentId;
14053 cy.batch(function () {
14054 // avoid duplicate style updates
14055 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
14056
14057 updated.emitAndNotify('moveout');
14058
14059 for (var i = 0; i < eles.length; i++) {
14060 var ele = eles[i];
14061 var _data6 = ele._private.data;
14062
14063 if (ele.isNode()) {
14064 _data6.parent = pidToAssign;
14065 }
14066 }
14067
14068 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
14069 });
14070 eles.emitAndNotify('move');
14071 }
14072 }
14073
14074 return this;
14075 };
14076
14077 [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) {
14078 extend(elesfn$u, props);
14079 });
14080
14081 var corefn = {
14082 add: function add(opts) {
14083 var elements;
14084 var cy = this; // add the elements
14085
14086 if (elementOrCollection(opts)) {
14087 var eles = opts;
14088
14089 if (eles._private.cy === cy) {
14090 // same instance => just restore
14091 elements = eles.restore();
14092 } else {
14093 // otherwise, copy from json
14094 var jsons = [];
14095
14096 for (var i = 0; i < eles.length; i++) {
14097 var ele = eles[i];
14098 jsons.push(ele.json());
14099 }
14100
14101 elements = new Collection(cy, jsons);
14102 }
14103 } // specify an array of options
14104 else if (array(opts)) {
14105 var _jsons = opts;
14106 elements = new Collection(cy, _jsons);
14107 } // specify via opts.nodes and opts.edges
14108 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
14109 var elesByGroup = opts;
14110 var _jsons2 = [];
14111 var grs = ['nodes', 'edges'];
14112
14113 for (var _i = 0, il = grs.length; _i < il; _i++) {
14114 var group = grs[_i];
14115 var elesArray = elesByGroup[group];
14116
14117 if (array(elesArray)) {
14118 for (var j = 0, jl = elesArray.length; j < jl; j++) {
14119 var json = extend({
14120 group: group
14121 }, elesArray[j]);
14122
14123 _jsons2.push(json);
14124 }
14125 }
14126 }
14127
14128 elements = new Collection(cy, _jsons2);
14129 } // specify options for one element
14130 else {
14131 var _json = opts;
14132 elements = new Element(cy, _json).collection();
14133 }
14134
14135 return elements;
14136 },
14137 remove: function remove(collection) {
14138 if (elementOrCollection(collection)) ; else if (string(collection)) {
14139 var selector = collection;
14140 collection = this.$(selector);
14141 }
14142
14143 return collection.remove();
14144 }
14145 };
14146
14147 /* global Float32Array */
14148
14149 /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14150 function generateCubicBezier(mX1, mY1, mX2, mY2) {
14151 var NEWTON_ITERATIONS = 4,
14152 NEWTON_MIN_SLOPE = 0.001,
14153 SUBDIVISION_PRECISION = 0.0000001,
14154 SUBDIVISION_MAX_ITERATIONS = 10,
14155 kSplineTableSize = 11,
14156 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
14157 float32ArraySupported = typeof Float32Array !== 'undefined';
14158 /* Must contain four arguments. */
14159
14160 if (arguments.length !== 4) {
14161 return false;
14162 }
14163 /* Arguments must be numbers. */
14164
14165
14166 for (var i = 0; i < 4; ++i) {
14167 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
14168 return false;
14169 }
14170 }
14171 /* X values must be in the [0, 1] range. */
14172
14173
14174 mX1 = Math.min(mX1, 1);
14175 mX2 = Math.min(mX2, 1);
14176 mX1 = Math.max(mX1, 0);
14177 mX2 = Math.max(mX2, 0);
14178 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
14179
14180 function A(aA1, aA2) {
14181 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
14182 }
14183
14184 function B(aA1, aA2) {
14185 return 3.0 * aA2 - 6.0 * aA1;
14186 }
14187
14188 function C(aA1) {
14189 return 3.0 * aA1;
14190 }
14191
14192 function calcBezier(aT, aA1, aA2) {
14193 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
14194 }
14195
14196 function getSlope(aT, aA1, aA2) {
14197 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
14198 }
14199
14200 function newtonRaphsonIterate(aX, aGuessT) {
14201 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
14202 var currentSlope = getSlope(aGuessT, mX1, mX2);
14203
14204 if (currentSlope === 0.0) {
14205 return aGuessT;
14206 }
14207
14208 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
14209 aGuessT -= currentX / currentSlope;
14210 }
14211
14212 return aGuessT;
14213 }
14214
14215 function calcSampleValues() {
14216 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
14217 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
14218 }
14219 }
14220
14221 function binarySubdivide(aX, aA, aB) {
14222 var currentX,
14223 currentT,
14224 i = 0;
14225
14226 do {
14227 currentT = aA + (aB - aA) / 2.0;
14228 currentX = calcBezier(currentT, mX1, mX2) - aX;
14229
14230 if (currentX > 0.0) {
14231 aB = currentT;
14232 } else {
14233 aA = currentT;
14234 }
14235 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
14236
14237 return currentT;
14238 }
14239
14240 function getTForX(aX) {
14241 var intervalStart = 0.0,
14242 currentSample = 1,
14243 lastSample = kSplineTableSize - 1;
14244
14245 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
14246 intervalStart += kSampleStepSize;
14247 }
14248
14249 --currentSample;
14250 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
14251 guessForT = intervalStart + dist * kSampleStepSize,
14252 initialSlope = getSlope(guessForT, mX1, mX2);
14253
14254 if (initialSlope >= NEWTON_MIN_SLOPE) {
14255 return newtonRaphsonIterate(aX, guessForT);
14256 } else if (initialSlope === 0.0) {
14257 return guessForT;
14258 } else {
14259 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
14260 }
14261 }
14262
14263 var _precomputed = false;
14264
14265 function precompute() {
14266 _precomputed = true;
14267
14268 if (mX1 !== mY1 || mX2 !== mY2) {
14269 calcSampleValues();
14270 }
14271 }
14272
14273 var f = function f(aX) {
14274 if (!_precomputed) {
14275 precompute();
14276 }
14277
14278 if (mX1 === mY1 && mX2 === mY2) {
14279 return aX;
14280 }
14281
14282 if (aX === 0) {
14283 return 0;
14284 }
14285
14286 if (aX === 1) {
14287 return 1;
14288 }
14289
14290 return calcBezier(getTForX(aX), mY1, mY2);
14291 };
14292
14293 f.getControlPoints = function () {
14294 return [{
14295 x: mX1,
14296 y: mY1
14297 }, {
14298 x: mX2,
14299 y: mY2
14300 }];
14301 };
14302
14303 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
14304
14305 f.toString = function () {
14306 return str;
14307 };
14308
14309 return f;
14310 }
14311
14312 /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14313
14314 /* 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
14315 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
14316 var generateSpringRK4 = function () {
14317 function springAccelerationForState(state) {
14318 return -state.tension * state.x - state.friction * state.v;
14319 }
14320
14321 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
14322 var state = {
14323 x: initialState.x + derivative.dx * dt,
14324 v: initialState.v + derivative.dv * dt,
14325 tension: initialState.tension,
14326 friction: initialState.friction
14327 };
14328 return {
14329 dx: state.v,
14330 dv: springAccelerationForState(state)
14331 };
14332 }
14333
14334 function springIntegrateState(state, dt) {
14335 var a = {
14336 dx: state.v,
14337 dv: springAccelerationForState(state)
14338 },
14339 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
14340 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
14341 d = springEvaluateStateWithDerivative(state, dt, c),
14342 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
14343 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
14344 state.x = state.x + dxdt * dt;
14345 state.v = state.v + dvdt * dt;
14346 return state;
14347 }
14348
14349 return function springRK4Factory(tension, friction, duration) {
14350 var initState = {
14351 x: -1,
14352 v: 0,
14353 tension: null,
14354 friction: null
14355 },
14356 path = [0],
14357 time_lapsed = 0,
14358 tolerance = 1 / 10000,
14359 DT = 16 / 1000,
14360 have_duration,
14361 dt,
14362 last_state;
14363 tension = parseFloat(tension) || 500;
14364 friction = parseFloat(friction) || 20;
14365 duration = duration || null;
14366 initState.tension = tension;
14367 initState.friction = friction;
14368 have_duration = duration !== null;
14369 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
14370
14371 if (have_duration) {
14372 /* Run the simulation without a duration. */
14373 time_lapsed = springRK4Factory(tension, friction);
14374 /* Compute the adjusted time delta. */
14375
14376 dt = time_lapsed / duration * DT;
14377 } else {
14378 dt = DT;
14379 }
14380
14381 for (;;) {
14382 /* Next/step function .*/
14383 last_state = springIntegrateState(last_state || initState, dt);
14384 /* Store the position. */
14385
14386 path.push(1 + last_state.x);
14387 time_lapsed += 16;
14388 /* If the change threshold is reached, break. */
14389
14390 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
14391 break;
14392 }
14393 }
14394 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
14395 computed path and returns a snapshot of the position according to a given percentComplete. */
14396
14397
14398 return !have_duration ? time_lapsed : function (percentComplete) {
14399 return path[percentComplete * (path.length - 1) | 0];
14400 };
14401 };
14402 }();
14403
14404 var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
14405 var bezier = generateCubicBezier(t1, p1, t2, p2);
14406 return function (start, end, percent) {
14407 return start + (end - start) * bezier(percent);
14408 };
14409 };
14410
14411 var easings = {
14412 'linear': function linear(start, end, percent) {
14413 return start + (end - start) * percent;
14414 },
14415 // default easings
14416 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
14417 'ease-in': cubicBezier(0.42, 0, 1, 1),
14418 'ease-out': cubicBezier(0, 0, 0.58, 1),
14419 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
14420 // sine
14421 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
14422 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
14423 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
14424 // quad
14425 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
14426 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
14427 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
14428 // cubic
14429 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
14430 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
14431 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
14432 // quart
14433 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
14434 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
14435 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
14436 // quint
14437 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
14438 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
14439 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
14440 // expo
14441 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
14442 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
14443 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
14444 // circ
14445 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
14446 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
14447 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
14448 // user param easings...
14449 'spring': function spring(tension, friction, duration) {
14450 if (duration === 0) {
14451 // can't get a spring w/ duration 0
14452 return easings.linear; // duration 0 => jump to end so impl doesn't matter
14453 }
14454
14455 var spring = generateSpringRK4(tension, friction, duration);
14456 return function (start, end, percent) {
14457 return start + (end - start) * spring(percent);
14458 };
14459 },
14460 'cubic-bezier': cubicBezier
14461 };
14462
14463 function getEasedValue(type, start, end, percent, easingFn) {
14464 if (percent === 1) {
14465 return end;
14466 }
14467
14468 if (start === end) {
14469 return end;
14470 }
14471
14472 var val = easingFn(start, end, percent);
14473
14474 if (type == null) {
14475 return val;
14476 }
14477
14478 if (type.roundValue || type.color) {
14479 val = Math.round(val);
14480 }
14481
14482 if (type.min !== undefined) {
14483 val = Math.max(val, type.min);
14484 }
14485
14486 if (type.max !== undefined) {
14487 val = Math.min(val, type.max);
14488 }
14489
14490 return val;
14491 }
14492
14493 function getValue(prop, spec) {
14494 if (prop.pfValue != null || prop.value != null) {
14495 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
14496 return prop.pfValue;
14497 } else {
14498 return prop.value;
14499 }
14500 } else {
14501 return prop;
14502 }
14503 }
14504
14505 function ease(startProp, endProp, percent, easingFn, propSpec) {
14506 var type = propSpec != null ? propSpec.type : null;
14507
14508 if (percent < 0) {
14509 percent = 0;
14510 } else if (percent > 1) {
14511 percent = 1;
14512 }
14513
14514 var start = getValue(startProp, propSpec);
14515 var end = getValue(endProp, propSpec);
14516
14517 if (number(start) && number(end)) {
14518 return getEasedValue(type, start, end, percent, easingFn);
14519 } else if (array(start) && array(end)) {
14520 var easedArr = [];
14521
14522 for (var i = 0; i < end.length; i++) {
14523 var si = start[i];
14524 var ei = end[i];
14525
14526 if (si != null && ei != null) {
14527 var val = getEasedValue(type, si, ei, percent, easingFn);
14528 easedArr.push(val);
14529 } else {
14530 easedArr.push(ei);
14531 }
14532 }
14533
14534 return easedArr;
14535 }
14536
14537 return undefined;
14538 }
14539
14540 function step(self, ani, now, isCore) {
14541 var isEles = !isCore;
14542 var _p = self._private;
14543 var ani_p = ani._private;
14544 var pEasing = ani_p.easing;
14545 var startTime = ani_p.startTime;
14546 var cy = isCore ? self : self.cy();
14547 var style = cy.style();
14548
14549 if (!ani_p.easingImpl) {
14550 if (pEasing == null) {
14551 // use default
14552 ani_p.easingImpl = easings['linear'];
14553 } else {
14554 // then define w/ name
14555 var easingVals;
14556
14557 if (string(pEasing)) {
14558 var easingProp = style.parse('transition-timing-function', pEasing);
14559 easingVals = easingProp.value;
14560 } else {
14561 // then assume preparsed array
14562 easingVals = pEasing;
14563 }
14564
14565 var name, args;
14566
14567 if (string(easingVals)) {
14568 name = easingVals;
14569 args = [];
14570 } else {
14571 name = easingVals[1];
14572 args = easingVals.slice(2).map(function (n) {
14573 return +n;
14574 });
14575 }
14576
14577 if (args.length > 0) {
14578 // create with args
14579 if (name === 'spring') {
14580 args.push(ani_p.duration); // need duration to generate spring
14581 }
14582
14583 ani_p.easingImpl = easings[name].apply(null, args);
14584 } else {
14585 // static impl by name
14586 ani_p.easingImpl = easings[name];
14587 }
14588 }
14589 }
14590
14591 var easing = ani_p.easingImpl;
14592 var percent;
14593
14594 if (ani_p.duration === 0) {
14595 percent = 1;
14596 } else {
14597 percent = (now - startTime) / ani_p.duration;
14598 }
14599
14600 if (ani_p.applying) {
14601 percent = ani_p.progress;
14602 }
14603
14604 if (percent < 0) {
14605 percent = 0;
14606 } else if (percent > 1) {
14607 percent = 1;
14608 }
14609
14610 if (ani_p.delay == null) {
14611 // then update
14612 var startPos = ani_p.startPosition;
14613 var endPos = ani_p.position;
14614
14615 if (endPos && isEles && !self.locked()) {
14616 var newPos = {};
14617
14618 if (valid(startPos.x, endPos.x)) {
14619 newPos.x = ease(startPos.x, endPos.x, percent, easing);
14620 }
14621
14622 if (valid(startPos.y, endPos.y)) {
14623 newPos.y = ease(startPos.y, endPos.y, percent, easing);
14624 }
14625
14626 self.position(newPos);
14627 }
14628
14629 var startPan = ani_p.startPan;
14630 var endPan = ani_p.pan;
14631 var pan = _p.pan;
14632 var animatingPan = endPan != null && isCore;
14633
14634 if (animatingPan) {
14635 if (valid(startPan.x, endPan.x)) {
14636 pan.x = ease(startPan.x, endPan.x, percent, easing);
14637 }
14638
14639 if (valid(startPan.y, endPan.y)) {
14640 pan.y = ease(startPan.y, endPan.y, percent, easing);
14641 }
14642
14643 self.emit('pan');
14644 }
14645
14646 var startZoom = ani_p.startZoom;
14647 var endZoom = ani_p.zoom;
14648 var animatingZoom = endZoom != null && isCore;
14649
14650 if (animatingZoom) {
14651 if (valid(startZoom, endZoom)) {
14652 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
14653 }
14654
14655 self.emit('zoom');
14656 }
14657
14658 if (animatingPan || animatingZoom) {
14659 self.emit('viewport');
14660 }
14661
14662 var props = ani_p.style;
14663
14664 if (props && props.length > 0 && isEles) {
14665 for (var i = 0; i < props.length; i++) {
14666 var prop = props[i];
14667 var _name = prop.name;
14668 var end = prop;
14669 var start = ani_p.startStyle[_name];
14670 var propSpec = style.properties[start.name];
14671 var easedVal = ease(start, end, percent, easing, propSpec);
14672 style.overrideBypass(self, _name, easedVal);
14673 } // for props
14674
14675
14676 self.emit('style');
14677 } // if
14678
14679 }
14680
14681 ani_p.progress = percent;
14682 return percent;
14683 }
14684
14685 function valid(start, end) {
14686 if (start == null || end == null) {
14687 return false;
14688 }
14689
14690 if (number(start) && number(end)) {
14691 return true;
14692 } else if (start && end) {
14693 return true;
14694 }
14695
14696 return false;
14697 }
14698
14699 function startAnimation(self, ani, now, isCore) {
14700 var ani_p = ani._private;
14701 ani_p.started = true;
14702 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14703 }
14704
14705 function stepAll(now, cy) {
14706 var eles = cy._private.aniEles;
14707 var doneEles = [];
14708
14709 function stepOne(ele, isCore) {
14710 var _p = ele._private;
14711 var current = _p.animation.current;
14712 var queue = _p.animation.queue;
14713 var ranAnis = false; // if nothing currently animating, get something from the queue
14714
14715 if (current.length === 0) {
14716 var next = queue.shift();
14717
14718 if (next) {
14719 current.push(next);
14720 }
14721 }
14722
14723 var callbacks = function callbacks(_callbacks) {
14724 for (var j = _callbacks.length - 1; j >= 0; j--) {
14725 var cb = _callbacks[j];
14726 cb();
14727 }
14728
14729 _callbacks.splice(0, _callbacks.length);
14730 }; // step and remove if done
14731
14732
14733 for (var i = current.length - 1; i >= 0; i--) {
14734 var ani = current[i];
14735 var ani_p = ani._private;
14736
14737 if (ani_p.stopped) {
14738 current.splice(i, 1);
14739 ani_p.hooked = false;
14740 ani_p.playing = false;
14741 ani_p.started = false;
14742 callbacks(ani_p.frames);
14743 continue;
14744 }
14745
14746 if (!ani_p.playing && !ani_p.applying) {
14747 continue;
14748 } // an apply() while playing shouldn't do anything
14749
14750
14751 if (ani_p.playing && ani_p.applying) {
14752 ani_p.applying = false;
14753 }
14754
14755 if (!ani_p.started) {
14756 startAnimation(ele, ani, now);
14757 }
14758
14759 step(ele, ani, now, isCore);
14760
14761 if (ani_p.applying) {
14762 ani_p.applying = false;
14763 }
14764
14765 callbacks(ani_p.frames);
14766
14767 if (ani_p.step != null) {
14768 ani_p.step(now);
14769 }
14770
14771 if (ani.completed()) {
14772 current.splice(i, 1);
14773 ani_p.hooked = false;
14774 ani_p.playing = false;
14775 ani_p.started = false;
14776 callbacks(ani_p.completes);
14777 }
14778
14779 ranAnis = true;
14780 }
14781
14782 if (!isCore && current.length === 0 && queue.length === 0) {
14783 doneEles.push(ele);
14784 }
14785
14786 return ranAnis;
14787 } // stepElement
14788 // handle all eles
14789
14790
14791 var ranEleAni = false;
14792
14793 for (var e = 0; e < eles.length; e++) {
14794 var ele = eles[e];
14795 var handledThisEle = stepOne(ele);
14796 ranEleAni = ranEleAni || handledThisEle;
14797 } // each element
14798
14799
14800 var ranCoreAni = stepOne(cy, true); // notify renderer
14801
14802 if (ranEleAni || ranCoreAni) {
14803 if (eles.length > 0) {
14804 cy.notify('draw', eles);
14805 } else {
14806 cy.notify('draw');
14807 }
14808 } // remove elements from list of currently animating if its queues are empty
14809
14810
14811 eles.unmerge(doneEles);
14812 cy.emit('step');
14813 } // stepAll
14814
14815 var corefn$1 = {
14816 // pull in animation functions
14817 animate: define$3.animate(),
14818 animation: define$3.animation(),
14819 animated: define$3.animated(),
14820 clearQueue: define$3.clearQueue(),
14821 delay: define$3.delay(),
14822 delayAnimation: define$3.delayAnimation(),
14823 stop: define$3.stop(),
14824 addToAnimationPool: function addToAnimationPool(eles) {
14825 var cy = this;
14826
14827 if (!cy.styleEnabled()) {
14828 return;
14829 } // save cycles when no style used
14830
14831
14832 cy._private.aniEles.merge(eles);
14833 },
14834 stopAnimationLoop: function stopAnimationLoop() {
14835 this._private.animationsRunning = false;
14836 },
14837 startAnimationLoop: function startAnimationLoop() {
14838 var cy = this;
14839 cy._private.animationsRunning = true;
14840
14841 if (!cy.styleEnabled()) {
14842 return;
14843 } // save cycles when no style used
14844 // NB the animation loop will exec in headless environments if style enabled
14845 // and explicit cy.destroy() is necessary to stop the loop
14846
14847
14848 function headlessStep() {
14849 if (!cy._private.animationsRunning) {
14850 return;
14851 }
14852
14853 requestAnimationFrame(function animationStep(now) {
14854 stepAll(now, cy);
14855 headlessStep();
14856 });
14857 }
14858
14859 var renderer = cy.renderer();
14860
14861 if (renderer && renderer.beforeRender) {
14862 // let the renderer schedule animations
14863 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14864 stepAll(now, cy);
14865 }, renderer.beforeRenderPriorities.animations);
14866 } else {
14867 // manage the animation loop ourselves
14868 headlessStep(); // first call
14869 }
14870 }
14871 };
14872
14873 var emitterOptions$1 = {
14874 qualifierCompare: function qualifierCompare(selector1, selector2) {
14875 if (selector1 == null || selector2 == null) {
14876 return selector1 == null && selector2 == null;
14877 } else {
14878 return selector1.sameText(selector2);
14879 }
14880 },
14881 eventMatches: function eventMatches(cy, listener, eventObj) {
14882 var selector = listener.qualifier;
14883
14884 if (selector != null) {
14885 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14886 }
14887
14888 return true;
14889 },
14890 addEventFields: function addEventFields(cy, evt) {
14891 evt.cy = cy;
14892 evt.target = cy;
14893 },
14894 callbackContext: function callbackContext(cy, listener, eventObj) {
14895 return listener.qualifier != null ? eventObj.target : cy;
14896 }
14897 };
14898
14899 var argSelector$1 = function argSelector(arg) {
14900 if (string(arg)) {
14901 return new Selector(arg);
14902 } else {
14903 return arg;
14904 }
14905 };
14906
14907 var elesfn$v = {
14908 createEmitter: function createEmitter() {
14909 var _p = this._private;
14910
14911 if (!_p.emitter) {
14912 _p.emitter = new Emitter(emitterOptions$1, this);
14913 }
14914
14915 return this;
14916 },
14917 emitter: function emitter() {
14918 return this._private.emitter;
14919 },
14920 on: function on(events, selector, callback) {
14921 this.emitter().on(events, argSelector$1(selector), callback);
14922 return this;
14923 },
14924 removeListener: function removeListener(events, selector, callback) {
14925 this.emitter().removeListener(events, argSelector$1(selector), callback);
14926 return this;
14927 },
14928 removeAllListeners: function removeAllListeners() {
14929 this.emitter().removeAllListeners();
14930 return this;
14931 },
14932 one: function one(events, selector, callback) {
14933 this.emitter().one(events, argSelector$1(selector), callback);
14934 return this;
14935 },
14936 once: function once(events, selector, callback) {
14937 this.emitter().one(events, argSelector$1(selector), callback);
14938 return this;
14939 },
14940 emit: function emit(events, extraParams) {
14941 this.emitter().emit(events, extraParams);
14942 return this;
14943 },
14944 emitAndNotify: function emitAndNotify(event, eles) {
14945 this.emit(event);
14946 this.notify(event, eles);
14947 return this;
14948 }
14949 };
14950 define$3.eventAliasesOn(elesfn$v);
14951
14952 var corefn$2 = {
14953 png: function png(options) {
14954 var renderer = this._private.renderer;
14955 options = options || {};
14956 return renderer.png(options);
14957 },
14958 jpg: function jpg(options) {
14959 var renderer = this._private.renderer;
14960 options = options || {};
14961 options.bg = options.bg || '#fff';
14962 return renderer.jpg(options);
14963 }
14964 };
14965 corefn$2.jpeg = corefn$2.jpg;
14966
14967 var corefn$3 = {
14968 layout: function layout(options) {
14969 var cy = this;
14970
14971 if (options == null) {
14972 error('Layout options must be specified to make a layout');
14973 return;
14974 }
14975
14976 if (options.name == null) {
14977 error('A `name` must be specified to make a layout');
14978 return;
14979 }
14980
14981 var name = options.name;
14982 var Layout = cy.extension('layout', name);
14983
14984 if (Layout == null) {
14985 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14986 return;
14987 }
14988
14989 var eles;
14990
14991 if (string(options.eles)) {
14992 eles = cy.$(options.eles);
14993 } else {
14994 eles = options.eles != null ? options.eles : cy.$();
14995 }
14996
14997 var layout = new Layout(extend({}, options, {
14998 cy: cy,
14999 eles: eles
15000 }));
15001 return layout;
15002 }
15003 };
15004 corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
15005
15006 var corefn$4 = {
15007 notify: function notify(eventName, eventEles) {
15008 var _p = this._private;
15009
15010 if (this.batching()) {
15011 _p.batchNotifications = _p.batchNotifications || {};
15012 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
15013
15014 if (eventEles != null) {
15015 eles.merge(eventEles);
15016 }
15017
15018 return; // notifications are disabled during batching
15019 }
15020
15021 if (!_p.notificationsEnabled) {
15022 return;
15023 } // exit on disabled
15024
15025
15026 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
15027
15028 if (this.destroyed() || !renderer) {
15029 return;
15030 }
15031
15032 renderer.notify(eventName, eventEles);
15033 },
15034 notifications: function notifications(bool) {
15035 var p = this._private;
15036
15037 if (bool === undefined) {
15038 return p.notificationsEnabled;
15039 } else {
15040 p.notificationsEnabled = bool ? true : false;
15041 }
15042
15043 return this;
15044 },
15045 noNotifications: function noNotifications(callback) {
15046 this.notifications(false);
15047 callback();
15048 this.notifications(true);
15049 },
15050 batching: function batching() {
15051 return this._private.batchCount > 0;
15052 },
15053 startBatch: function startBatch() {
15054 var _p = this._private;
15055
15056 if (_p.batchCount == null) {
15057 _p.batchCount = 0;
15058 }
15059
15060 if (_p.batchCount === 0) {
15061 _p.batchStyleEles = this.collection();
15062 _p.batchNotifications = {};
15063 }
15064
15065 _p.batchCount++;
15066 return this;
15067 },
15068 endBatch: function endBatch() {
15069 var _p = this._private;
15070
15071 if (_p.batchCount === 0) {
15072 return this;
15073 }
15074
15075 _p.batchCount--;
15076
15077 if (_p.batchCount === 0) {
15078 // update style for dirty eles
15079 _p.batchStyleEles.updateStyle();
15080
15081 var renderer = this.renderer(); // notify the renderer of queued eles and event types
15082
15083 Object.keys(_p.batchNotifications).forEach(function (eventName) {
15084 var eles = _p.batchNotifications[eventName];
15085
15086 if (eles.empty()) {
15087 renderer.notify(eventName);
15088 } else {
15089 renderer.notify(eventName, eles);
15090 }
15091 });
15092 }
15093
15094 return this;
15095 },
15096 batch: function batch(callback) {
15097 this.startBatch();
15098 callback();
15099 this.endBatch();
15100 return this;
15101 },
15102 // for backwards compatibility
15103 batchData: function batchData(map) {
15104 var cy = this;
15105 return this.batch(function () {
15106 var ids = Object.keys(map);
15107
15108 for (var i = 0; i < ids.length; i++) {
15109 var id = ids[i];
15110 var data = map[id];
15111 var ele = cy.getElementById(id);
15112 ele.data(data);
15113 }
15114 });
15115 }
15116 };
15117
15118 var rendererDefaults = defaults({
15119 hideEdgesOnViewport: false,
15120 textureOnViewport: false,
15121 motionBlur: false,
15122 motionBlurOpacity: 0.05,
15123 pixelRatio: undefined,
15124 desktopTapThreshold: 4,
15125 touchTapThreshold: 8,
15126 wheelSensitivity: 1,
15127 debug: false,
15128 showFps: false
15129 });
15130 var corefn$5 = {
15131 renderTo: function renderTo(context, zoom, pan, pxRatio) {
15132 var r = this._private.renderer;
15133 r.renderTo(context, zoom, pan, pxRatio);
15134 return this;
15135 },
15136 renderer: function renderer() {
15137 return this._private.renderer;
15138 },
15139 forceRender: function forceRender() {
15140 this.notify('draw');
15141 return this;
15142 },
15143 resize: function resize() {
15144 this.invalidateSize();
15145 this.emitAndNotify('resize');
15146 return this;
15147 },
15148 initRenderer: function initRenderer(options) {
15149 var cy = this;
15150 var RendererProto = cy.extension('renderer', options.name);
15151
15152 if (RendererProto == null) {
15153 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
15154 return;
15155 }
15156
15157 if (options.wheelSensitivity !== undefined) {
15158 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.");
15159 }
15160
15161 var rOpts = rendererDefaults(options);
15162 rOpts.cy = cy;
15163 cy._private.renderer = new RendererProto(rOpts);
15164 this.notify('init');
15165 },
15166 destroyRenderer: function destroyRenderer() {
15167 var cy = this;
15168 cy.notify('destroy'); // destroy the renderer
15169
15170 var domEle = cy.container();
15171
15172 if (domEle) {
15173 domEle._cyreg = null;
15174
15175 while (domEle.childNodes.length > 0) {
15176 domEle.removeChild(domEle.childNodes[0]);
15177 }
15178 }
15179
15180 cy._private.renderer = null; // to be extra safe, remove the ref
15181
15182 cy.mutableElements().forEach(function (ele) {
15183 var _p = ele._private;
15184 _p.rscratch = {};
15185 _p.rstyle = {};
15186 _p.animation.current = [];
15187 _p.animation.queue = [];
15188 });
15189 },
15190 onRender: function onRender(fn) {
15191 return this.on('render', fn);
15192 },
15193 offRender: function offRender(fn) {
15194 return this.off('render', fn);
15195 }
15196 };
15197 corefn$5.invalidateDimensions = corefn$5.resize;
15198
15199 var corefn$6 = {
15200 // get a collection
15201 // - empty collection on no args
15202 // - collection of elements in the graph on selector arg
15203 // - guarantee a returned collection when elements or collection specified
15204 collection: function collection(eles, opts) {
15205 if (string(eles)) {
15206 return this.$(eles);
15207 } else if (elementOrCollection(eles)) {
15208 return eles.collection();
15209 } else if (array(eles)) {
15210 return new Collection(this, eles, opts);
15211 }
15212
15213 return new Collection(this);
15214 },
15215 nodes: function nodes(selector) {
15216 var nodes = this.$(function (ele) {
15217 return ele.isNode();
15218 });
15219
15220 if (selector) {
15221 return nodes.filter(selector);
15222 }
15223
15224 return nodes;
15225 },
15226 edges: function edges(selector) {
15227 var edges = this.$(function (ele) {
15228 return ele.isEdge();
15229 });
15230
15231 if (selector) {
15232 return edges.filter(selector);
15233 }
15234
15235 return edges;
15236 },
15237 // search the graph like jQuery
15238 $: function $(selector) {
15239 var eles = this._private.elements;
15240
15241 if (selector) {
15242 return eles.filter(selector);
15243 } else {
15244 return eles.spawnSelf();
15245 }
15246 },
15247 mutableElements: function mutableElements() {
15248 return this._private.elements;
15249 }
15250 }; // aliases
15251
15252 corefn$6.elements = corefn$6.filter = corefn$6.$;
15253
15254 var styfn = {}; // keys for style blocks, e.g. ttfftt
15255
15256 var TRUE = 't';
15257 var FALSE = 'f'; // (potentially expensive calculation)
15258 // apply the style to the element based on
15259 // - its bypass
15260 // - what selectors match it
15261
15262 styfn.apply = function (eles) {
15263 var self = this;
15264 var _p = self._private;
15265 var cy = _p.cy;
15266 var updatedEles = cy.collection();
15267
15268 if (_p.newStyle) {
15269 // clear style caches
15270 _p.contextStyles = {};
15271 _p.propDiffs = {};
15272 self.cleanElements(eles, true);
15273 }
15274
15275 for (var ie = 0; ie < eles.length; ie++) {
15276 var ele = eles[ie];
15277 var cxtMeta = self.getContextMeta(ele);
15278
15279 if (cxtMeta.empty) {
15280 continue;
15281 }
15282
15283 var cxtStyle = self.getContextStyle(cxtMeta);
15284 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
15285
15286 if (!_p.newStyle) {
15287 self.updateTransitions(ele, app.diffProps);
15288 }
15289
15290 var hintsDiff = self.updateStyleHints(ele);
15291
15292 if (hintsDiff) {
15293 updatedEles.push(ele);
15294 }
15295 } // for elements
15296
15297
15298 _p.newStyle = false;
15299 return updatedEles;
15300 };
15301
15302 styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
15303 var self = this;
15304 var cache = self._private.propDiffs = self._private.propDiffs || {};
15305 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
15306 var cachedVal = cache[dualCxtKey];
15307
15308 if (cachedVal) {
15309 return cachedVal;
15310 }
15311
15312 var diffProps = [];
15313 var addedProp = {};
15314
15315 for (var i = 0; i < self.length; i++) {
15316 var cxt = self[i];
15317 var oldHasCxt = oldCxtKey[i] === TRUE;
15318 var newHasCxt = newCxtKey[i] === TRUE;
15319 var cxtHasDiffed = oldHasCxt !== newHasCxt;
15320 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
15321
15322 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
15323 var props = void 0;
15324
15325 if (cxtHasDiffed && cxtHasMappedProps) {
15326 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
15327 } else if (cxtHasDiffed) {
15328 props = cxt.properties; // need to check them all
15329 } else if (cxtHasMappedProps) {
15330 props = cxt.mappedProperties; // only need to check mapped
15331 }
15332
15333 for (var j = 0; j < props.length; j++) {
15334 var prop = props[j];
15335 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
15336 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
15337 // is cached)
15338
15339 var laterCxtOverrides = false;
15340
15341 for (var k = i + 1; k < self.length; k++) {
15342 var laterCxt = self[k];
15343 var hasLaterCxt = newCxtKey[k] === TRUE;
15344
15345 if (!hasLaterCxt) {
15346 continue;
15347 } // can't override unless the context is active
15348
15349
15350 laterCxtOverrides = laterCxt.properties[prop.name] != null;
15351
15352 if (laterCxtOverrides) {
15353 break;
15354 } // exit early as long as one later context overrides
15355
15356 }
15357
15358 if (!addedProp[name] && !laterCxtOverrides) {
15359 addedProp[name] = true;
15360 diffProps.push(name);
15361 }
15362 } // for props
15363
15364 } // if
15365
15366 } // for contexts
15367
15368
15369 cache[dualCxtKey] = diffProps;
15370 return diffProps;
15371 };
15372
15373 styfn.getContextMeta = function (ele) {
15374 var self = this;
15375 var cxtKey = '';
15376 var diffProps;
15377 var prevKey = ele._private.styleCxtKey || '';
15378
15379 if (self._private.newStyle) {
15380 prevKey = ''; // since we need to apply all style if a fresh stylesheet
15381 } // get the cxt key
15382
15383
15384 for (var i = 0; i < self.length; i++) {
15385 var context = self[i];
15386 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
15387
15388 if (contextSelectorMatches) {
15389 cxtKey += TRUE;
15390 } else {
15391 cxtKey += FALSE;
15392 }
15393 } // for context
15394
15395
15396 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
15397 ele._private.styleCxtKey = cxtKey;
15398 return {
15399 key: cxtKey,
15400 diffPropNames: diffProps,
15401 empty: diffProps.length === 0
15402 };
15403 }; // gets a computed ele style object based on matched contexts
15404
15405
15406 styfn.getContextStyle = function (cxtMeta) {
15407 var cxtKey = cxtMeta.key;
15408 var self = this;
15409 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
15410
15411 if (cxtStyles[cxtKey]) {
15412 return cxtStyles[cxtKey];
15413 }
15414
15415 var style = {
15416 _private: {
15417 key: cxtKey
15418 }
15419 };
15420
15421 for (var i = 0; i < self.length; i++) {
15422 var cxt = self[i];
15423 var hasCxt = cxtKey[i] === TRUE;
15424
15425 if (!hasCxt) {
15426 continue;
15427 }
15428
15429 for (var j = 0; j < cxt.properties.length; j++) {
15430 var prop = cxt.properties[j];
15431 style[prop.name] = prop;
15432 }
15433 }
15434
15435 cxtStyles[cxtKey] = style;
15436 return style;
15437 };
15438
15439 styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
15440 var self = this;
15441 var diffProps = cxtMeta.diffPropNames;
15442 var retDiffProps = {};
15443 var types = self.types;
15444
15445 for (var i = 0; i < diffProps.length; i++) {
15446 var diffPropName = diffProps[i];
15447 var cxtProp = cxtStyle[diffPropName];
15448 var eleProp = ele.pstyle(diffPropName);
15449
15450 if (!cxtProp) {
15451 // no context prop means delete
15452 if (!eleProp) {
15453 continue; // no existing prop means nothing needs to be removed
15454 // nb affects initial application on mapped values like control-point-distances
15455 } else if (eleProp.bypass) {
15456 cxtProp = {
15457 name: diffPropName,
15458 deleteBypassed: true
15459 };
15460 } else {
15461 cxtProp = {
15462 name: diffPropName,
15463 "delete": true
15464 };
15465 }
15466 } // save cycles when the context prop doesn't need to be applied
15467
15468
15469 if (eleProp === cxtProp) {
15470 continue;
15471 } // save cycles when a mapped context prop doesn't need to be applied
15472
15473
15474 if (cxtProp.mapped === types.fn // context prop is function mapper
15475 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
15476 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
15477 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
15478 ) {
15479 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
15480 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
15481
15482 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
15483
15484 if (fnValue === mapping.prevFnValue) {
15485 continue;
15486 }
15487 }
15488
15489 var retDiffProp = retDiffProps[diffPropName] = {
15490 prev: eleProp
15491 };
15492 self.applyParsedProperty(ele, cxtProp);
15493 retDiffProp.next = ele.pstyle(diffPropName);
15494
15495 if (retDiffProp.next && retDiffProp.next.bypass) {
15496 retDiffProp.next = retDiffProp.next.bypassed;
15497 }
15498 }
15499
15500 return {
15501 diffProps: retDiffProps
15502 };
15503 };
15504
15505 styfn.updateStyleHints = function (ele) {
15506 var _p = ele._private;
15507 var self = this;
15508 var propNames = self.propertyGroupNames;
15509 var propGrKeys = self.propertyGroupKeys;
15510
15511 var propHash = function propHash(ele, propNames, seedKey) {
15512 return self.getPropertiesHash(ele, propNames, seedKey);
15513 };
15514
15515 var oldStyleKey = _p.styleKey;
15516
15517 if (ele.removed()) {
15518 return false;
15519 }
15520
15521 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
15522 // but lazily -- only use non-default prop values to reduce the number of hashes
15523 //
15524
15525 var overriddenStyles = ele._private.style;
15526 propNames = Object.keys(overriddenStyles);
15527
15528 for (var i = 0; i < propGrKeys.length; i++) {
15529 var grKey = propGrKeys[i];
15530 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15531 }
15532
15533 var updateGrKey1 = function updateGrKey1(val, grKey) {
15534 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
15535 };
15536
15537 var updateGrKey2 = function updateGrKey2(val, grKey) {
15538 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
15539 };
15540
15541 var updateGrKey = function updateGrKey(val, grKey) {
15542 updateGrKey1(val, grKey);
15543 updateGrKey2(val, grKey);
15544 };
15545
15546 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
15547 for (var j = 0; j < strVal.length; j++) {
15548 var ch = strVal.charCodeAt(j);
15549 updateGrKey1(ch, grKey);
15550 updateGrKey2(ch, grKey);
15551 }
15552 }; // - hashing works on 32 bit ints b/c we use bitwise ops
15553 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
15554 // - raise up small numbers so more significant digits are seen by hashing
15555 // - make small numbers larger than a normal value to avoid collisions
15556 // - works in practice and it's relatively cheap
15557
15558
15559 var N = 2000000000;
15560
15561 var cleanNum = function cleanNum(val) {
15562 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
15563 };
15564
15565 for (var _i = 0; _i < propNames.length; _i++) {
15566 var name = propNames[_i];
15567 var parsedProp = overriddenStyles[name];
15568
15569 if (parsedProp == null) {
15570 continue;
15571 }
15572
15573 var propInfo = this.properties[name];
15574 var type = propInfo.type;
15575 var _grKey = propInfo.groupKey;
15576 var normalizedNumberVal = void 0;
15577
15578 if (propInfo.hashOverride != null) {
15579 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
15580 } else if (parsedProp.pfValue != null) {
15581 normalizedNumberVal = parsedProp.pfValue;
15582 } // might not be a number if it allows enums
15583
15584
15585 var numberVal = propInfo.enums == null ? parsedProp.value : null;
15586 var haveNormNum = normalizedNumberVal != null;
15587 var haveUnitedNum = numberVal != null;
15588 var haveNum = haveNormNum || haveUnitedNum;
15589 var units = parsedProp.units; // numbers are cheaper to hash than strings
15590 // 1 hash op vs n hash ops (for length n string)
15591
15592 if (type.number && haveNum && !type.multiple) {
15593 var v = haveNormNum ? normalizedNumberVal : numberVal;
15594 updateGrKey(cleanNum(v), _grKey);
15595
15596 if (!haveNormNum && units != null) {
15597 updateGrKeyWStr(units, _grKey);
15598 }
15599 } else {
15600 updateGrKeyWStr(parsedProp.strValue, _grKey);
15601 }
15602 } // overall style key
15603 //
15604
15605
15606 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15607
15608 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
15609 var _grKey2 = propGrKeys[_i2];
15610 var grHash = _p.styleKeys[_grKey2];
15611 hash[0] = hashInt(grHash[0], hash[0]);
15612 hash[1] = hashIntAlt(grHash[1], hash[1]);
15613 }
15614
15615 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
15616 //
15617
15618 var sk = _p.styleKeys;
15619 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
15620 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
15621 _p.labelKey = combineHashesArray(labelKeys);
15622 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
15623
15624 if (!isNode) {
15625 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
15626 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
15627 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
15628 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
15629 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
15630 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
15631 } // node
15632 //
15633
15634
15635 if (isNode) {
15636 var _p$styleKeys = _p.styleKeys,
15637 nodeBody = _p$styleKeys.nodeBody,
15638 nodeBorder = _p$styleKeys.nodeBorder,
15639 backgroundImage = _p$styleKeys.backgroundImage,
15640 compound = _p$styleKeys.compound,
15641 pie = _p$styleKeys.pie;
15642 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
15643 return k != null;
15644 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
15645 _p.nodeKey = combineHashesArray(nodeKeys);
15646 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
15647 }
15648
15649 return oldStyleKey !== _p.styleKey;
15650 };
15651
15652 styfn.clearStyleHints = function (ele) {
15653 var _p = ele._private;
15654 _p.styleKeys = {};
15655 _p.styleKey = null;
15656 _p.labelKey = null;
15657 _p.labelStyleKey = null;
15658 _p.sourceLabelKey = null;
15659 _p.sourceLabelStyleKey = null;
15660 _p.targetLabelKey = null;
15661 _p.targetLabelStyleKey = null;
15662 _p.nodeKey = null;
15663 _p.hasPie = null;
15664 }; // apply a property to the style (for internal use)
15665 // returns whether application was successful
15666 //
15667 // now, this function flattens the property, and here's how:
15668 //
15669 // for parsedProp:{ bypass: true, deleteBypass: true }
15670 // no property is generated, instead the bypass property in the
15671 // element's style is replaced by what's pointed to by the `bypassed`
15672 // field in the bypass property (i.e. restoring the property the
15673 // bypass was overriding)
15674 //
15675 // for parsedProp:{ mapped: truthy }
15676 // the generated flattenedProp:{ mapping: prop }
15677 //
15678 // for parsedProp:{ bypass: true }
15679 // the generated flattenedProp:{ bypassed: parsedProp }
15680
15681
15682 styfn.applyParsedProperty = function (ele, parsedProp) {
15683 var self = this;
15684 var prop = parsedProp;
15685 var style = ele._private.style;
15686 var flatProp;
15687 var types = self.types;
15688 var type = self.properties[prop.name].type;
15689 var propIsBypass = prop.bypass;
15690 var origProp = style[prop.name];
15691 var origPropIsBypass = origProp && origProp.bypass;
15692 var _p = ele._private;
15693 var flatPropMapping = 'mapping';
15694
15695 var getVal = function getVal(p) {
15696 if (p == null) {
15697 return null;
15698 } else if (p.pfValue != null) {
15699 return p.pfValue;
15700 } else {
15701 return p.value;
15702 }
15703 };
15704
15705 var checkTriggers = function checkTriggers() {
15706 var fromVal = getVal(origProp);
15707 var toVal = getVal(prop);
15708 self.checkTriggers(ele, prop.name, fromVal, toVal);
15709 }; // edge sanity checks to prevent the client from making serious mistakes
15710
15711
15712 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15713 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15714 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15715 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15716 }
15717
15718 if (prop["delete"]) {
15719 // delete the property and use the default value on falsey value
15720 style[prop.name] = undefined;
15721 checkTriggers();
15722 return true;
15723 }
15724
15725 if (prop.deleteBypassed) {
15726 // delete the property that the
15727 if (!origProp) {
15728 checkTriggers();
15729 return true; // can't delete if no prop
15730 } else if (origProp.bypass) {
15731 // delete bypassed
15732 origProp.bypassed = undefined;
15733 checkTriggers();
15734 return true;
15735 } else {
15736 return false; // we're unsuccessful deleting the bypassed
15737 }
15738 } // check if we need to delete the current bypass
15739
15740
15741 if (prop.deleteBypass) {
15742 // then this property is just here to indicate we need to delete
15743 if (!origProp) {
15744 checkTriggers();
15745 return true; // property is already not defined
15746 } else if (origProp.bypass) {
15747 // then replace the bypass property with the original
15748 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15749 style[prop.name] = origProp.bypassed;
15750 checkTriggers();
15751 return true;
15752 } else {
15753 return false; // we're unsuccessful deleting the bypass
15754 }
15755 }
15756
15757 var printMappingErr = function printMappingErr() {
15758 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');
15759 }; // put the property in the style objects
15760
15761
15762 switch (prop.mapped) {
15763 // flatten the property if mapped
15764 case types.mapData:
15765 {
15766 // flatten the field (e.g. data.foo.bar)
15767 var fields = prop.field.split('.');
15768 var fieldVal = _p.data;
15769
15770 for (var i = 0; i < fields.length && fieldVal; i++) {
15771 var field = fields[i];
15772 fieldVal = fieldVal[field];
15773 }
15774
15775 if (fieldVal == null) {
15776 printMappingErr();
15777 return false;
15778 }
15779
15780 var percent;
15781
15782 if (!number(fieldVal)) {
15783 // then don't apply and fall back on the existing style
15784 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15785 return false;
15786 } else {
15787 var fieldWidth = prop.fieldMax - prop.fieldMin;
15788
15789 if (fieldWidth === 0) {
15790 // safety check -- not strictly necessary as no props of zero range should be passed here
15791 percent = 0;
15792 } else {
15793 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15794 }
15795 } // make sure to bound percent value
15796
15797
15798 if (percent < 0) {
15799 percent = 0;
15800 } else if (percent > 1) {
15801 percent = 1;
15802 }
15803
15804 if (type.color) {
15805 var r1 = prop.valueMin[0];
15806 var r2 = prop.valueMax[0];
15807 var g1 = prop.valueMin[1];
15808 var g2 = prop.valueMax[1];
15809 var b1 = prop.valueMin[2];
15810 var b2 = prop.valueMax[2];
15811 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15812 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15813 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)];
15814 flatProp = {
15815 // colours are simple, so just create the flat property instead of expensive string parsing
15816 bypass: prop.bypass,
15817 // we're a bypass if the mapping property is a bypass
15818 name: prop.name,
15819 value: clr,
15820 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15821 };
15822 } else if (type.number) {
15823 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15824 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15825 } else {
15826 return false; // can only map to colours and numbers
15827 }
15828
15829 if (!flatProp) {
15830 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15831 printMappingErr();
15832 return false;
15833 }
15834
15835 flatProp.mapping = prop; // keep a reference to the mapping
15836
15837 prop = flatProp; // the flattened (mapped) property is the one we want
15838
15839 break;
15840 }
15841 // direct mapping
15842
15843 case types.data:
15844 {
15845 // flatten the field (e.g. data.foo.bar)
15846 var _fields = prop.field.split('.');
15847
15848 var _fieldVal = _p.data;
15849
15850 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15851 var _field = _fields[_i3];
15852 _fieldVal = _fieldVal[_field];
15853 }
15854
15855 if (_fieldVal != null) {
15856 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15857 }
15858
15859 if (!flatProp) {
15860 // if we can't flatten the property, then don't apply and fall back on the existing style
15861 printMappingErr();
15862 return false;
15863 }
15864
15865 flatProp.mapping = prop; // keep a reference to the mapping
15866
15867 prop = flatProp; // the flattened (mapped) property is the one we want
15868
15869 break;
15870 }
15871
15872 case types.fn:
15873 {
15874 var fn = prop.value;
15875 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15876
15877 prop.prevFnValue = fnRetVal;
15878
15879 if (fnRetVal == null) {
15880 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15881 return false;
15882 }
15883
15884 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15885
15886 if (!flatProp) {
15887 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15888 return false;
15889 }
15890
15891 flatProp.mapping = copy(prop); // keep a reference to the mapping
15892
15893 prop = flatProp; // the flattened (mapped) property is the one we want
15894
15895 break;
15896 }
15897
15898 case undefined:
15899 break;
15900 // just set the property
15901
15902 default:
15903 return false;
15904 // not a valid mapping
15905 } // if the property is a bypass property, then link the resultant property to the original one
15906
15907
15908 if (propIsBypass) {
15909 if (origPropIsBypass) {
15910 // then this bypass overrides the existing one
15911 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15912 } else {
15913 // then link the orig prop to the new bypass
15914 prop.bypassed = origProp;
15915 }
15916
15917 style[prop.name] = prop; // and set
15918 } else {
15919 // prop is not bypass
15920 if (origPropIsBypass) {
15921 // then keep the orig prop (since it's a bypass) and link to the new prop
15922 origProp.bypassed = prop;
15923 } else {
15924 // then just replace the old prop with the new one
15925 style[prop.name] = prop;
15926 }
15927 }
15928
15929 checkTriggers();
15930 return true;
15931 };
15932
15933 styfn.cleanElements = function (eles, keepBypasses) {
15934 for (var i = 0; i < eles.length; i++) {
15935 var ele = eles[i];
15936 this.clearStyleHints(ele);
15937 ele.dirtyCompoundBoundsCache();
15938 ele.dirtyBoundingBoxCache();
15939
15940 if (!keepBypasses) {
15941 ele._private.style = {};
15942 } else {
15943 var style = ele._private.style;
15944 var propNames = Object.keys(style);
15945
15946 for (var j = 0; j < propNames.length; j++) {
15947 var propName = propNames[j];
15948 var eleProp = style[propName];
15949
15950 if (eleProp != null) {
15951 if (eleProp.bypass) {
15952 eleProp.bypassed = null;
15953 } else {
15954 style[propName] = null;
15955 }
15956 }
15957 }
15958 }
15959 }
15960 }; // updates the visual style for all elements (useful for manual style modification after init)
15961
15962
15963 styfn.update = function () {
15964 var cy = this._private.cy;
15965 var eles = cy.mutableElements();
15966 eles.updateStyle();
15967 }; // diffProps : { name => { prev, next } }
15968
15969
15970 styfn.updateTransitions = function (ele, diffProps) {
15971 var self = this;
15972 var _p = ele._private;
15973 var props = ele.pstyle('transition-property').value;
15974 var duration = ele.pstyle('transition-duration').pfValue;
15975 var delay = ele.pstyle('transition-delay').pfValue;
15976
15977 if (props.length > 0 && duration > 0) {
15978 var style = {}; // build up the style to animate towards
15979
15980 var anyPrev = false;
15981
15982 for (var i = 0; i < props.length; i++) {
15983 var prop = props[i];
15984 var styProp = ele.pstyle(prop);
15985 var diffProp = diffProps[prop];
15986
15987 if (!diffProp) {
15988 continue;
15989 }
15990
15991 var prevProp = diffProp.prev;
15992 var fromProp = prevProp;
15993 var toProp = diffProp.next != null ? diffProp.next : styProp;
15994 var diff = false;
15995 var initVal = void 0;
15996 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15997
15998 if (!fromProp) {
15999 continue;
16000 } // consider px values
16001
16002
16003 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
16004 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
16005
16006 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
16007 } else if (number(fromProp.value) && number(toProp.value)) {
16008 diff = toProp.value - fromProp.value; // nonzero is truthy
16009
16010 initVal = fromProp.value + initDt * diff; // consider colour values
16011 } else if (array(fromProp.value) && array(toProp.value)) {
16012 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
16013 initVal = fromProp.strValue;
16014 } // the previous value is good for an animation only if it's different
16015
16016
16017 if (diff) {
16018 style[prop] = toProp.strValue; // to val
16019
16020 this.applyBypass(ele, prop, initVal); // from val
16021
16022 anyPrev = true;
16023 }
16024 } // end if props allow ani
16025 // can't transition if there's nothing previous to transition from
16026
16027
16028 if (!anyPrev) {
16029 return;
16030 }
16031
16032 _p.transitioning = true;
16033 new Promise$1(function (resolve) {
16034 if (delay > 0) {
16035 ele.delayAnimation(delay).play().promise().then(resolve);
16036 } else {
16037 resolve();
16038 }
16039 }).then(function () {
16040 return ele.animation({
16041 style: style,
16042 duration: duration,
16043 easing: ele.pstyle('transition-timing-function').value,
16044 queue: false
16045 }).play().promise();
16046 }).then(function () {
16047 // if( !isBypass ){
16048 self.removeBypasses(ele, props);
16049 ele.emitAndNotify('style'); // }
16050
16051 _p.transitioning = false;
16052 });
16053 } else if (_p.transitioning) {
16054 this.removeBypasses(ele, props);
16055 ele.emitAndNotify('style');
16056 _p.transitioning = false;
16057 }
16058 };
16059
16060 styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
16061 var prop = this.properties[name];
16062 var triggerCheck = getTrigger(prop);
16063
16064 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
16065 onTrigger(prop);
16066 }
16067 };
16068
16069 styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
16070 var _this = this;
16071
16072 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
16073 return prop.triggersZOrder;
16074 }, function () {
16075 _this._private.cy.notify('zorder', ele);
16076 });
16077 };
16078
16079 styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
16080 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
16081 return prop.triggersBounds;
16082 }, function (prop) {
16083 ele.dirtyCompoundBoundsCache();
16084 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
16085 // then dirty the pll edge bb cache as well
16086
16087 if ( // only for beziers -- so performance of other edges isn't affected
16088 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
16089 ele.parallelEdges().forEach(function (pllEdge) {
16090 if (pllEdge.isBundledBezier()) {
16091 pllEdge.dirtyBoundingBoxCache();
16092 }
16093 });
16094 }
16095 });
16096 };
16097
16098 styfn.checkTriggers = function (ele, name, fromValue, toValue) {
16099 ele.dirtyStyleCache();
16100 this.checkZOrderTrigger(ele, name, fromValue, toValue);
16101 this.checkBoundsTrigger(ele, name, fromValue, toValue);
16102 };
16103
16104 var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
16105 // returns true iff application was successful for at least 1 specified property
16106
16107 styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
16108 var self = this;
16109 var props = [];
16110 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
16111
16112 if (name === '*' || name === '**') {
16113 // apply to all property names
16114 if (value !== undefined) {
16115 for (var i = 0; i < self.properties.length; i++) {
16116 var prop = self.properties[i];
16117 var _name = prop.name;
16118 var parsedProp = this.parse(_name, value, true);
16119
16120 if (parsedProp) {
16121 props.push(parsedProp);
16122 }
16123 }
16124 }
16125 } else if (string(name)) {
16126 // then parse the single property
16127 var _parsedProp = this.parse(name, value, true);
16128
16129 if (_parsedProp) {
16130 props.push(_parsedProp);
16131 }
16132 } else if (plainObject(name)) {
16133 // then parse each property
16134 var specifiedProps = name;
16135 updateTransitions = value;
16136 var names = Object.keys(specifiedProps);
16137
16138 for (var _i = 0; _i < names.length; _i++) {
16139 var _name2 = names[_i];
16140 var _value = specifiedProps[_name2];
16141
16142 if (_value === undefined) {
16143 // try camel case name too
16144 _value = specifiedProps[dash2camel(_name2)];
16145 }
16146
16147 if (_value !== undefined) {
16148 var _parsedProp2 = this.parse(_name2, _value, true);
16149
16150 if (_parsedProp2) {
16151 props.push(_parsedProp2);
16152 }
16153 }
16154 }
16155 } else {
16156 // can't do anything without well defined properties
16157 return false;
16158 } // we've failed if there are no valid properties
16159
16160
16161 if (props.length === 0) {
16162 return false;
16163 } // now, apply the bypass properties on the elements
16164
16165
16166 var ret = false; // return true if at least one succesful bypass applied
16167
16168 for (var _i2 = 0; _i2 < eles.length; _i2++) {
16169 // for each ele
16170 var ele = eles[_i2];
16171 var diffProps = {};
16172 var diffProp = void 0;
16173
16174 for (var j = 0; j < props.length; j++) {
16175 // for each prop
16176 var _prop = props[j];
16177
16178 if (updateTransitions) {
16179 var prevProp = ele.pstyle(_prop.name);
16180 diffProp = diffProps[_prop.name] = {
16181 prev: prevProp
16182 };
16183 }
16184
16185 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
16186
16187 if (updateTransitions) {
16188 diffProp.next = ele.pstyle(_prop.name);
16189 }
16190 } // for props
16191
16192
16193 if (ret) {
16194 this.updateStyleHints(ele);
16195 }
16196
16197 if (updateTransitions) {
16198 this.updateTransitions(ele, diffProps, isBypass);
16199 }
16200 } // for eles
16201
16202
16203 return ret;
16204 }; // only useful in specific cases like animation
16205
16206
16207 styfn$1.overrideBypass = function (eles, name, value) {
16208 name = camel2dash(name);
16209
16210 for (var i = 0; i < eles.length; i++) {
16211 var ele = eles[i];
16212 var prop = ele._private.style[name];
16213 var type = this.properties[name].type;
16214 var isColor = type.color;
16215 var isMulti = type.mutiple;
16216 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
16217
16218 if (!prop || !prop.bypass) {
16219 // need a bypass if one doesn't exist
16220 this.applyBypass(ele, name, value);
16221 } else {
16222 prop.value = value;
16223
16224 if (prop.pfValue != null) {
16225 prop.pfValue = value;
16226 }
16227
16228 if (isColor) {
16229 prop.strValue = 'rgb(' + value.join(',') + ')';
16230 } else if (isMulti) {
16231 prop.strValue = value.join(' ');
16232 } else {
16233 prop.strValue = '' + value;
16234 }
16235
16236 this.updateStyleHints(ele);
16237 }
16238
16239 this.checkTriggers(ele, name, oldValue, value);
16240 }
16241 };
16242
16243 styfn$1.removeAllBypasses = function (eles, updateTransitions) {
16244 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
16245 };
16246
16247 styfn$1.removeBypasses = function (eles, props, updateTransitions) {
16248 var isBypass = true;
16249
16250 for (var j = 0; j < eles.length; j++) {
16251 var ele = eles[j];
16252 var diffProps = {};
16253
16254 for (var i = 0; i < props.length; i++) {
16255 var name = props[i];
16256 var prop = this.properties[name];
16257 var prevProp = ele.pstyle(prop.name);
16258
16259 if (!prevProp || !prevProp.bypass) {
16260 // if a bypass doesn't exist for the prop, nothing needs to be removed
16261 continue;
16262 }
16263
16264 var value = ''; // empty => remove bypass
16265
16266 var parsedProp = this.parse(name, value, true);
16267 var diffProp = diffProps[prop.name] = {
16268 prev: prevProp
16269 };
16270 this.applyParsedProperty(ele, parsedProp);
16271 diffProp.next = ele.pstyle(prop.name);
16272 } // for props
16273
16274
16275 this.updateStyleHints(ele);
16276
16277 if (updateTransitions) {
16278 this.updateTransitions(ele, diffProps, isBypass);
16279 }
16280 } // for eles
16281
16282 };
16283
16284 var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
16285
16286 styfn$2.getEmSizeInPixels = function () {
16287 var px = this.containerCss('font-size');
16288
16289 if (px != null) {
16290 return parseFloat(px);
16291 } else {
16292 return 1; // for headless
16293 }
16294 }; // gets css property from the core container
16295
16296
16297 styfn$2.containerCss = function (propName) {
16298 var cy = this._private.cy;
16299 var domElement = cy.container();
16300
16301 if (window$1 && domElement && window$1.getComputedStyle) {
16302 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
16303 }
16304 };
16305
16306 var styfn$3 = {}; // gets the rendered style for an element
16307
16308 styfn$3.getRenderedStyle = function (ele, prop) {
16309 if (prop) {
16310 return this.getStylePropertyValue(ele, prop, true);
16311 } else {
16312 return this.getRawStyle(ele, true);
16313 }
16314 }; // gets the raw style for an element
16315
16316
16317 styfn$3.getRawStyle = function (ele, isRenderedVal) {
16318 var self = this;
16319 ele = ele[0]; // insure it's an element
16320
16321 if (ele) {
16322 var rstyle = {};
16323
16324 for (var i = 0; i < self.properties.length; i++) {
16325 var prop = self.properties[i];
16326 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
16327
16328 if (val != null) {
16329 rstyle[prop.name] = val;
16330 rstyle[dash2camel(prop.name)] = val;
16331 }
16332 }
16333
16334 return rstyle;
16335 }
16336 };
16337
16338 styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
16339 var pstyle = ele.pstyle(property)[subproperty][index];
16340 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
16341 };
16342
16343 styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
16344 var self = this;
16345 ele = ele[0]; // insure it's an element
16346
16347 if (ele) {
16348 var prop = self.properties[propName];
16349
16350 if (prop.alias) {
16351 prop = prop.pointsTo;
16352 }
16353
16354 var type = prop.type;
16355 var styleProp = ele.pstyle(prop.name);
16356
16357 if (styleProp) {
16358 var value = styleProp.value,
16359 units = styleProp.units,
16360 strValue = styleProp.strValue;
16361
16362 if (isRenderedVal && type.number && value != null && number(value)) {
16363 var zoom = ele.cy().zoom();
16364
16365 var getRenderedValue = function getRenderedValue(val) {
16366 return val * zoom;
16367 };
16368
16369 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
16370 return getRenderedValue(val) + units;
16371 };
16372
16373 var isArrayValue = array(value);
16374 var haveUnits = isArrayValue ? units.every(function (u) {
16375 return u != null;
16376 }) : units != null;
16377
16378 if (haveUnits) {
16379 if (isArrayValue) {
16380 return value.map(function (v, i) {
16381 return getValueStringWithUnits(v, units[i]);
16382 }).join(' ');
16383 } else {
16384 return getValueStringWithUnits(value, units);
16385 }
16386 } else {
16387 if (isArrayValue) {
16388 return value.map(function (v) {
16389 return string(v) ? v : '' + getRenderedValue(v);
16390 }).join(' ');
16391 } else {
16392 return '' + getRenderedValue(value);
16393 }
16394 }
16395 } else if (strValue != null) {
16396 return strValue;
16397 }
16398 }
16399
16400 return null;
16401 }
16402 };
16403
16404 styfn$3.getAnimationStartStyle = function (ele, aniProps) {
16405 var rstyle = {};
16406
16407 for (var i = 0; i < aniProps.length; i++) {
16408 var aniProp = aniProps[i];
16409 var name = aniProp.name;
16410 var styleProp = ele.pstyle(name);
16411
16412 if (styleProp !== undefined) {
16413 // then make a prop of it
16414 if (plainObject(styleProp)) {
16415 styleProp = this.parse(name, styleProp.strValue);
16416 } else {
16417 styleProp = this.parse(name, styleProp);
16418 }
16419 }
16420
16421 if (styleProp) {
16422 rstyle[name] = styleProp;
16423 }
16424 }
16425
16426 return rstyle;
16427 };
16428
16429 styfn$3.getPropsList = function (propsObj) {
16430 var self = this;
16431 var rstyle = [];
16432 var style = propsObj;
16433 var props = self.properties;
16434
16435 if (style) {
16436 var names = Object.keys(style);
16437
16438 for (var i = 0; i < names.length; i++) {
16439 var name = names[i];
16440 var val = style[name];
16441 var prop = props[name] || props[camel2dash(name)];
16442 var styleProp = this.parse(prop.name, val);
16443
16444 if (styleProp) {
16445 rstyle.push(styleProp);
16446 }
16447 }
16448 }
16449
16450 return rstyle;
16451 };
16452
16453 styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
16454 var hash = seed.slice();
16455 var name, val, strVal, chVal;
16456 var i, j;
16457
16458 for (i = 0; i < propNames.length; i++) {
16459 name = propNames[i];
16460 val = ele.pstyle(name, false);
16461
16462 if (val == null) {
16463 continue;
16464 } else if (val.pfValue != null) {
16465 hash[0] = hashInt(chVal, hash[0]);
16466 hash[1] = hashIntAlt(chVal, hash[1]);
16467 } else {
16468 strVal = val.strValue;
16469
16470 for (j = 0; j < strVal.length; j++) {
16471 chVal = strVal.charCodeAt(j);
16472 hash[0] = hashInt(chVal, hash[0]);
16473 hash[1] = hashIntAlt(chVal, hash[1]);
16474 }
16475 }
16476 }
16477
16478 return hash;
16479 };
16480
16481 styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
16482
16483 var styfn$4 = {};
16484
16485 styfn$4.appendFromJson = function (json) {
16486 var style = this;
16487
16488 for (var i = 0; i < json.length; i++) {
16489 var context = json[i];
16490 var selector = context.selector;
16491 var props = context.style || context.css;
16492 var names = Object.keys(props);
16493 style.selector(selector); // apply selector
16494
16495 for (var j = 0; j < names.length; j++) {
16496 var name = names[j];
16497 var value = props[name];
16498 style.css(name, value); // apply property
16499 }
16500 }
16501
16502 return style;
16503 }; // accessible cy.style() function
16504
16505
16506 styfn$4.fromJson = function (json) {
16507 var style = this;
16508 style.resetToDefault();
16509 style.appendFromJson(json);
16510 return style;
16511 }; // get json from cy.style() api
16512
16513
16514 styfn$4.json = function () {
16515 var json = [];
16516
16517 for (var i = this.defaultLength; i < this.length; i++) {
16518 var cxt = this[i];
16519 var selector = cxt.selector;
16520 var props = cxt.properties;
16521 var css = {};
16522
16523 for (var j = 0; j < props.length; j++) {
16524 var prop = props[j];
16525 css[prop.name] = prop.strValue;
16526 }
16527
16528 json.push({
16529 selector: !selector ? 'core' : selector.toString(),
16530 style: css
16531 });
16532 }
16533
16534 return json;
16535 };
16536
16537 var styfn$5 = {};
16538
16539 styfn$5.appendFromString = function (string) {
16540 var self = this;
16541 var style = this;
16542 var remaining = '' + string;
16543 var selAndBlockStr;
16544 var blockRem;
16545 var propAndValStr; // remove comments from the style string
16546
16547 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
16548
16549 function removeSelAndBlockFromRemaining() {
16550 // remove the parsed selector and block from the remaining text to parse
16551 if (remaining.length > selAndBlockStr.length) {
16552 remaining = remaining.substr(selAndBlockStr.length);
16553 } else {
16554 remaining = '';
16555 }
16556 }
16557
16558 function removePropAndValFromRem() {
16559 // remove the parsed property and value from the remaining block text to parse
16560 if (blockRem.length > propAndValStr.length) {
16561 blockRem = blockRem.substr(propAndValStr.length);
16562 } else {
16563 blockRem = '';
16564 }
16565 }
16566
16567 for (;;) {
16568 var nothingLeftToParse = remaining.match(/^\s*$/);
16569
16570 if (nothingLeftToParse) {
16571 break;
16572 }
16573
16574 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
16575
16576 if (!selAndBlock) {
16577 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
16578 break;
16579 }
16580
16581 selAndBlockStr = selAndBlock[0]; // parse the selector
16582
16583 var selectorStr = selAndBlock[1];
16584
16585 if (selectorStr !== 'core') {
16586 var selector = new Selector(selectorStr);
16587
16588 if (selector.invalid) {
16589 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
16590
16591 removeSelAndBlockFromRemaining();
16592 continue;
16593 }
16594 } // parse the block of properties and values
16595
16596
16597 var blockStr = selAndBlock[2];
16598 var invalidBlock = false;
16599 blockRem = blockStr;
16600 var props = [];
16601
16602 for (;;) {
16603 var _nothingLeftToParse = blockRem.match(/^\s*$/);
16604
16605 if (_nothingLeftToParse) {
16606 break;
16607 }
16608
16609 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
16610
16611 if (!propAndVal) {
16612 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
16613 invalidBlock = true;
16614 break;
16615 }
16616
16617 propAndValStr = propAndVal[0];
16618 var propStr = propAndVal[1];
16619 var valStr = propAndVal[2];
16620 var prop = self.properties[propStr];
16621
16622 if (!prop) {
16623 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
16624
16625 removePropAndValFromRem();
16626 continue;
16627 }
16628
16629 var parsedProp = style.parse(propStr, valStr);
16630
16631 if (!parsedProp) {
16632 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
16633
16634 removePropAndValFromRem();
16635 continue;
16636 }
16637
16638 props.push({
16639 name: propStr,
16640 val: valStr
16641 });
16642 removePropAndValFromRem();
16643 }
16644
16645 if (invalidBlock) {
16646 removeSelAndBlockFromRemaining();
16647 break;
16648 } // put the parsed block in the style
16649
16650
16651 style.selector(selectorStr);
16652
16653 for (var i = 0; i < props.length; i++) {
16654 var _prop = props[i];
16655 style.css(_prop.name, _prop.val);
16656 }
16657
16658 removeSelAndBlockFromRemaining();
16659 }
16660
16661 return style;
16662 };
16663
16664 styfn$5.fromString = function (string) {
16665 var style = this;
16666 style.resetToDefault();
16667 style.appendFromString(string);
16668 return style;
16669 };
16670
16671 var styfn$6 = {};
16672
16673 (function () {
16674 var number = number$1;
16675 var rgba = rgbaNoBackRefs;
16676 var hsla = hslaNoBackRefs;
16677 var hex3$1 = hex3;
16678 var hex6$1 = hex6;
16679
16680 var data = function data(prefix) {
16681 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16682 };
16683
16684 var mapData = function mapData(prefix) {
16685 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16686 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16687 };
16688
16689 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
16690
16691 styfn$6.types = {
16692 time: {
16693 number: true,
16694 min: 0,
16695 units: 's|ms',
16696 implicitUnits: 'ms'
16697 },
16698 percent: {
16699 number: true,
16700 min: 0,
16701 max: 100,
16702 units: '%',
16703 implicitUnits: '%'
16704 },
16705 percentages: {
16706 number: true,
16707 min: 0,
16708 max: 100,
16709 units: '%',
16710 implicitUnits: '%',
16711 multiple: true
16712 },
16713 zeroOneNumber: {
16714 number: true,
16715 min: 0,
16716 max: 1,
16717 unitless: true
16718 },
16719 zeroOneNumbers: {
16720 number: true,
16721 min: 0,
16722 max: 1,
16723 unitless: true,
16724 multiple: true
16725 },
16726 nOneOneNumber: {
16727 number: true,
16728 min: -1,
16729 max: 1,
16730 unitless: true
16731 },
16732 nonNegativeInt: {
16733 number: true,
16734 min: 0,
16735 integer: true,
16736 unitless: true
16737 },
16738 position: {
16739 enums: ['parent', 'origin']
16740 },
16741 nodeSize: {
16742 number: true,
16743 min: 0,
16744 enums: ['label']
16745 },
16746 number: {
16747 number: true,
16748 unitless: true
16749 },
16750 numbers: {
16751 number: true,
16752 unitless: true,
16753 multiple: true
16754 },
16755 positiveNumber: {
16756 number: true,
16757 unitless: true,
16758 min: 0,
16759 strictMin: true
16760 },
16761 size: {
16762 number: true,
16763 min: 0
16764 },
16765 bidirectionalSize: {
16766 number: true
16767 },
16768 // allows negative
16769 bidirectionalSizeMaybePercent: {
16770 number: true,
16771 allowPercent: true
16772 },
16773 // allows negative
16774 bidirectionalSizes: {
16775 number: true,
16776 multiple: true
16777 },
16778 // allows negative
16779 sizeMaybePercent: {
16780 number: true,
16781 min: 0,
16782 allowPercent: true
16783 },
16784 axisDirection: {
16785 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16786 },
16787 paddingRelativeTo: {
16788 enums: ['width', 'height', 'average', 'min', 'max']
16789 },
16790 bgWH: {
16791 number: true,
16792 min: 0,
16793 allowPercent: true,
16794 enums: ['auto'],
16795 multiple: true
16796 },
16797 bgPos: {
16798 number: true,
16799 allowPercent: true,
16800 multiple: true
16801 },
16802 bgRelativeTo: {
16803 enums: ['inner', 'include-padding'],
16804 multiple: true
16805 },
16806 bgRepeat: {
16807 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16808 multiple: true
16809 },
16810 bgFit: {
16811 enums: ['none', 'contain', 'cover'],
16812 multiple: true
16813 },
16814 bgCrossOrigin: {
16815 enums: ['anonymous', 'use-credentials'],
16816 multiple: true
16817 },
16818 bgClip: {
16819 enums: ['none', 'node'],
16820 multiple: true
16821 },
16822 color: {
16823 color: true
16824 },
16825 colors: {
16826 color: true,
16827 multiple: true
16828 },
16829 fill: {
16830 enums: ['solid', 'linear-gradient', 'radial-gradient']
16831 },
16832 bool: {
16833 enums: ['yes', 'no']
16834 },
16835 lineStyle: {
16836 enums: ['solid', 'dotted', 'dashed']
16837 },
16838 lineCap: {
16839 enums: ['butt', 'round', 'square']
16840 },
16841 borderStyle: {
16842 enums: ['solid', 'dotted', 'dashed', 'double']
16843 },
16844 curveStyle: {
16845 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16846 },
16847 fontFamily: {
16848 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16849 },
16850 fontStyle: {
16851 enums: ['italic', 'normal', 'oblique']
16852 },
16853 fontWeight: {
16854 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16855 },
16856 textDecoration: {
16857 enums: ['none', 'underline', 'overline', 'line-through']
16858 },
16859 textTransform: {
16860 enums: ['none', 'uppercase', 'lowercase']
16861 },
16862 textWrap: {
16863 enums: ['none', 'wrap', 'ellipsis']
16864 },
16865 textOverflowWrap: {
16866 enums: ['whitespace', 'anywhere']
16867 },
16868 textBackgroundShape: {
16869 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16870 },
16871 nodeShape: {
16872 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']
16873 },
16874 compoundIncludeLabels: {
16875 enums: ['include', 'exclude']
16876 },
16877 arrowShape: {
16878 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16879 },
16880 arrowFill: {
16881 enums: ['filled', 'hollow']
16882 },
16883 display: {
16884 enums: ['element', 'none']
16885 },
16886 visibility: {
16887 enums: ['hidden', 'visible']
16888 },
16889 zCompoundDepth: {
16890 enums: ['bottom', 'orphan', 'auto', 'top']
16891 },
16892 zIndexCompare: {
16893 enums: ['auto', 'manual']
16894 },
16895 valign: {
16896 enums: ['top', 'center', 'bottom']
16897 },
16898 halign: {
16899 enums: ['left', 'center', 'right']
16900 },
16901 justification: {
16902 enums: ['left', 'center', 'right', 'auto']
16903 },
16904 text: {
16905 string: true
16906 },
16907 data: {
16908 mapping: true,
16909 regex: data('data')
16910 },
16911 layoutData: {
16912 mapping: true,
16913 regex: data('layoutData')
16914 },
16915 scratch: {
16916 mapping: true,
16917 regex: data('scratch')
16918 },
16919 mapData: {
16920 mapping: true,
16921 regex: mapData('mapData')
16922 },
16923 mapLayoutData: {
16924 mapping: true,
16925 regex: mapData('mapLayoutData')
16926 },
16927 mapScratch: {
16928 mapping: true,
16929 regex: mapData('mapScratch')
16930 },
16931 fn: {
16932 mapping: true,
16933 fn: true
16934 },
16935 url: {
16936 regexes: urlRegexes,
16937 singleRegexMatchValue: true
16938 },
16939 urls: {
16940 regexes: urlRegexes,
16941 singleRegexMatchValue: true,
16942 multiple: true
16943 },
16944 propList: {
16945 propList: true
16946 },
16947 angle: {
16948 number: true,
16949 units: 'deg|rad',
16950 implicitUnits: 'rad'
16951 },
16952 textRotation: {
16953 number: true,
16954 units: 'deg|rad',
16955 implicitUnits: 'rad',
16956 enums: ['none', 'autorotate']
16957 },
16958 polygonPointList: {
16959 number: true,
16960 multiple: true,
16961 evenMultiple: true,
16962 min: -1,
16963 max: 1,
16964 unitless: true
16965 },
16966 edgeDistances: {
16967 enums: ['intersection', 'node-position']
16968 },
16969 edgeEndpoint: {
16970 number: true,
16971 multiple: true,
16972 units: '%|px|em|deg|rad',
16973 implicitUnits: 'px',
16974 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16975 singleEnum: true,
16976 validate: function validate(valArr, unitsArr) {
16977 switch (valArr.length) {
16978 case 2:
16979 // can be % or px only
16980 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16981
16982 case 1:
16983 // can be enum, deg, or rad only
16984 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16985
16986 default:
16987 return false;
16988 }
16989 }
16990 },
16991 easing: {
16992 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16993 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']
16994 },
16995 gradientDirection: {
16996 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']
16997 },
16998 boundsExpansion: {
16999 number: true,
17000 multiple: true,
17001 min: 0,
17002 validate: function validate(valArr) {
17003 var length = valArr.length;
17004 return length === 1 || length === 2 || length === 4;
17005 }
17006 }
17007 };
17008 var diff = {
17009 zeroNonZero: function zeroNonZero(val1, val2) {
17010 if ((val1 == null || val2 == null) && val1 !== val2) {
17011 return true; // null cases could represent any value
17012 }
17013
17014 if (val1 == 0 && val2 != 0) {
17015 return true;
17016 } else if (val1 != 0 && val2 == 0) {
17017 return true;
17018 } else {
17019 return false;
17020 }
17021 },
17022 any: function any(val1, val2) {
17023 return val1 != val2;
17024 },
17025 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
17026 var empty1 = emptyString(str1);
17027 var empty2 = emptyString(str2);
17028 return empty1 && !empty2 || !empty1 && empty2;
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 triggersZOrder: diff.emptyNonEmpty
17041 }, {
17042 name: 'text-rotation',
17043 type: t.textRotation,
17044 triggersBounds: diff.any
17045 }, {
17046 name: 'text-margin-x',
17047 type: t.bidirectionalSize,
17048 triggersBounds: diff.any
17049 }, {
17050 name: 'text-margin-y',
17051 type: t.bidirectionalSize,
17052 triggersBounds: diff.any
17053 }];
17054 var sourceLabel = [{
17055 name: 'source-label',
17056 type: t.text,
17057 triggersBounds: diff.any
17058 }, {
17059 name: 'source-text-rotation',
17060 type: t.textRotation,
17061 triggersBounds: diff.any
17062 }, {
17063 name: 'source-text-margin-x',
17064 type: t.bidirectionalSize,
17065 triggersBounds: diff.any
17066 }, {
17067 name: 'source-text-margin-y',
17068 type: t.bidirectionalSize,
17069 triggersBounds: diff.any
17070 }, {
17071 name: 'source-text-offset',
17072 type: t.size,
17073 triggersBounds: diff.any
17074 }];
17075 var targetLabel = [{
17076 name: 'target-label',
17077 type: t.text,
17078 triggersBounds: diff.any
17079 }, {
17080 name: 'target-text-rotation',
17081 type: t.textRotation,
17082 triggersBounds: diff.any
17083 }, {
17084 name: 'target-text-margin-x',
17085 type: t.bidirectionalSize,
17086 triggersBounds: diff.any
17087 }, {
17088 name: 'target-text-margin-y',
17089 type: t.bidirectionalSize,
17090 triggersBounds: diff.any
17091 }, {
17092 name: 'target-text-offset',
17093 type: t.size,
17094 triggersBounds: diff.any
17095 }];
17096 var labelDimensions = [{
17097 name: 'font-family',
17098 type: t.fontFamily,
17099 triggersBounds: diff.any
17100 }, {
17101 name: 'font-style',
17102 type: t.fontStyle,
17103 triggersBounds: diff.any
17104 }, {
17105 name: 'font-weight',
17106 type: t.fontWeight,
17107 triggersBounds: diff.any
17108 }, {
17109 name: 'font-size',
17110 type: t.size,
17111 triggersBounds: diff.any
17112 }, {
17113 name: 'text-transform',
17114 type: t.textTransform,
17115 triggersBounds: diff.any
17116 }, {
17117 name: 'text-wrap',
17118 type: t.textWrap,
17119 triggersBounds: diff.any
17120 }, {
17121 name: 'text-overflow-wrap',
17122 type: t.textOverflowWrap,
17123 triggersBounds: diff.any
17124 }, {
17125 name: 'text-max-width',
17126 type: t.size,
17127 triggersBounds: diff.any
17128 }, {
17129 name: 'text-outline-width',
17130 type: t.size,
17131 triggersBounds: diff.any
17132 }, {
17133 name: 'line-height',
17134 type: t.positiveNumber,
17135 triggersBounds: diff.any
17136 }];
17137 var commonLabel = [{
17138 name: 'text-valign',
17139 type: t.valign,
17140 triggersBounds: diff.any
17141 }, {
17142 name: 'text-halign',
17143 type: t.halign,
17144 triggersBounds: diff.any
17145 }, {
17146 name: 'color',
17147 type: t.color
17148 }, {
17149 name: 'text-outline-color',
17150 type: t.color
17151 }, {
17152 name: 'text-outline-opacity',
17153 type: t.zeroOneNumber
17154 }, {
17155 name: 'text-background-color',
17156 type: t.color
17157 }, {
17158 name: 'text-background-opacity',
17159 type: t.zeroOneNumber
17160 }, {
17161 name: 'text-background-padding',
17162 type: t.size,
17163 triggersBounds: diff.any
17164 }, {
17165 name: 'text-border-opacity',
17166 type: t.zeroOneNumber
17167 }, {
17168 name: 'text-border-color',
17169 type: t.color
17170 }, {
17171 name: 'text-border-width',
17172 type: t.size,
17173 triggersBounds: diff.any
17174 }, {
17175 name: 'text-border-style',
17176 type: t.borderStyle,
17177 triggersBounds: diff.any
17178 }, {
17179 name: 'text-background-shape',
17180 type: t.textBackgroundShape,
17181 triggersBounds: diff.any
17182 }, {
17183 name: 'text-justification',
17184 type: t.justification
17185 }];
17186 var behavior = [{
17187 name: 'events',
17188 type: t.bool
17189 }, {
17190 name: 'text-events',
17191 type: t.bool
17192 }];
17193 var visibility = [{
17194 name: 'display',
17195 type: t.display,
17196 triggersZOrder: diff.any,
17197 triggersBounds: diff.any,
17198 triggersBoundsOfParallelBeziers: true
17199 }, {
17200 name: 'visibility',
17201 type: t.visibility,
17202 triggersZOrder: diff.any
17203 }, {
17204 name: 'opacity',
17205 type: t.zeroOneNumber,
17206 triggersZOrder: diff.zeroNonZero
17207 }, {
17208 name: 'text-opacity',
17209 type: t.zeroOneNumber
17210 }, {
17211 name: 'min-zoomed-font-size',
17212 type: t.size
17213 }, {
17214 name: 'z-compound-depth',
17215 type: t.zCompoundDepth,
17216 triggersZOrder: diff.any
17217 }, {
17218 name: 'z-index-compare',
17219 type: t.zIndexCompare,
17220 triggersZOrder: diff.any
17221 }, {
17222 name: 'z-index',
17223 type: t.nonNegativeInt,
17224 triggersZOrder: diff.any
17225 }];
17226 var overlay = [{
17227 name: 'overlay-padding',
17228 type: t.size,
17229 triggersBounds: diff.any
17230 }, {
17231 name: 'overlay-color',
17232 type: t.color
17233 }, {
17234 name: 'overlay-opacity',
17235 type: t.zeroOneNumber,
17236 triggersBounds: diff.zeroNonZero
17237 }];
17238 var transition = [{
17239 name: 'transition-property',
17240 type: t.propList
17241 }, {
17242 name: 'transition-duration',
17243 type: t.time
17244 }, {
17245 name: 'transition-delay',
17246 type: t.time
17247 }, {
17248 name: 'transition-timing-function',
17249 type: t.easing
17250 }];
17251
17252 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
17253 if (parsedProp.value === 'label') {
17254 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
17255 } else {
17256 return parsedProp.pfValue;
17257 }
17258 };
17259
17260 var nodeBody = [{
17261 name: 'height',
17262 type: t.nodeSize,
17263 triggersBounds: diff.any,
17264 hashOverride: nodeSizeHashOverride
17265 }, {
17266 name: 'width',
17267 type: t.nodeSize,
17268 triggersBounds: diff.any,
17269 hashOverride: nodeSizeHashOverride
17270 }, {
17271 name: 'shape',
17272 type: t.nodeShape,
17273 triggersBounds: diff.any
17274 }, {
17275 name: 'shape-polygon-points',
17276 type: t.polygonPointList,
17277 triggersBounds: diff.any
17278 }, {
17279 name: 'background-color',
17280 type: t.color
17281 }, {
17282 name: 'background-fill',
17283 type: t.fill
17284 }, {
17285 name: 'background-opacity',
17286 type: t.zeroOneNumber
17287 }, {
17288 name: 'background-blacken',
17289 type: t.nOneOneNumber
17290 }, {
17291 name: 'background-gradient-stop-colors',
17292 type: t.colors
17293 }, {
17294 name: 'background-gradient-stop-positions',
17295 type: t.percentages
17296 }, {
17297 name: 'background-gradient-direction',
17298 type: t.gradientDirection
17299 }, {
17300 name: 'padding',
17301 type: t.sizeMaybePercent,
17302 triggersBounds: diff.any
17303 }, {
17304 name: 'padding-relative-to',
17305 type: t.paddingRelativeTo,
17306 triggersBounds: diff.any
17307 }, {
17308 name: 'bounds-expansion',
17309 type: t.boundsExpansion,
17310 triggersBounds: diff.any
17311 }];
17312 var nodeBorder = [{
17313 name: 'border-color',
17314 type: t.color
17315 }, {
17316 name: 'border-opacity',
17317 type: t.zeroOneNumber
17318 }, {
17319 name: 'border-width',
17320 type: t.size,
17321 triggersBounds: diff.any
17322 }, {
17323 name: 'border-style',
17324 type: t.borderStyle
17325 }];
17326 var backgroundImage = [{
17327 name: 'background-image',
17328 type: t.urls
17329 }, {
17330 name: 'background-image-crossorigin',
17331 type: t.bgCrossOrigin
17332 }, {
17333 name: 'background-image-opacity',
17334 type: t.zeroOneNumbers
17335 }, {
17336 name: 'background-position-x',
17337 type: t.bgPos
17338 }, {
17339 name: 'background-position-y',
17340 type: t.bgPos
17341 }, {
17342 name: 'background-width-relative-to',
17343 type: t.bgRelativeTo
17344 }, {
17345 name: 'background-height-relative-to',
17346 type: t.bgRelativeTo
17347 }, {
17348 name: 'background-repeat',
17349 type: t.bgRepeat
17350 }, {
17351 name: 'background-fit',
17352 type: t.bgFit
17353 }, {
17354 name: 'background-clip',
17355 type: t.bgClip
17356 }, {
17357 name: 'background-width',
17358 type: t.bgWH
17359 }, {
17360 name: 'background-height',
17361 type: t.bgWH
17362 }, {
17363 name: 'background-offset-x',
17364 type: t.bgPos
17365 }, {
17366 name: 'background-offset-y',
17367 type: t.bgPos
17368 }];
17369 var compound = [{
17370 name: 'position',
17371 type: t.position,
17372 triggersBounds: diff.any
17373 }, {
17374 name: 'compound-sizing-wrt-labels',
17375 type: t.compoundIncludeLabels,
17376 triggersBounds: diff.any
17377 }, {
17378 name: 'min-width',
17379 type: t.size,
17380 triggersBounds: diff.any
17381 }, {
17382 name: 'min-width-bias-left',
17383 type: t.sizeMaybePercent,
17384 triggersBounds: diff.any
17385 }, {
17386 name: 'min-width-bias-right',
17387 type: t.sizeMaybePercent,
17388 triggersBounds: diff.any
17389 }, {
17390 name: 'min-height',
17391 type: t.size,
17392 triggersBounds: diff.any
17393 }, {
17394 name: 'min-height-bias-top',
17395 type: t.sizeMaybePercent,
17396 triggersBounds: diff.any
17397 }, {
17398 name: 'min-height-bias-bottom',
17399 type: t.sizeMaybePercent,
17400 triggersBounds: diff.any
17401 }];
17402 var edgeLine = [{
17403 name: 'line-style',
17404 type: t.lineStyle
17405 }, {
17406 name: 'line-color',
17407 type: t.color
17408 }, {
17409 name: 'line-fill',
17410 type: t.fill
17411 }, {
17412 name: 'line-cap',
17413 type: t.lineCap
17414 }, {
17415 name: 'line-dash-pattern',
17416 type: t.numbers
17417 }, {
17418 name: 'line-dash-offset',
17419 type: t.number
17420 }, {
17421 name: 'line-gradient-stop-colors',
17422 type: t.colors
17423 }, {
17424 name: 'line-gradient-stop-positions',
17425 type: t.percentages
17426 }, {
17427 name: 'curve-style',
17428 type: t.curveStyle,
17429 triggersBounds: diff.any,
17430 triggersBoundsOfParallelBeziers: true
17431 }, {
17432 name: 'haystack-radius',
17433 type: t.zeroOneNumber,
17434 triggersBounds: diff.any
17435 }, {
17436 name: 'source-endpoint',
17437 type: t.edgeEndpoint,
17438 triggersBounds: diff.any
17439 }, {
17440 name: 'target-endpoint',
17441 type: t.edgeEndpoint,
17442 triggersBounds: diff.any
17443 }, {
17444 name: 'control-point-step-size',
17445 type: t.size,
17446 triggersBounds: diff.any
17447 }, {
17448 name: 'control-point-distances',
17449 type: t.bidirectionalSizes,
17450 triggersBounds: diff.any
17451 }, {
17452 name: 'control-point-weights',
17453 type: t.numbers,
17454 triggersBounds: diff.any
17455 }, {
17456 name: 'segment-distances',
17457 type: t.bidirectionalSizes,
17458 triggersBounds: diff.any
17459 }, {
17460 name: 'segment-weights',
17461 type: t.numbers,
17462 triggersBounds: diff.any
17463 }, {
17464 name: 'taxi-turn',
17465 type: t.bidirectionalSizeMaybePercent,
17466 triggersBounds: diff.any
17467 }, {
17468 name: 'taxi-turn-min-distance',
17469 type: t.size,
17470 triggersBounds: diff.any
17471 }, {
17472 name: 'taxi-direction',
17473 type: t.axisDirection,
17474 triggersBounds: diff.any
17475 }, {
17476 name: 'edge-distances',
17477 type: t.edgeDistances,
17478 triggersBounds: diff.any
17479 }, {
17480 name: 'arrow-scale',
17481 type: t.positiveNumber,
17482 triggersBounds: diff.any
17483 }, {
17484 name: 'loop-direction',
17485 type: t.angle,
17486 triggersBounds: diff.any
17487 }, {
17488 name: 'loop-sweep',
17489 type: t.angle,
17490 triggersBounds: diff.any
17491 }, {
17492 name: 'source-distance-from-node',
17493 type: t.size,
17494 triggersBounds: diff.any
17495 }, {
17496 name: 'target-distance-from-node',
17497 type: t.size,
17498 triggersBounds: diff.any
17499 }];
17500 var ghost = [{
17501 name: 'ghost',
17502 type: t.bool,
17503 triggersBounds: diff.any
17504 }, {
17505 name: 'ghost-offset-x',
17506 type: t.bidirectionalSize,
17507 triggersBounds: diff.any
17508 }, {
17509 name: 'ghost-offset-y',
17510 type: t.bidirectionalSize,
17511 triggersBounds: diff.any
17512 }, {
17513 name: 'ghost-opacity',
17514 type: t.zeroOneNumber
17515 }];
17516 var core = [{
17517 name: 'selection-box-color',
17518 type: t.color
17519 }, {
17520 name: 'selection-box-opacity',
17521 type: t.zeroOneNumber
17522 }, {
17523 name: 'selection-box-border-color',
17524 type: t.color
17525 }, {
17526 name: 'selection-box-border-width',
17527 type: t.size
17528 }, {
17529 name: 'active-bg-color',
17530 type: t.color
17531 }, {
17532 name: 'active-bg-opacity',
17533 type: t.zeroOneNumber
17534 }, {
17535 name: 'active-bg-size',
17536 type: t.size
17537 }, {
17538 name: 'outside-texture-bg-color',
17539 type: t.color
17540 }, {
17541 name: 'outside-texture-bg-opacity',
17542 type: t.zeroOneNumber
17543 }]; // pie backgrounds for nodes
17544
17545 var pie = [];
17546 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
17547
17548 pie.push({
17549 name: 'pie-size',
17550 type: t.sizeMaybePercent
17551 });
17552
17553 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17554 pie.push({
17555 name: 'pie-' + i + '-background-color',
17556 type: t.color
17557 });
17558 pie.push({
17559 name: 'pie-' + i + '-background-size',
17560 type: t.percent
17561 });
17562 pie.push({
17563 name: 'pie-' + i + '-background-opacity',
17564 type: t.zeroOneNumber
17565 });
17566 } // edge arrows
17567
17568
17569 var edgeArrow = [];
17570 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
17571 [{
17572 name: 'arrow-shape',
17573 type: t.arrowShape,
17574 triggersBounds: diff.any
17575 }, {
17576 name: 'arrow-color',
17577 type: t.color
17578 }, {
17579 name: 'arrow-fill',
17580 type: t.arrowFill
17581 }].forEach(function (prop) {
17582 arrowPrefixes.forEach(function (prefix) {
17583 var name = prefix + '-' + prop.name;
17584 var type = prop.type,
17585 triggersBounds = prop.triggersBounds;
17586 edgeArrow.push({
17587 name: name,
17588 type: type,
17589 triggersBounds: triggersBounds
17590 });
17591 });
17592 }, {});
17593 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
17594 var propGroups = styfn$6.propertyGroups = {
17595 // common to all eles
17596 behavior: behavior,
17597 transition: transition,
17598 visibility: visibility,
17599 overlay: overlay,
17600 ghost: ghost,
17601 // labels
17602 commonLabel: commonLabel,
17603 labelDimensions: labelDimensions,
17604 mainLabel: mainLabel,
17605 sourceLabel: sourceLabel,
17606 targetLabel: targetLabel,
17607 // node props
17608 nodeBody: nodeBody,
17609 nodeBorder: nodeBorder,
17610 backgroundImage: backgroundImage,
17611 pie: pie,
17612 compound: compound,
17613 // edge props
17614 edgeLine: edgeLine,
17615 edgeArrow: edgeArrow,
17616 core: core
17617 };
17618 var propGroupNames = styfn$6.propertyGroupNames = {};
17619 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
17620 propGroupKeys.forEach(function (key) {
17621 propGroupNames[key] = propGroups[key].map(function (prop) {
17622 return prop.name;
17623 });
17624 propGroups[key].forEach(function (prop) {
17625 return prop.groupKey = key;
17626 });
17627 }); // define aliases
17628
17629 var aliases = styfn$6.aliases = [{
17630 name: 'content',
17631 pointsTo: 'label'
17632 }, {
17633 name: 'control-point-distance',
17634 pointsTo: 'control-point-distances'
17635 }, {
17636 name: 'control-point-weight',
17637 pointsTo: 'control-point-weights'
17638 }, {
17639 name: 'edge-text-rotation',
17640 pointsTo: 'text-rotation'
17641 }, {
17642 name: 'padding-left',
17643 pointsTo: 'padding'
17644 }, {
17645 name: 'padding-right',
17646 pointsTo: 'padding'
17647 }, {
17648 name: 'padding-top',
17649 pointsTo: 'padding'
17650 }, {
17651 name: 'padding-bottom',
17652 pointsTo: 'padding'
17653 }]; // list of property names
17654
17655 styfn$6.propertyNames = props.map(function (p) {
17656 return p.name;
17657 }); // allow access of properties by name ( e.g. style.properties.height )
17658
17659 for (var _i = 0; _i < props.length; _i++) {
17660 var prop = props[_i];
17661 props[prop.name] = prop; // allow lookup by name
17662 } // map aliases
17663
17664
17665 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17666 var alias = aliases[_i2];
17667 var pointsToProp = props[alias.pointsTo];
17668 var aliasProp = {
17669 name: alias.name,
17670 alias: true,
17671 pointsTo: pointsToProp
17672 }; // add alias prop for parsing
17673
17674 props.push(aliasProp);
17675 props[alias.name] = aliasProp; // allow lookup by name
17676 }
17677 })();
17678
17679 styfn$6.getDefaultProperty = function (name) {
17680 return this.getDefaultProperties()[name];
17681 };
17682
17683 styfn$6.getDefaultProperties = function () {
17684 var _p = this._private;
17685
17686 if (_p.defaultProperties != null) {
17687 return _p.defaultProperties;
17688 }
17689
17690 var rawProps = extend({
17691 // core props
17692 'selection-box-color': '#ddd',
17693 'selection-box-opacity': 0.65,
17694 'selection-box-border-color': '#aaa',
17695 'selection-box-border-width': 1,
17696 'active-bg-color': 'black',
17697 'active-bg-opacity': 0.15,
17698 'active-bg-size': 30,
17699 'outside-texture-bg-color': '#000',
17700 'outside-texture-bg-opacity': 0.125,
17701 // common node/edge props
17702 'events': 'yes',
17703 'text-events': 'no',
17704 'text-valign': 'top',
17705 'text-halign': 'center',
17706 'text-justification': 'auto',
17707 'line-height': 1,
17708 'color': '#000',
17709 'text-outline-color': '#000',
17710 'text-outline-width': 0,
17711 'text-outline-opacity': 1,
17712 'text-opacity': 1,
17713 'text-decoration': 'none',
17714 'text-transform': 'none',
17715 'text-wrap': 'none',
17716 'text-overflow-wrap': 'whitespace',
17717 'text-max-width': 9999,
17718 'text-background-color': '#000',
17719 'text-background-opacity': 0,
17720 'text-background-shape': 'rectangle',
17721 'text-background-padding': 0,
17722 'text-border-opacity': 0,
17723 'text-border-width': 0,
17724 'text-border-style': 'solid',
17725 'text-border-color': '#000',
17726 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17727 'font-style': 'normal',
17728 'font-weight': 'normal',
17729 'font-size': 16,
17730 'min-zoomed-font-size': 0,
17731 'text-rotation': 'none',
17732 'source-text-rotation': 'none',
17733 'target-text-rotation': 'none',
17734 'visibility': 'visible',
17735 'display': 'element',
17736 'opacity': 1,
17737 'z-compound-depth': 'auto',
17738 'z-index-compare': 'auto',
17739 'z-index': 0,
17740 'label': '',
17741 'text-margin-x': 0,
17742 'text-margin-y': 0,
17743 'source-label': '',
17744 'source-text-offset': 0,
17745 'source-text-margin-x': 0,
17746 'source-text-margin-y': 0,
17747 'target-label': '',
17748 'target-text-offset': 0,
17749 'target-text-margin-x': 0,
17750 'target-text-margin-y': 0,
17751 'overlay-opacity': 0,
17752 'overlay-color': '#000',
17753 'overlay-padding': 10,
17754 'transition-property': 'none',
17755 'transition-duration': 0,
17756 'transition-delay': 0,
17757 'transition-timing-function': 'linear',
17758 // node props
17759 'background-blacken': 0,
17760 'background-color': '#999',
17761 'background-fill': 'solid',
17762 'background-opacity': 1,
17763 'background-image': 'none',
17764 'background-image-crossorigin': 'anonymous',
17765 'background-image-opacity': 1,
17766 'background-position-x': '50%',
17767 'background-position-y': '50%',
17768 'background-offset-x': 0,
17769 'background-offset-y': 0,
17770 'background-width-relative-to': 'include-padding',
17771 'background-height-relative-to': 'include-padding',
17772 'background-repeat': 'no-repeat',
17773 'background-fit': 'none',
17774 'background-clip': 'node',
17775 'background-width': 'auto',
17776 'background-height': 'auto',
17777 'border-color': '#000',
17778 'border-opacity': 1,
17779 'border-width': 0,
17780 'border-style': 'solid',
17781 'height': 30,
17782 'width': 30,
17783 'shape': 'ellipse',
17784 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17785 'bounds-expansion': 0,
17786 // node gradient
17787 'background-gradient-direction': 'to-bottom',
17788 'background-gradient-stop-colors': '#999',
17789 'background-gradient-stop-positions': '0%',
17790 // ghost props
17791 'ghost': 'no',
17792 'ghost-offset-y': 0,
17793 'ghost-offset-x': 0,
17794 'ghost-opacity': 0,
17795 // compound props
17796 'padding': 0,
17797 'padding-relative-to': 'width',
17798 'position': 'origin',
17799 'compound-sizing-wrt-labels': 'include',
17800 'min-width': 0,
17801 'min-width-bias-left': 0,
17802 'min-width-bias-right': 0,
17803 'min-height': 0,
17804 'min-height-bias-top': 0,
17805 'min-height-bias-bottom': 0
17806 }, {
17807 // node pie bg
17808 'pie-size': '100%'
17809 }, [{
17810 name: 'pie-{{i}}-background-color',
17811 value: 'black'
17812 }, {
17813 name: 'pie-{{i}}-background-size',
17814 value: '0%'
17815 }, {
17816 name: 'pie-{{i}}-background-opacity',
17817 value: 1
17818 }].reduce(function (css, prop) {
17819 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17820 var name = prop.name.replace('{{i}}', i);
17821 var val = prop.value;
17822 css[name] = val;
17823 }
17824
17825 return css;
17826 }, {}), {
17827 // edge props
17828 'line-style': 'solid',
17829 'line-color': '#999',
17830 'line-fill': 'solid',
17831 'line-cap': 'butt',
17832 'line-gradient-stop-colors': '#999',
17833 'line-gradient-stop-positions': '0%',
17834 'control-point-step-size': 40,
17835 'control-point-weights': 0.5,
17836 'segment-weights': 0.5,
17837 'segment-distances': 20,
17838 'taxi-turn': '50%',
17839 'taxi-turn-min-distance': 10,
17840 'taxi-direction': 'auto',
17841 'edge-distances': 'intersection',
17842 'curve-style': 'haystack',
17843 'haystack-radius': 0,
17844 'arrow-scale': 1,
17845 'loop-direction': '-45deg',
17846 'loop-sweep': '-90deg',
17847 'source-distance-from-node': 0,
17848 'target-distance-from-node': 0,
17849 'source-endpoint': 'outside-to-node',
17850 'target-endpoint': 'outside-to-node',
17851 'line-dash-pattern': [6, 3],
17852 'line-dash-offset': 0
17853 }, [{
17854 name: 'arrow-shape',
17855 value: 'none'
17856 }, {
17857 name: 'arrow-color',
17858 value: '#999'
17859 }, {
17860 name: 'arrow-fill',
17861 value: 'filled'
17862 }].reduce(function (css, prop) {
17863 styfn$6.arrowPrefixes.forEach(function (prefix) {
17864 var name = prefix + '-' + prop.name;
17865 var val = prop.value;
17866 css[name] = val;
17867 });
17868 return css;
17869 }, {}));
17870 var parsedProps = {};
17871
17872 for (var i = 0; i < this.properties.length; i++) {
17873 var prop = this.properties[i];
17874
17875 if (prop.pointsTo) {
17876 continue;
17877 }
17878
17879 var name = prop.name;
17880 var val = rawProps[name];
17881 var parsedProp = this.parse(name, val);
17882 parsedProps[name] = parsedProp;
17883 }
17884
17885 _p.defaultProperties = parsedProps;
17886 return _p.defaultProperties;
17887 };
17888
17889 styfn$6.addDefaultStylesheet = function () {
17890 this.selector(':parent').css({
17891 'shape': 'rectangle',
17892 'padding': 10,
17893 'background-color': '#eee',
17894 'border-color': '#ccc',
17895 'border-width': 1
17896 }).selector('edge').css({
17897 'width': 3
17898 }).selector(':loop').css({
17899 'curve-style': 'bezier'
17900 }).selector('edge:compound').css({
17901 'curve-style': 'bezier',
17902 'source-endpoint': 'outside-to-line',
17903 'target-endpoint': 'outside-to-line'
17904 }).selector(':selected').css({
17905 'background-color': '#0169D9',
17906 'line-color': '#0169D9',
17907 'source-arrow-color': '#0169D9',
17908 'target-arrow-color': '#0169D9',
17909 'mid-source-arrow-color': '#0169D9',
17910 'mid-target-arrow-color': '#0169D9'
17911 }).selector(':parent:selected').css({
17912 'background-color': '#CCE1F9',
17913 'border-color': '#aec8e5'
17914 }).selector(':active').css({
17915 'overlay-color': 'black',
17916 'overlay-padding': 10,
17917 'overlay-opacity': 0.25
17918 });
17919 this.defaultLength = this.length;
17920 };
17921
17922 var styfn$7 = {}; // a caching layer for property parsing
17923
17924 styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17925 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17926
17927 if (fn(value)) {
17928 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17929 }
17930
17931 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17932 var bypassKey = propIsBypass ? 't' : 'f';
17933 var valueKey = '' + value;
17934 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17935 var propCache = self.propCache = self.propCache || [];
17936 var ret;
17937
17938 if (!(ret = propCache[argHash])) {
17939 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17940 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17941 // - mappings can't be shared b/c mappings are per-element
17942
17943
17944 if (propIsBypass || propIsFlat === 'mapping') {
17945 // need a copy since props are mutated later in their lifecycles
17946 ret = copy(ret);
17947
17948 if (ret) {
17949 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17950 }
17951 }
17952
17953 return ret;
17954 };
17955
17956 styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17957 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17958
17959 if (!prop && value != null) {
17960 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17961 }
17962
17963 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17964 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17965 }
17966
17967 return prop;
17968 }; // parse a property; return null on invalid; return parsed property otherwise
17969 // fields :
17970 // - name : the name of the property
17971 // - value : the parsed, native-typed value of the property
17972 // - strValue : a string value that represents the property value in valid css
17973 // - bypass : true iff the property is a bypass property
17974
17975
17976 styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17977 var self = this;
17978 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17979
17980 var property = self.properties[name];
17981 var passedValue = value;
17982 var types = self.types;
17983
17984 if (!property) {
17985 return null;
17986 } // return null on property of unknown name
17987
17988
17989 if (value === undefined) {
17990 return null;
17991 } // can't assign undefined
17992 // the property may be an alias
17993
17994
17995 if (property.alias) {
17996 property = property.pointsTo;
17997 name = property.name;
17998 }
17999
18000 var valueIsString = string(value);
18001
18002 if (valueIsString) {
18003 // trim the value to make parsing easier
18004 value = value.trim();
18005 }
18006
18007 var type = property.type;
18008
18009 if (!type) {
18010 return null;
18011 } // no type, no luck
18012 // check if bypass is null or empty string (i.e. indication to delete bypass property)
18013
18014
18015 if (propIsBypass && (value === '' || value === null)) {
18016 return {
18017 name: name,
18018 value: value,
18019 bypass: true,
18020 deleteBypass: true
18021 };
18022 } // check if value is a function used as a mapper
18023
18024
18025 if (fn(value)) {
18026 return {
18027 name: name,
18028 value: value,
18029 strValue: 'fn',
18030 mapped: types.fn,
18031 bypass: propIsBypass
18032 };
18033 } // check if value is mapped
18034
18035
18036 var data, mapData;
18037
18038 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))) {
18039 if (propIsBypass) {
18040 return false;
18041 } // mappers not allowed in bypass
18042
18043
18044 var mapped = types.data;
18045 return {
18046 name: name,
18047 value: data,
18048 strValue: '' + value,
18049 mapped: mapped,
18050 field: data[1],
18051 bypass: propIsBypass
18052 };
18053 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
18054 if (propIsBypass) {
18055 return false;
18056 } // mappers not allowed in bypass
18057
18058
18059 if (type.multiple) {
18060 return false;
18061 } // impossible to map to num
18062
18063
18064 var _mapped = types.mapData; // we can map only if the type is a colour or a number
18065
18066 if (!(type.color || type.number)) {
18067 return false;
18068 }
18069
18070 var valueMin = this.parse(name, mapData[4]); // parse to validate
18071
18072 if (!valueMin || valueMin.mapped) {
18073 return false;
18074 } // can't be invalid or mapped
18075
18076
18077 var valueMax = this.parse(name, mapData[5]); // parse to validate
18078
18079 if (!valueMax || valueMax.mapped) {
18080 return false;
18081 } // can't be invalid or mapped
18082 // check if valueMin and valueMax are the same
18083
18084
18085 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
18086 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
18087 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
18088 } else if (type.color) {
18089 var c1 = valueMin.value;
18090 var c2 = valueMax.value;
18091 var same = c1[0] === c2[0] // red
18092 && c1[1] === c2[1] // green
18093 && c1[2] === c2[2] // blue
18094 && ( // optional alpha
18095 c1[3] === c2[3] // same alpha outright
18096 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
18097 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
18098 );
18099
18100 if (same) {
18101 return false;
18102 } // can't make a mapper without a range
18103
18104 }
18105
18106 return {
18107 name: name,
18108 value: mapData,
18109 strValue: '' + value,
18110 mapped: _mapped,
18111 field: mapData[1],
18112 fieldMin: parseFloat(mapData[2]),
18113 // min & max are numeric
18114 fieldMax: parseFloat(mapData[3]),
18115 valueMin: valueMin.value,
18116 valueMax: valueMax.value,
18117 bypass: propIsBypass
18118 };
18119 }
18120
18121 if (type.multiple && propIsFlat !== 'multiple') {
18122 var vals;
18123
18124 if (valueIsString) {
18125 vals = value.split(/\s+/);
18126 } else if (array(value)) {
18127 vals = value;
18128 } else {
18129 vals = [value];
18130 }
18131
18132 if (type.evenMultiple && vals.length % 2 !== 0) {
18133 return null;
18134 }
18135
18136 var valArr = [];
18137 var unitsArr = [];
18138 var pfValArr = [];
18139 var strVal = '';
18140 var hasEnum = false;
18141
18142 for (var i = 0; i < vals.length; i++) {
18143 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
18144 hasEnum = hasEnum || string(p.value);
18145 valArr.push(p.value);
18146 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
18147 unitsArr.push(p.units);
18148 strVal += (i > 0 ? ' ' : '') + p.strValue;
18149 }
18150
18151 if (type.validate && !type.validate(valArr, unitsArr)) {
18152 return null;
18153 }
18154
18155 if (type.singleEnum && hasEnum) {
18156 if (valArr.length === 1 && string(valArr[0])) {
18157 return {
18158 name: name,
18159 value: valArr[0],
18160 strValue: valArr[0],
18161 bypass: propIsBypass
18162 };
18163 } else {
18164 return null;
18165 }
18166 }
18167
18168 return {
18169 name: name,
18170 value: valArr,
18171 pfValue: pfValArr,
18172 strValue: strVal,
18173 bypass: propIsBypass,
18174 units: unitsArr
18175 };
18176 } // several types also allow enums
18177
18178
18179 var checkEnums = function checkEnums() {
18180 for (var _i = 0; _i < type.enums.length; _i++) {
18181 var en = type.enums[_i];
18182
18183 if (en === value) {
18184 return {
18185 name: name,
18186 value: value,
18187 strValue: '' + value,
18188 bypass: propIsBypass
18189 };
18190 }
18191 }
18192
18193 return null;
18194 }; // check the type and return the appropriate object
18195
18196
18197 if (type.number) {
18198 var units;
18199 var implicitUnits = 'px'; // not set => px
18200
18201 if (type.units) {
18202 // use specified units if set
18203 units = type.units;
18204 }
18205
18206 if (type.implicitUnits) {
18207 implicitUnits = type.implicitUnits;
18208 }
18209
18210 if (!type.unitless) {
18211 if (valueIsString) {
18212 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
18213
18214 if (units) {
18215 unitsRegex = units;
18216 } // only allow explicit units if so set
18217
18218
18219 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
18220
18221 if (match) {
18222 value = match[1];
18223 units = match[2] || implicitUnits;
18224 }
18225 } else if (!units || type.implicitUnits) {
18226 units = implicitUnits; // implicitly px if unspecified
18227 }
18228 }
18229
18230 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
18231
18232 if (isNaN(value) && type.enums === undefined) {
18233 return null;
18234 } // check if this number type also accepts special keywords in place of numbers
18235 // (i.e. `left`, `auto`, etc)
18236
18237
18238 if (isNaN(value) && type.enums !== undefined) {
18239 value = passedValue;
18240 return checkEnums();
18241 } // check if value must be an integer
18242
18243
18244 if (type.integer && !integer(value)) {
18245 return null;
18246 } // check value is within range
18247
18248
18249 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
18250 return null;
18251 }
18252
18253 var ret = {
18254 name: name,
18255 value: value,
18256 strValue: '' + value + (units ? units : ''),
18257 units: units,
18258 bypass: propIsBypass
18259 }; // normalise value in pixels
18260
18261 if (type.unitless || units !== 'px' && units !== 'em') {
18262 ret.pfValue = value;
18263 } else {
18264 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
18265 } // normalise value in ms
18266
18267
18268 if (units === 'ms' || units === 's') {
18269 ret.pfValue = units === 'ms' ? value : 1000 * value;
18270 } // normalise value in rad
18271
18272
18273 if (units === 'deg' || units === 'rad') {
18274 ret.pfValue = units === 'rad' ? value : deg2rad(value);
18275 } // normalize value in %
18276
18277
18278 if (units === '%') {
18279 ret.pfValue = value / 100;
18280 }
18281
18282 return ret;
18283 } else if (type.propList) {
18284 var props = [];
18285 var propsStr = '' + value;
18286
18287 if (propsStr === 'none') ; else {
18288 // go over each prop
18289 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
18290
18291 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
18292 var propName = propsSplit[_i2].trim();
18293
18294 if (self.properties[propName]) {
18295 props.push(propName);
18296 } else {
18297 warn('`' + propName + '` is not a valid property name');
18298 }
18299 }
18300
18301 if (props.length === 0) {
18302 return null;
18303 }
18304 }
18305
18306 return {
18307 name: name,
18308 value: props,
18309 strValue: props.length === 0 ? 'none' : props.join(' '),
18310 bypass: propIsBypass
18311 };
18312 } else if (type.color) {
18313 var tuple = color2tuple(value);
18314
18315 if (!tuple) {
18316 return null;
18317 }
18318
18319 return {
18320 name: name,
18321 value: tuple,
18322 pfValue: tuple,
18323 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
18324 // n.b. no spaces b/c of multiple support
18325 bypass: propIsBypass
18326 };
18327 } else if (type.regex || type.regexes) {
18328 // first check enums
18329 if (type.enums) {
18330 var enumProp = checkEnums();
18331
18332 if (enumProp) {
18333 return enumProp;
18334 }
18335 }
18336
18337 var regexes = type.regexes ? type.regexes : [type.regex];
18338
18339 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
18340 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
18341
18342 var m = regex.exec(value);
18343
18344 if (m) {
18345 // regex matches
18346 return {
18347 name: name,
18348 value: type.singleRegexMatchValue ? m[1] : m,
18349 strValue: '' + value,
18350 bypass: propIsBypass
18351 };
18352 }
18353 }
18354
18355 return null; // didn't match any
18356 } else if (type.string) {
18357 // just return
18358 return {
18359 name: name,
18360 value: '' + value,
18361 strValue: '' + value,
18362 bypass: propIsBypass
18363 };
18364 } else if (type.enums) {
18365 // check enums last because it's a combo type in others
18366 return checkEnums();
18367 } else {
18368 return null; // not a type we can handle
18369 }
18370 };
18371
18372 var Style = function Style(cy) {
18373 if (!(this instanceof Style)) {
18374 return new Style(cy);
18375 }
18376
18377 if (!core(cy)) {
18378 error('A style must have a core reference');
18379 return;
18380 }
18381
18382 this._private = {
18383 cy: cy,
18384 coreStyle: {}
18385 };
18386 this.length = 0;
18387 this.resetToDefault();
18388 };
18389
18390 var styfn$8 = Style.prototype;
18391
18392 styfn$8.instanceString = function () {
18393 return 'style';
18394 }; // remove all contexts
18395
18396
18397 styfn$8.clear = function () {
18398 for (var i = 0; i < this.length; i++) {
18399 this[i] = undefined;
18400 }
18401
18402 this.length = 0;
18403 var _p = this._private;
18404 _p.newStyle = true;
18405 return this; // chaining
18406 };
18407
18408 styfn$8.resetToDefault = function () {
18409 this.clear();
18410 this.addDefaultStylesheet();
18411 return this;
18412 }; // builds a style object for the 'core' selector
18413
18414
18415 styfn$8.core = function (propName) {
18416 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
18417 }; // create a new context from the specified selector string and switch to that context
18418
18419
18420 styfn$8.selector = function (selectorStr) {
18421 // 'core' is a special case and does not need a selector
18422 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
18423 var i = this.length++; // new context means new index
18424
18425 this[i] = {
18426 selector: selector,
18427 properties: [],
18428 mappedProperties: [],
18429 index: i
18430 };
18431 return this; // chaining
18432 }; // add one or many css rules to the current context
18433
18434
18435 styfn$8.css = function () {
18436 var self = this;
18437 var args = arguments;
18438
18439 if (args.length === 1) {
18440 var map = args[0];
18441
18442 for (var i = 0; i < self.properties.length; i++) {
18443 var prop = self.properties[i];
18444 var mapVal = map[prop.name];
18445
18446 if (mapVal === undefined) {
18447 mapVal = map[dash2camel(prop.name)];
18448 }
18449
18450 if (mapVal !== undefined) {
18451 this.cssRule(prop.name, mapVal);
18452 }
18453 }
18454 } else if (args.length === 2) {
18455 this.cssRule(args[0], args[1]);
18456 } // do nothing if args are invalid
18457
18458
18459 return this; // chaining
18460 };
18461
18462 styfn$8.style = styfn$8.css; // add a single css rule to the current context
18463
18464 styfn$8.cssRule = function (name, value) {
18465 // name-value pair
18466 var property = this.parse(name, value); // add property to current context if valid
18467
18468 if (property) {
18469 var i = this.length - 1;
18470 this[i].properties.push(property);
18471 this[i].properties[property.name] = property; // allow access by name as well
18472
18473 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
18474 this._private.hasPie = true;
18475 }
18476
18477 if (property.mapped) {
18478 this[i].mappedProperties.push(property);
18479 } // add to core style if necessary
18480
18481
18482 var currentSelectorIsCore = !this[i].selector;
18483
18484 if (currentSelectorIsCore) {
18485 this._private.coreStyle[property.name] = property;
18486 }
18487 }
18488
18489 return this; // chaining
18490 };
18491
18492 styfn$8.append = function (style) {
18493 if (stylesheet(style)) {
18494 style.appendToStyle(this);
18495 } else if (array(style)) {
18496 this.appendFromJson(style);
18497 } else if (string(style)) {
18498 this.appendFromString(style);
18499 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
18500
18501
18502 return this;
18503 }; // static function
18504
18505
18506 Style.fromJson = function (cy, json) {
18507 var style = new Style(cy);
18508 style.fromJson(json);
18509 return style;
18510 };
18511
18512 Style.fromString = function (cy, string) {
18513 return new Style(cy).fromString(string);
18514 };
18515
18516 [styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
18517 extend(styfn$8, props);
18518 });
18519 Style.types = styfn$8.types;
18520 Style.properties = styfn$8.properties;
18521 Style.propertyGroups = styfn$8.propertyGroups;
18522 Style.propertyGroupNames = styfn$8.propertyGroupNames;
18523 Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
18524
18525 var corefn$7 = {
18526 style: function style(newStyle) {
18527 if (newStyle) {
18528 var s = this.setStyle(newStyle);
18529 s.update();
18530 }
18531
18532 return this._private.style;
18533 },
18534 setStyle: function setStyle(style) {
18535 var _p = this._private;
18536
18537 if (stylesheet(style)) {
18538 _p.style = style.generateStyle(this);
18539 } else if (array(style)) {
18540 _p.style = Style.fromJson(this, style);
18541 } else if (string(style)) {
18542 _p.style = Style.fromString(this, style);
18543 } else {
18544 _p.style = Style(this);
18545 }
18546
18547 return _p.style;
18548 }
18549 };
18550
18551 var defaultSelectionType = 'single';
18552 var corefn$8 = {
18553 autolock: function autolock(bool) {
18554 if (bool !== undefined) {
18555 this._private.autolock = bool ? true : false;
18556 } else {
18557 return this._private.autolock;
18558 }
18559
18560 return this; // chaining
18561 },
18562 autoungrabify: function autoungrabify(bool) {
18563 if (bool !== undefined) {
18564 this._private.autoungrabify = bool ? true : false;
18565 } else {
18566 return this._private.autoungrabify;
18567 }
18568
18569 return this; // chaining
18570 },
18571 autounselectify: function autounselectify(bool) {
18572 if (bool !== undefined) {
18573 this._private.autounselectify = bool ? true : false;
18574 } else {
18575 return this._private.autounselectify;
18576 }
18577
18578 return this; // chaining
18579 },
18580 selectionType: function selectionType(selType) {
18581 var _p = this._private;
18582
18583 if (_p.selectionType == null) {
18584 _p.selectionType = defaultSelectionType;
18585 }
18586
18587 if (selType !== undefined) {
18588 if (selType === 'additive' || selType === 'single') {
18589 _p.selectionType = selType;
18590 }
18591 } else {
18592 return _p.selectionType;
18593 }
18594
18595 return this;
18596 },
18597 panningEnabled: function panningEnabled(bool) {
18598 if (bool !== undefined) {
18599 this._private.panningEnabled = bool ? true : false;
18600 } else {
18601 return this._private.panningEnabled;
18602 }
18603
18604 return this; // chaining
18605 },
18606 userPanningEnabled: function userPanningEnabled(bool) {
18607 if (bool !== undefined) {
18608 this._private.userPanningEnabled = bool ? true : false;
18609 } else {
18610 return this._private.userPanningEnabled;
18611 }
18612
18613 return this; // chaining
18614 },
18615 zoomingEnabled: function zoomingEnabled(bool) {
18616 if (bool !== undefined) {
18617 this._private.zoomingEnabled = bool ? true : false;
18618 } else {
18619 return this._private.zoomingEnabled;
18620 }
18621
18622 return this; // chaining
18623 },
18624 userZoomingEnabled: function userZoomingEnabled(bool) {
18625 if (bool !== undefined) {
18626 this._private.userZoomingEnabled = bool ? true : false;
18627 } else {
18628 return this._private.userZoomingEnabled;
18629 }
18630
18631 return this; // chaining
18632 },
18633 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18634 if (bool !== undefined) {
18635 this._private.boxSelectionEnabled = bool ? true : false;
18636 } else {
18637 return this._private.boxSelectionEnabled;
18638 }
18639
18640 return this; // chaining
18641 },
18642 pan: function pan() {
18643 var args = arguments;
18644 var pan = this._private.pan;
18645 var dim, val, dims, x, y;
18646
18647 switch (args.length) {
18648 case 0:
18649 // .pan()
18650 return pan;
18651
18652 case 1:
18653 if (string(args[0])) {
18654 // .pan('x')
18655 dim = args[0];
18656 return pan[dim];
18657 } else if (plainObject(args[0])) {
18658 // .pan({ x: 0, y: 100 })
18659 if (!this._private.panningEnabled) {
18660 return this;
18661 }
18662
18663 dims = args[0];
18664 x = dims.x;
18665 y = dims.y;
18666
18667 if (number(x)) {
18668 pan.x = x;
18669 }
18670
18671 if (number(y)) {
18672 pan.y = y;
18673 }
18674
18675 this.emit('pan viewport');
18676 }
18677
18678 break;
18679
18680 case 2:
18681 // .pan('x', 100)
18682 if (!this._private.panningEnabled) {
18683 return this;
18684 }
18685
18686 dim = args[0];
18687 val = args[1];
18688
18689 if ((dim === 'x' || dim === 'y') && number(val)) {
18690 pan[dim] = val;
18691 }
18692
18693 this.emit('pan viewport');
18694 break;
18695 // invalid
18696 }
18697
18698 this.notify('viewport');
18699 return this; // chaining
18700 },
18701 panBy: function panBy(arg0, arg1) {
18702 var args = arguments;
18703 var pan = this._private.pan;
18704 var dim, val, dims, x, y;
18705
18706 if (!this._private.panningEnabled) {
18707 return this;
18708 }
18709
18710 switch (args.length) {
18711 case 1:
18712 if (plainObject(arg0)) {
18713 // .panBy({ x: 0, y: 100 })
18714 dims = args[0];
18715 x = dims.x;
18716 y = dims.y;
18717
18718 if (number(x)) {
18719 pan.x += x;
18720 }
18721
18722 if (number(y)) {
18723 pan.y += y;
18724 }
18725
18726 this.emit('pan viewport');
18727 }
18728
18729 break;
18730
18731 case 2:
18732 // .panBy('x', 100)
18733 dim = arg0;
18734 val = arg1;
18735
18736 if ((dim === 'x' || dim === 'y') && number(val)) {
18737 pan[dim] += val;
18738 }
18739
18740 this.emit('pan viewport');
18741 break;
18742 // invalid
18743 }
18744
18745 this.notify('viewport');
18746 return this; // chaining
18747 },
18748 fit: function fit(elements, padding) {
18749 var viewportState = this.getFitViewport(elements, padding);
18750
18751 if (viewportState) {
18752 var _p = this._private;
18753 _p.zoom = viewportState.zoom;
18754 _p.pan = viewportState.pan;
18755 this.emit('pan zoom viewport');
18756 this.notify('viewport');
18757 }
18758
18759 return this; // chaining
18760 },
18761 getFitViewport: function getFitViewport(elements, padding) {
18762 if (number(elements) && padding === undefined) {
18763 // elements is optional
18764 padding = elements;
18765 elements = undefined;
18766 }
18767
18768 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18769 return;
18770 }
18771
18772 var bb;
18773
18774 if (string(elements)) {
18775 var sel = elements;
18776 elements = this.$(sel);
18777 } else if (boundingBox(elements)) {
18778 // assume bb
18779 var bbe = elements;
18780 bb = {
18781 x1: bbe.x1,
18782 y1: bbe.y1,
18783 x2: bbe.x2,
18784 y2: bbe.y2
18785 };
18786 bb.w = bb.x2 - bb.x1;
18787 bb.h = bb.y2 - bb.y1;
18788 } else if (!elementOrCollection(elements)) {
18789 elements = this.mutableElements();
18790 }
18791
18792 if (elementOrCollection(elements) && elements.empty()) {
18793 return;
18794 } // can't fit to nothing
18795
18796
18797 bb = bb || elements.boundingBox();
18798 var w = this.width();
18799 var h = this.height();
18800 var zoom;
18801 padding = number(padding) ? padding : 0;
18802
18803 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18804 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18805
18806 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18807 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18808 var pan = {
18809 // now pan to middle
18810 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18811 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18812 };
18813 return {
18814 zoom: zoom,
18815 pan: pan
18816 };
18817 }
18818
18819 return;
18820 },
18821 zoomRange: function zoomRange(min, max) {
18822 var _p = this._private;
18823
18824 if (max == null) {
18825 var opts = min;
18826 min = opts.min;
18827 max = opts.max;
18828 }
18829
18830 if (number(min) && number(max) && min <= max) {
18831 _p.minZoom = min;
18832 _p.maxZoom = max;
18833 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18834 _p.minZoom = min;
18835 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18836 _p.maxZoom = max;
18837 }
18838
18839 return this;
18840 },
18841 minZoom: function minZoom(zoom) {
18842 if (zoom === undefined) {
18843 return this._private.minZoom;
18844 } else {
18845 return this.zoomRange({
18846 min: zoom
18847 });
18848 }
18849 },
18850 maxZoom: function maxZoom(zoom) {
18851 if (zoom === undefined) {
18852 return this._private.maxZoom;
18853 } else {
18854 return this.zoomRange({
18855 max: zoom
18856 });
18857 }
18858 },
18859 getZoomedViewport: function getZoomedViewport(params) {
18860 var _p = this._private;
18861 var currentPan = _p.pan;
18862 var currentZoom = _p.zoom;
18863 var pos; // in rendered px
18864
18865 var zoom;
18866 var bail = false;
18867
18868 if (!_p.zoomingEnabled) {
18869 // zooming disabled
18870 bail = true;
18871 }
18872
18873 if (number(params)) {
18874 // then set the zoom
18875 zoom = params;
18876 } else if (plainObject(params)) {
18877 // then zoom about a point
18878 zoom = params.level;
18879
18880 if (params.position != null) {
18881 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18882 } else if (params.renderedPosition != null) {
18883 pos = params.renderedPosition;
18884 }
18885
18886 if (pos != null && !_p.panningEnabled) {
18887 // panning disabled
18888 bail = true;
18889 }
18890 } // crop zoom
18891
18892
18893 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18894 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18895
18896 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18897 return null;
18898 }
18899
18900 if (pos != null) {
18901 // set zoom about position
18902 var pan1 = currentPan;
18903 var zoom1 = currentZoom;
18904 var zoom2 = zoom;
18905 var pan2 = {
18906 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18907 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18908 };
18909 return {
18910 zoomed: true,
18911 panned: true,
18912 zoom: zoom2,
18913 pan: pan2
18914 };
18915 } else {
18916 // just set the zoom
18917 return {
18918 zoomed: true,
18919 panned: false,
18920 zoom: zoom,
18921 pan: currentPan
18922 };
18923 }
18924 },
18925 zoom: function zoom(params) {
18926 if (params === undefined) {
18927 // get
18928 return this._private.zoom;
18929 } else {
18930 // set
18931 var vp = this.getZoomedViewport(params);
18932 var _p = this._private;
18933
18934 if (vp == null || !vp.zoomed) {
18935 return this;
18936 }
18937
18938 _p.zoom = vp.zoom;
18939
18940 if (vp.panned) {
18941 _p.pan.x = vp.pan.x;
18942 _p.pan.y = vp.pan.y;
18943 }
18944
18945 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18946 this.notify('viewport');
18947 return this; // chaining
18948 }
18949 },
18950 viewport: function viewport(opts) {
18951 var _p = this._private;
18952 var zoomDefd = true;
18953 var panDefd = true;
18954 var events = []; // to trigger
18955
18956 var zoomFailed = false;
18957 var panFailed = false;
18958
18959 if (!opts) {
18960 return this;
18961 }
18962
18963 if (!number(opts.zoom)) {
18964 zoomDefd = false;
18965 }
18966
18967 if (!plainObject(opts.pan)) {
18968 panDefd = false;
18969 }
18970
18971 if (!zoomDefd && !panDefd) {
18972 return this;
18973 }
18974
18975 if (zoomDefd) {
18976 var z = opts.zoom;
18977
18978 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18979 zoomFailed = true;
18980 } else {
18981 _p.zoom = z;
18982 events.push('zoom');
18983 }
18984 }
18985
18986 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18987 var p = opts.pan;
18988
18989 if (number(p.x)) {
18990 _p.pan.x = p.x;
18991 panFailed = false;
18992 }
18993
18994 if (number(p.y)) {
18995 _p.pan.y = p.y;
18996 panFailed = false;
18997 }
18998
18999 if (!panFailed) {
19000 events.push('pan');
19001 }
19002 }
19003
19004 if (events.length > 0) {
19005 events.push('viewport');
19006 this.emit(events.join(' '));
19007 this.notify('viewport');
19008 }
19009
19010 return this; // chaining
19011 },
19012 center: function center(elements) {
19013 var pan = this.getCenterPan(elements);
19014
19015 if (pan) {
19016 this._private.pan = pan;
19017 this.emit('pan viewport');
19018 this.notify('viewport');
19019 }
19020
19021 return this; // chaining
19022 },
19023 getCenterPan: function getCenterPan(elements, zoom) {
19024 if (!this._private.panningEnabled) {
19025 return;
19026 }
19027
19028 if (string(elements)) {
19029 var selector = elements;
19030 elements = this.mutableElements().filter(selector);
19031 } else if (!elementOrCollection(elements)) {
19032 elements = this.mutableElements();
19033 }
19034
19035 if (elements.length === 0) {
19036 return;
19037 } // can't centre pan to nothing
19038
19039
19040 var bb = elements.boundingBox();
19041 var w = this.width();
19042 var h = this.height();
19043 zoom = zoom === undefined ? this._private.zoom : zoom;
19044 var pan = {
19045 // middle
19046 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
19047 y: (h - zoom * (bb.y1 + bb.y2)) / 2
19048 };
19049 return pan;
19050 },
19051 reset: function reset() {
19052 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
19053 return this;
19054 }
19055
19056 this.viewport({
19057 pan: {
19058 x: 0,
19059 y: 0
19060 },
19061 zoom: 1
19062 });
19063 return this; // chaining
19064 },
19065 invalidateSize: function invalidateSize() {
19066 this._private.sizeCache = null;
19067 },
19068 size: function size() {
19069 var _p = this._private;
19070 var container = _p.container;
19071 return _p.sizeCache = _p.sizeCache || (container ? function () {
19072 var style = window$1.getComputedStyle(container);
19073
19074 var val = function val(name) {
19075 return parseFloat(style.getPropertyValue(name));
19076 };
19077
19078 return {
19079 width: container.clientWidth - val('padding-left') - val('padding-right'),
19080 height: container.clientHeight - val('padding-top') - val('padding-bottom')
19081 };
19082 }() : {
19083 // fallback if no container (not 0 b/c can be used for dividing etc)
19084 width: 1,
19085 height: 1
19086 });
19087 },
19088 width: function width() {
19089 return this.size().width;
19090 },
19091 height: function height() {
19092 return this.size().height;
19093 },
19094 extent: function extent() {
19095 var pan = this._private.pan;
19096 var zoom = this._private.zoom;
19097 var rb = this.renderedExtent();
19098 var b = {
19099 x1: (rb.x1 - pan.x) / zoom,
19100 x2: (rb.x2 - pan.x) / zoom,
19101 y1: (rb.y1 - pan.y) / zoom,
19102 y2: (rb.y2 - pan.y) / zoom
19103 };
19104 b.w = b.x2 - b.x1;
19105 b.h = b.y2 - b.y1;
19106 return b;
19107 },
19108 renderedExtent: function renderedExtent() {
19109 var width = this.width();
19110 var height = this.height();
19111 return {
19112 x1: 0,
19113 y1: 0,
19114 x2: width,
19115 y2: height,
19116 w: width,
19117 h: height
19118 };
19119 }
19120 }; // aliases
19121
19122 corefn$8.centre = corefn$8.center; // backwards compatibility
19123
19124 corefn$8.autolockNodes = corefn$8.autolock;
19125 corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
19126
19127 var fn$6 = {
19128 data: define$3.data({
19129 field: 'data',
19130 bindingEvent: 'data',
19131 allowBinding: true,
19132 allowSetting: true,
19133 settingEvent: 'data',
19134 settingTriggersEvent: true,
19135 triggerFnName: 'trigger',
19136 allowGetting: true
19137 }),
19138 removeData: define$3.removeData({
19139 field: 'data',
19140 event: 'data',
19141 triggerFnName: 'trigger',
19142 triggerEvent: true
19143 }),
19144 scratch: define$3.data({
19145 field: 'scratch',
19146 bindingEvent: 'scratch',
19147 allowBinding: true,
19148 allowSetting: true,
19149 settingEvent: 'scratch',
19150 settingTriggersEvent: true,
19151 triggerFnName: 'trigger',
19152 allowGetting: true
19153 }),
19154 removeScratch: define$3.removeData({
19155 field: 'scratch',
19156 event: 'scratch',
19157 triggerFnName: 'trigger',
19158 triggerEvent: true
19159 })
19160 }; // aliases
19161
19162 fn$6.attr = fn$6.data;
19163 fn$6.removeAttr = fn$6.removeData;
19164
19165 var Core = function Core(opts) {
19166 var cy = this;
19167 opts = extend({}, opts);
19168 var container = opts.container; // allow for passing a wrapped jquery object
19169 // e.g. cytoscape({ container: $('#cy') })
19170
19171 if (container && !htmlElement(container) && htmlElement(container[0])) {
19172 container = container[0];
19173 }
19174
19175 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
19176
19177 reg = reg || {};
19178
19179 if (reg && reg.cy) {
19180 reg.cy.destroy();
19181 reg = {}; // old instance => replace reg completely
19182 }
19183
19184 var readies = reg.readies = reg.readies || [];
19185
19186 if (container) {
19187 container._cyreg = reg;
19188 } // make sure container assoc'd reg points to this cy
19189
19190
19191 reg.cy = cy;
19192 var head = window$1 !== undefined && container !== undefined && !opts.headless;
19193 var options = opts;
19194 options.layout = extend({
19195 name: head ? 'grid' : 'null'
19196 }, options.layout);
19197 options.renderer = extend({
19198 name: head ? 'canvas' : 'null'
19199 }, options.renderer);
19200
19201 var defVal = function defVal(def, val, altVal) {
19202 if (val !== undefined) {
19203 return val;
19204 } else if (altVal !== undefined) {
19205 return altVal;
19206 } else {
19207 return def;
19208 }
19209 };
19210
19211 var _p = this._private = {
19212 container: container,
19213 // html dom ele container
19214 ready: false,
19215 // whether ready has been triggered
19216 options: options,
19217 // cached options
19218 elements: new Collection(this),
19219 // elements in the graph
19220 listeners: [],
19221 // list of listeners
19222 aniEles: new Collection(this),
19223 // elements being animated
19224 data: {},
19225 // data for the core
19226 scratch: {},
19227 // scratch object for core
19228 layout: null,
19229 renderer: null,
19230 destroyed: false,
19231 // whether destroy was called
19232 notificationsEnabled: true,
19233 // whether notifications are sent to the renderer
19234 minZoom: 1e-50,
19235 maxZoom: 1e50,
19236 zoomingEnabled: defVal(true, options.zoomingEnabled),
19237 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
19238 panningEnabled: defVal(true, options.panningEnabled),
19239 userPanningEnabled: defVal(true, options.userPanningEnabled),
19240 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
19241 autolock: defVal(false, options.autolock, options.autolockNodes),
19242 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
19243 autounselectify: defVal(false, options.autounselectify),
19244 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
19245 zoom: number(options.zoom) ? options.zoom : 1,
19246 pan: {
19247 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
19248 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
19249 },
19250 animation: {
19251 // object for currently-running animations
19252 current: [],
19253 queue: []
19254 },
19255 hasCompoundNodes: false
19256 };
19257
19258 this.createEmitter(); // set selection type
19259
19260 this.selectionType(options.selectionType); // init zoom bounds
19261
19262 this.zoomRange({
19263 min: options.minZoom,
19264 max: options.maxZoom
19265 });
19266
19267 var loadExtData = function loadExtData(extData, next) {
19268 var anyIsPromise = extData.some(promise);
19269
19270 if (anyIsPromise) {
19271 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
19272 } else {
19273 next(extData); // exec synchronously for convenience
19274 }
19275 }; // start with the default stylesheet so we have something before loading an external stylesheet
19276
19277
19278 if (_p.styleEnabled) {
19279 cy.setStyle([]);
19280 } // create the renderer
19281
19282
19283 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
19284
19285 cy.initRenderer(rendererOptions);
19286
19287 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
19288 cy.notifications(false); // remove old elements
19289
19290 var oldEles = cy.mutableElements();
19291
19292 if (oldEles.length > 0) {
19293 oldEles.remove();
19294 }
19295
19296 if (elements != null) {
19297 if (plainObject(elements) || array(elements)) {
19298 cy.add(elements);
19299 }
19300 }
19301
19302 cy.one('layoutready', function (e) {
19303 cy.notifications(true);
19304 cy.emit(e); // we missed this event by turning notifications off, so pass it on
19305
19306 cy.one('load', onload);
19307 cy.emitAndNotify('load');
19308 }).one('layoutstop', function () {
19309 cy.one('done', ondone);
19310 cy.emit('done');
19311 });
19312 var layoutOpts = extend({}, cy._private.options.layout);
19313 layoutOpts.eles = cy.elements();
19314 cy.layout(layoutOpts).run();
19315 };
19316
19317 loadExtData([options.style, options.elements], function (thens) {
19318 var initStyle = thens[0];
19319 var initEles = thens[1]; // init style
19320
19321 if (_p.styleEnabled) {
19322 cy.style().append(initStyle);
19323 } // initial load
19324
19325
19326 setElesAndLayout(initEles, function () {
19327 // onready
19328 cy.startAnimationLoop();
19329 _p.ready = true; // if a ready callback is specified as an option, the bind it
19330
19331 if (fn(options.ready)) {
19332 cy.on('ready', options.ready);
19333 } // bind all the ready handlers registered before creating this instance
19334
19335
19336 for (var i = 0; i < readies.length; i++) {
19337 var fn$1 = readies[i];
19338 cy.on('ready', fn$1);
19339 }
19340
19341 if (reg) {
19342 reg.readies = [];
19343 } // 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
19344
19345
19346 cy.emit('ready');
19347 }, options.done);
19348 });
19349 };
19350
19351 var corefn$9 = Core.prototype; // short alias
19352
19353 extend(corefn$9, {
19354 instanceString: function instanceString() {
19355 return 'core';
19356 },
19357 isReady: function isReady() {
19358 return this._private.ready;
19359 },
19360 destroyed: function destroyed() {
19361 return this._private.destroyed;
19362 },
19363 ready: function ready(fn) {
19364 if (this.isReady()) {
19365 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
19366 } else {
19367 this.on('ready', fn);
19368 }
19369
19370 return this;
19371 },
19372 destroy: function destroy() {
19373 var cy = this;
19374 if (cy.destroyed()) return;
19375 cy.stopAnimationLoop();
19376 cy.destroyRenderer();
19377 this.emit('destroy');
19378 cy._private.destroyed = true;
19379 return cy;
19380 },
19381 hasElementWithId: function hasElementWithId(id) {
19382 return this._private.elements.hasElementWithId(id);
19383 },
19384 getElementById: function getElementById(id) {
19385 return this._private.elements.getElementById(id);
19386 },
19387 hasCompoundNodes: function hasCompoundNodes() {
19388 return this._private.hasCompoundNodes;
19389 },
19390 headless: function headless() {
19391 return this._private.renderer.isHeadless();
19392 },
19393 styleEnabled: function styleEnabled() {
19394 return this._private.styleEnabled;
19395 },
19396 addToPool: function addToPool(eles) {
19397 this._private.elements.merge(eles);
19398
19399 return this; // chaining
19400 },
19401 removeFromPool: function removeFromPool(eles) {
19402 this._private.elements.unmerge(eles);
19403
19404 return this;
19405 },
19406 container: function container() {
19407 return this._private.container || null;
19408 },
19409 mount: function mount(container) {
19410 if (container == null) {
19411 return;
19412 }
19413
19414 var cy = this;
19415 var _p = cy._private;
19416 var options = _p.options;
19417
19418 if (!htmlElement(container) && htmlElement(container[0])) {
19419 container = container[0];
19420 }
19421
19422 cy.stopAnimationLoop();
19423 cy.destroyRenderer();
19424 _p.container = container;
19425 _p.styleEnabled = true;
19426 cy.invalidateSize();
19427 cy.initRenderer(extend({}, options, options.renderer, {
19428 // allow custom renderer name to be re-used, otherwise use canvas
19429 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
19430 }));
19431 cy.startAnimationLoop();
19432 cy.style(options.style);
19433 cy.emit('mount');
19434 return cy;
19435 },
19436 unmount: function unmount() {
19437 var cy = this;
19438 cy.stopAnimationLoop();
19439 cy.destroyRenderer();
19440 cy.initRenderer({
19441 name: 'null'
19442 });
19443 cy.emit('unmount');
19444 return cy;
19445 },
19446 options: function options() {
19447 return copy(this._private.options);
19448 },
19449 json: function json(obj) {
19450 var cy = this;
19451 var _p = cy._private;
19452 var eles = cy.mutableElements();
19453
19454 var getFreshRef = function getFreshRef(ele) {
19455 return cy.getElementById(ele.id());
19456 };
19457
19458 if (plainObject(obj)) {
19459 // set
19460 cy.startBatch();
19461
19462 if (obj.elements) {
19463 var idInJson = {};
19464
19465 var updateEles = function updateEles(jsons, gr) {
19466 var toAdd = [];
19467 var toMod = [];
19468
19469 for (var i = 0; i < jsons.length; i++) {
19470 var json = jsons[i];
19471 var id = '' + json.data.id; // id must be string
19472
19473 var ele = cy.getElementById(id);
19474 idInJson[id] = true;
19475
19476 if (ele.length !== 0) {
19477 // existing element should be updated
19478 toMod.push({
19479 ele: ele,
19480 json: json
19481 });
19482 } else {
19483 // otherwise should be added
19484 if (gr) {
19485 json.group = gr;
19486 toAdd.push(json);
19487 } else {
19488 toAdd.push(json);
19489 }
19490 }
19491 }
19492
19493 cy.add(toAdd);
19494
19495 for (var _i = 0; _i < toMod.length; _i++) {
19496 var _toMod$_i = toMod[_i],
19497 _ele = _toMod$_i.ele,
19498 _json = _toMod$_i.json;
19499
19500 _ele.json(_json);
19501 }
19502 };
19503
19504 if (array(obj.elements)) {
19505 // elements: []
19506 updateEles(obj.elements);
19507 } else {
19508 // elements: { nodes: [], edges: [] }
19509 var grs = ['nodes', 'edges'];
19510
19511 for (var i = 0; i < grs.length; i++) {
19512 var gr = grs[i];
19513 var elements = obj.elements[gr];
19514
19515 if (array(elements)) {
19516 updateEles(elements, gr);
19517 }
19518 }
19519 }
19520
19521 var parentsToRemove = cy.collection();
19522 eles.filter(function (ele) {
19523 return !idInJson[ele.id()];
19524 }).forEach(function (ele) {
19525 if (ele.isParent()) {
19526 parentsToRemove.merge(ele);
19527 } else {
19528 ele.remove();
19529 }
19530 }); // so that children are not removed w/parent
19531
19532 parentsToRemove.forEach(function (ele) {
19533 return ele.children().move({
19534 parent: null
19535 });
19536 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
19537
19538 parentsToRemove.forEach(function (ele) {
19539 return getFreshRef(ele).remove();
19540 });
19541 }
19542
19543 if (obj.style) {
19544 cy.style(obj.style);
19545 }
19546
19547 if (obj.zoom != null && obj.zoom !== _p.zoom) {
19548 cy.zoom(obj.zoom);
19549 }
19550
19551 if (obj.pan) {
19552 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
19553 cy.pan(obj.pan);
19554 }
19555 }
19556
19557 if (obj.data) {
19558 cy.data(obj.data);
19559 }
19560
19561 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
19562
19563 for (var _i2 = 0; _i2 < fields.length; _i2++) {
19564 var f = fields[_i2];
19565
19566 if (obj[f] != null) {
19567 cy[f](obj[f]);
19568 }
19569 }
19570
19571 cy.endBatch();
19572 return this; // chaining
19573 } else {
19574 // get
19575 var flat = !!obj;
19576 var json = {};
19577
19578 if (flat) {
19579 json.elements = this.elements().map(function (ele) {
19580 return ele.json();
19581 });
19582 } else {
19583 json.elements = {};
19584 eles.forEach(function (ele) {
19585 var group = ele.group();
19586
19587 if (!json.elements[group]) {
19588 json.elements[group] = [];
19589 }
19590
19591 json.elements[group].push(ele.json());
19592 });
19593 }
19594
19595 if (this._private.styleEnabled) {
19596 json.style = cy.style().json();
19597 }
19598
19599 json.data = copy(cy.data());
19600 var options = _p.options;
19601 json.zoomingEnabled = _p.zoomingEnabled;
19602 json.userZoomingEnabled = _p.userZoomingEnabled;
19603 json.zoom = _p.zoom;
19604 json.minZoom = _p.minZoom;
19605 json.maxZoom = _p.maxZoom;
19606 json.panningEnabled = _p.panningEnabled;
19607 json.userPanningEnabled = _p.userPanningEnabled;
19608 json.pan = copy(_p.pan);
19609 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19610 json.renderer = copy(options.renderer);
19611 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19612 json.textureOnViewport = options.textureOnViewport;
19613 json.wheelSensitivity = options.wheelSensitivity;
19614 json.motionBlur = options.motionBlur;
19615 return json;
19616 }
19617 }
19618 });
19619 corefn$9.$id = corefn$9.getElementById;
19620 [corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
19621 extend(corefn$9, props);
19622 });
19623
19624 /* eslint-disable no-unused-vars */
19625
19626 var defaults$9 = {
19627 fit: true,
19628 // whether to fit the viewport to the graph
19629 directed: false,
19630 // whether the tree is directed downwards (or edges can point in any direction if false)
19631 padding: 30,
19632 // padding on fit
19633 circle: false,
19634 // put depths in concentric circles if true, put depths top down if false
19635 grid: false,
19636 // whether to create an even grid into which the DAG is placed (circle:false only)
19637 spacingFactor: 1.75,
19638 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19639 boundingBox: undefined,
19640 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19641 avoidOverlap: true,
19642 // prevents node overlap, may overflow boundingBox if not enough space
19643 nodeDimensionsIncludeLabels: false,
19644 // Excludes the label when calculating node bounding boxes for the layout algorithm
19645 roots: undefined,
19646 // the roots of the trees
19647 maximal: false,
19648 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19649 animate: false,
19650 // whether to transition the node positions
19651 animationDuration: 500,
19652 // duration of animation in ms if enabled
19653 animationEasing: undefined,
19654 // easing of animation if enabled,
19655 animateFilter: function animateFilter(node, i) {
19656 return true;
19657 },
19658 // 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
19659 ready: undefined,
19660 // callback on layoutready
19661 stop: undefined,
19662 // callback on layoutstop
19663 transform: function transform(node, position) {
19664 return position;
19665 } // transform a given node position. Useful for changing flow direction in discrete layouts
19666
19667 };
19668 /* eslint-enable */
19669
19670 var getInfo = function getInfo(ele) {
19671 return ele.scratch('breadthfirst');
19672 };
19673
19674 var setInfo = function setInfo(ele, obj) {
19675 return ele.scratch('breadthfirst', obj);
19676 };
19677
19678 function BreadthFirstLayout(options) {
19679 this.options = extend({}, defaults$9, options);
19680 }
19681
19682 BreadthFirstLayout.prototype.run = function () {
19683 var params = this.options;
19684 var options = params;
19685 var cy = params.cy;
19686 var eles = options.eles;
19687 var nodes = eles.nodes().filter(function (n) {
19688 return !n.isParent();
19689 });
19690 var graph = eles;
19691 var directed = options.directed;
19692 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19693
19694 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19695 x1: 0,
19696 y1: 0,
19697 w: cy.width(),
19698 h: cy.height()
19699 });
19700 var roots;
19701
19702 if (elementOrCollection(options.roots)) {
19703 roots = options.roots;
19704 } else if (array(options.roots)) {
19705 var rootsArray = [];
19706
19707 for (var i = 0; i < options.roots.length; i++) {
19708 var id = options.roots[i];
19709 var ele = cy.getElementById(id);
19710 rootsArray.push(ele);
19711 }
19712
19713 roots = cy.collection(rootsArray);
19714 } else if (string(options.roots)) {
19715 roots = cy.$(options.roots);
19716 } else {
19717 if (directed) {
19718 roots = nodes.roots();
19719 } else {
19720 var components = eles.components();
19721 roots = cy.collection();
19722
19723 var _loop = function _loop(_i) {
19724 var comp = components[_i];
19725 var maxDegree = comp.maxDegree(false);
19726 var compRoots = comp.filter(function (ele) {
19727 return ele.degree(false) === maxDegree;
19728 });
19729 roots = roots.add(compRoots);
19730 };
19731
19732 for (var _i = 0; _i < components.length; _i++) {
19733 _loop(_i);
19734 }
19735 }
19736 }
19737
19738 var depths = [];
19739 var foundByBfs = {};
19740
19741 var addToDepth = function addToDepth(ele, d) {
19742 if (depths[d] == null) {
19743 depths[d] = [];
19744 }
19745
19746 var i = depths[d].length;
19747 depths[d].push(ele);
19748 setInfo(ele, {
19749 index: i,
19750 depth: d
19751 });
19752 };
19753
19754 var changeDepth = function changeDepth(ele, newDepth) {
19755 var _getInfo = getInfo(ele),
19756 depth = _getInfo.depth,
19757 index = _getInfo.index;
19758
19759 depths[depth][index] = null;
19760 addToDepth(ele, newDepth);
19761 }; // find the depths of the nodes
19762
19763
19764 graph.bfs({
19765 roots: roots,
19766 directed: options.directed,
19767 visit: function visit(node, edge, pNode, i, depth) {
19768 var ele = node[0];
19769 var id = ele.id();
19770 addToDepth(ele, depth);
19771 foundByBfs[id] = true;
19772 }
19773 }); // check for nodes not found by bfs
19774
19775 var orphanNodes = [];
19776
19777 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19778 var _ele = nodes[_i2];
19779
19780 if (foundByBfs[_ele.id()]) {
19781 continue;
19782 } else {
19783 orphanNodes.push(_ele);
19784 }
19785 } // assign the nodes a depth and index
19786
19787
19788 var assignDepthsAt = function assignDepthsAt(i) {
19789 var eles = depths[i];
19790
19791 for (var j = 0; j < eles.length; j++) {
19792 var _ele2 = eles[j];
19793
19794 if (_ele2 == null) {
19795 eles.splice(j, 1);
19796 j--;
19797 continue;
19798 }
19799
19800 setInfo(_ele2, {
19801 depth: i,
19802 index: j
19803 });
19804 }
19805 };
19806
19807 var assignDepths = function assignDepths() {
19808 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19809 assignDepthsAt(_i3);
19810 }
19811 };
19812
19813 var adjustMaximally = function adjustMaximally(ele, shifted) {
19814 var eInfo = getInfo(ele);
19815 var incomers = ele.incomers().filter(function (el) {
19816 return el.isNode() && eles.has(el);
19817 });
19818 var maxDepth = -1;
19819 var id = ele.id();
19820
19821 for (var k = 0; k < incomers.length; k++) {
19822 var incmr = incomers[k];
19823 var iInfo = getInfo(incmr);
19824 maxDepth = Math.max(maxDepth, iInfo.depth);
19825 }
19826
19827 if (eInfo.depth <= maxDepth) {
19828 if (shifted[id]) {
19829 return null;
19830 }
19831
19832 changeDepth(ele, maxDepth + 1);
19833 shifted[id] = true;
19834 return true;
19835 }
19836
19837 return false;
19838 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19839
19840
19841 if (directed && maximal) {
19842 var Q = [];
19843 var shifted = {};
19844
19845 var enqueue = function enqueue(n) {
19846 return Q.push(n);
19847 };
19848
19849 var dequeue = function dequeue() {
19850 return Q.shift();
19851 };
19852
19853 nodes.forEach(function (n) {
19854 return Q.push(n);
19855 });
19856
19857 while (Q.length > 0) {
19858 var _ele3 = dequeue();
19859
19860 var didShift = adjustMaximally(_ele3, shifted);
19861
19862 if (didShift) {
19863 _ele3.outgoers().filter(function (el) {
19864 return el.isNode() && eles.has(el);
19865 }).forEach(enqueue);
19866 } else if (didShift === null) {
19867 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19868 break; // exit on failure
19869 }
19870 }
19871 }
19872
19873 assignDepths(); // clear holes
19874 // find min distance we need to leave between nodes
19875
19876 var minDistance = 0;
19877
19878 if (options.avoidOverlap) {
19879 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19880 var n = nodes[_i4];
19881 var nbb = n.layoutDimensions(options);
19882 var w = nbb.w;
19883 var h = nbb.h;
19884 minDistance = Math.max(minDistance, w, h);
19885 }
19886 } // get the weighted percent for an element based on its connectivity to other levels
19887
19888
19889 var cachedWeightedPercent = {};
19890
19891 var getWeightedPercent = function getWeightedPercent(ele) {
19892 if (cachedWeightedPercent[ele.id()]) {
19893 return cachedWeightedPercent[ele.id()];
19894 }
19895
19896 var eleDepth = getInfo(ele).depth;
19897 var neighbors = ele.neighborhood();
19898 var percent = 0;
19899 var samples = 0;
19900
19901 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19902 var neighbor = neighbors[_i5];
19903
19904 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19905 continue;
19906 }
19907
19908 var bf = getInfo(neighbor);
19909 var index = bf.index;
19910 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19911
19912 if (index == null || depth == null) {
19913 continue;
19914 }
19915
19916 var nDepth = depths[depth].length;
19917
19918 if (depth < eleDepth) {
19919 // only get influenced by elements above
19920 percent += index / nDepth;
19921 samples++;
19922 }
19923 }
19924
19925 samples = Math.max(1, samples);
19926 percent = percent / samples;
19927
19928 if (samples === 0) {
19929 // put lone nodes at the start
19930 percent = 0;
19931 }
19932
19933 cachedWeightedPercent[ele.id()] = percent;
19934 return percent;
19935 }; // rearrange the indices in each depth level based on connectivity
19936
19937
19938 var sortFn = function sortFn(a, b) {
19939 var apct = getWeightedPercent(a);
19940 var bpct = getWeightedPercent(b);
19941 var diff = apct - bpct;
19942
19943 if (diff === 0) {
19944 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19945 } else {
19946 return diff;
19947 }
19948 }; // sort each level to make connected nodes closer
19949
19950
19951 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19952 depths[_i6].sort(sortFn);
19953
19954 assignDepthsAt(_i6);
19955 } // assign orphan nodes to a new top-level depth
19956
19957
19958 var orphanDepth = [];
19959
19960 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19961 orphanDepth.push(orphanNodes[_i7]);
19962 }
19963
19964 depths.unshift(orphanDepth);
19965 assignDepths();
19966 var biggestDepthSize = 0;
19967
19968 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19969 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19970 }
19971
19972 var center = {
19973 x: bb.x1 + bb.w / 2,
19974 y: bb.x1 + bb.h / 2
19975 };
19976 var maxDepthSize = depths.reduce(function (max, eles) {
19977 return Math.max(max, eles.length);
19978 }, 0);
19979
19980 var getPosition = function getPosition(ele) {
19981 var _getInfo2 = getInfo(ele),
19982 depth = _getInfo2.depth,
19983 index = _getInfo2.index;
19984
19985 var depthSize = depths[depth].length;
19986 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19987 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19988 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19989 radiusStepSize = Math.max(radiusStepSize, minDistance);
19990
19991 if (!options.circle) {
19992 var epos = {
19993 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19994 y: (depth + 1) * distanceY
19995 };
19996 return epos;
19997 } else {
19998 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19999 var theta = 2 * Math.PI / depths[depth].length * index;
20000
20001 if (depth === 0 && depths[0].length === 1) {
20002 radius = 1;
20003 }
20004
20005 return {
20006 x: center.x + radius * Math.cos(theta),
20007 y: center.y + radius * Math.sin(theta)
20008 };
20009 }
20010 };
20011
20012 nodes.layoutPositions(this, options, getPosition);
20013 return this; // chaining
20014 };
20015
20016 var defaults$a = {
20017 fit: true,
20018 // whether to fit the viewport to the graph
20019 padding: 30,
20020 // the padding on fit
20021 boundingBox: undefined,
20022 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20023 avoidOverlap: true,
20024 // prevents node overlap, may overflow boundingBox and radius if not enough space
20025 nodeDimensionsIncludeLabels: false,
20026 // Excludes the label when calculating node bounding boxes for the layout algorithm
20027 spacingFactor: undefined,
20028 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20029 radius: undefined,
20030 // the radius of the circle
20031 startAngle: 3 / 2 * Math.PI,
20032 // where nodes start in radians
20033 sweep: undefined,
20034 // how many radians should be between the first and last node (defaults to full circle)
20035 clockwise: true,
20036 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
20037 sort: undefined,
20038 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20039 animate: false,
20040 // whether to transition the node positions
20041 animationDuration: 500,
20042 // duration of animation in ms if enabled
20043 animationEasing: undefined,
20044 // easing of animation if enabled
20045 animateFilter: function animateFilter(node, i) {
20046 return true;
20047 },
20048 // 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
20049 ready: undefined,
20050 // callback on layoutready
20051 stop: undefined,
20052 // callback on layoutstop
20053 transform: function transform(node, position) {
20054 return position;
20055 } // transform a given node position. Useful for changing flow direction in discrete layouts
20056
20057 };
20058
20059 function CircleLayout(options) {
20060 this.options = extend({}, defaults$a, options);
20061 }
20062
20063 CircleLayout.prototype.run = function () {
20064 var params = this.options;
20065 var options = params;
20066 var cy = params.cy;
20067 var eles = options.eles;
20068 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
20069 var nodes = eles.nodes().not(':parent');
20070
20071 if (options.sort) {
20072 nodes = nodes.sort(options.sort);
20073 }
20074
20075 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20076 x1: 0,
20077 y1: 0,
20078 w: cy.width(),
20079 h: cy.height()
20080 });
20081 var center = {
20082 x: bb.x1 + bb.w / 2,
20083 y: bb.y1 + bb.h / 2
20084 };
20085 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
20086 var dTheta = sweep / Math.max(1, nodes.length - 1);
20087 var r;
20088 var minDistance = 0;
20089
20090 for (var i = 0; i < nodes.length; i++) {
20091 var n = nodes[i];
20092 var nbb = n.layoutDimensions(options);
20093 var w = nbb.w;
20094 var h = nbb.h;
20095 minDistance = Math.max(minDistance, w, h);
20096 }
20097
20098 if (number(options.radius)) {
20099 r = options.radius;
20100 } else if (nodes.length <= 1) {
20101 r = 0;
20102 } else {
20103 r = Math.min(bb.h, bb.w) / 2 - minDistance;
20104 } // calculate the radius
20105
20106
20107 if (nodes.length > 1 && options.avoidOverlap) {
20108 // but only if more than one node (can't overlap)
20109 minDistance *= 1.75; // just to have some nice spacing
20110
20111 var dcos = Math.cos(dTheta) - Math.cos(0);
20112 var dsin = Math.sin(dTheta) - Math.sin(0);
20113 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
20114
20115 r = Math.max(rMin, r);
20116 }
20117
20118 var getPos = function getPos(ele, i) {
20119 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
20120 var rx = r * Math.cos(theta);
20121 var ry = r * Math.sin(theta);
20122 var pos = {
20123 x: center.x + rx,
20124 y: center.y + ry
20125 };
20126 return pos;
20127 };
20128
20129 nodes.layoutPositions(this, options, getPos);
20130 return this; // chaining
20131 };
20132
20133 var defaults$b = {
20134 fit: true,
20135 // whether to fit the viewport to the graph
20136 padding: 30,
20137 // the padding on fit
20138 startAngle: 3 / 2 * Math.PI,
20139 // where nodes start in radians
20140 sweep: undefined,
20141 // how many radians should be between the first and last node (defaults to full circle)
20142 clockwise: true,
20143 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
20144 equidistant: false,
20145 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
20146 minNodeSpacing: 10,
20147 // min spacing between outside of nodes (used for radius adjustment)
20148 boundingBox: undefined,
20149 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20150 avoidOverlap: true,
20151 // prevents node overlap, may overflow boundingBox if not enough space
20152 nodeDimensionsIncludeLabels: false,
20153 // Excludes the label when calculating node bounding boxes for the layout algorithm
20154 height: undefined,
20155 // height of layout area (overrides container height)
20156 width: undefined,
20157 // width of layout area (overrides container width)
20158 spacingFactor: undefined,
20159 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20160 concentric: function concentric(node) {
20161 // returns numeric value for each node, placing higher nodes in levels towards the centre
20162 return node.degree();
20163 },
20164 levelWidth: function levelWidth(nodes) {
20165 // the variation of concentric values in each level
20166 return nodes.maxDegree() / 4;
20167 },
20168 animate: false,
20169 // whether to transition the node positions
20170 animationDuration: 500,
20171 // duration of animation in ms if enabled
20172 animationEasing: undefined,
20173 // easing of animation if enabled
20174 animateFilter: function animateFilter(node, i) {
20175 return true;
20176 },
20177 // 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
20178 ready: undefined,
20179 // callback on layoutready
20180 stop: undefined,
20181 // callback on layoutstop
20182 transform: function transform(node, position) {
20183 return position;
20184 } // transform a given node position. Useful for changing flow direction in discrete layouts
20185
20186 };
20187
20188 function ConcentricLayout(options) {
20189 this.options = extend({}, defaults$b, options);
20190 }
20191
20192 ConcentricLayout.prototype.run = function () {
20193 var params = this.options;
20194 var options = params;
20195 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
20196 var cy = params.cy;
20197 var eles = options.eles;
20198 var nodes = eles.nodes().not(':parent');
20199 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20200 x1: 0,
20201 y1: 0,
20202 w: cy.width(),
20203 h: cy.height()
20204 });
20205 var center = {
20206 x: bb.x1 + bb.w / 2,
20207 y: bb.y1 + bb.h / 2
20208 };
20209 var nodeValues = []; // { node, value }
20210
20211 var maxNodeSize = 0;
20212
20213 for (var i = 0; i < nodes.length; i++) {
20214 var node = nodes[i];
20215 var value = void 0; // calculate the node value
20216
20217 value = options.concentric(node);
20218 nodeValues.push({
20219 value: value,
20220 node: node
20221 }); // for style mapping
20222
20223 node._private.scratch.concentric = value;
20224 } // in case we used the `concentric` in style
20225
20226
20227 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
20228
20229 for (var _i = 0; _i < nodes.length; _i++) {
20230 var _node = nodes[_i];
20231
20232 var nbb = _node.layoutDimensions(options);
20233
20234 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
20235 } // sort node values in descreasing order
20236
20237
20238 nodeValues.sort(function (a, b) {
20239 return b.value - a.value;
20240 });
20241 var levelWidth = options.levelWidth(nodes); // put the values into levels
20242
20243 var levels = [[]];
20244 var currentLevel = levels[0];
20245
20246 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
20247 var val = nodeValues[_i2];
20248
20249 if (currentLevel.length > 0) {
20250 var diff = Math.abs(currentLevel[0].value - val.value);
20251
20252 if (diff >= levelWidth) {
20253 currentLevel = [];
20254 levels.push(currentLevel);
20255 }
20256 }
20257
20258 currentLevel.push(val);
20259 } // create positions from levels
20260
20261
20262 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
20263
20264 if (!options.avoidOverlap) {
20265 // then strictly constrain to bb
20266 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
20267 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
20268 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
20269 minDist = Math.min(minDist, rStep);
20270 } // find the metrics for each level
20271
20272
20273 var r = 0;
20274
20275 for (var _i3 = 0; _i3 < levels.length; _i3++) {
20276 var level = levels[_i3];
20277 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
20278 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
20279
20280 if (level.length > 1 && options.avoidOverlap) {
20281 // but only if more than one node (can't overlap)
20282 var dcos = Math.cos(dTheta) - Math.cos(0);
20283 var dsin = Math.sin(dTheta) - Math.sin(0);
20284 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
20285
20286 r = Math.max(rMin, r);
20287 }
20288
20289 level.r = r;
20290 r += minDist;
20291 }
20292
20293 if (options.equidistant) {
20294 var rDeltaMax = 0;
20295 var _r = 0;
20296
20297 for (var _i4 = 0; _i4 < levels.length; _i4++) {
20298 var _level = levels[_i4];
20299 var rDelta = _level.r - _r;
20300 rDeltaMax = Math.max(rDeltaMax, rDelta);
20301 }
20302
20303 _r = 0;
20304
20305 for (var _i5 = 0; _i5 < levels.length; _i5++) {
20306 var _level2 = levels[_i5];
20307
20308 if (_i5 === 0) {
20309 _r = _level2.r;
20310 }
20311
20312 _level2.r = _r;
20313 _r += rDeltaMax;
20314 }
20315 } // calculate the node positions
20316
20317
20318 var pos = {}; // id => position
20319
20320 for (var _i6 = 0; _i6 < levels.length; _i6++) {
20321 var _level3 = levels[_i6];
20322 var _dTheta = _level3.dTheta;
20323 var _r2 = _level3.r;
20324
20325 for (var j = 0; j < _level3.length; j++) {
20326 var _val = _level3[j];
20327 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
20328 var p = {
20329 x: center.x + _r2 * Math.cos(theta),
20330 y: center.y + _r2 * Math.sin(theta)
20331 };
20332 pos[_val.node.id()] = p;
20333 }
20334 } // position the nodes
20335
20336
20337 nodes.layoutPositions(this, options, function (ele) {
20338 var id = ele.id();
20339 return pos[id];
20340 });
20341 return this; // chaining
20342 };
20343
20344 /*
20345 The CoSE layout was written by Gerardo Huck.
20346 https://www.linkedin.com/in/gerardohuck/
20347
20348 Based on the following article:
20349 http://dl.acm.org/citation.cfm?id=1498047
20350
20351 Modifications tracked on Github.
20352 */
20353 var DEBUG;
20354 /**
20355 * @brief : default layout options
20356 */
20357
20358 var defaults$c = {
20359 // Called on `layoutready`
20360 ready: function ready() {},
20361 // Called on `layoutstop`
20362 stop: function stop() {},
20363 // Whether to animate while running the layout
20364 // true : Animate continuously as the layout is running
20365 // false : Just show the end result
20366 // 'end' : Animate with the end result, from the initial positions to the end positions
20367 animate: true,
20368 // Easing of the animation for animate:'end'
20369 animationEasing: undefined,
20370 // The duration of the animation for animate:'end'
20371 animationDuration: undefined,
20372 // A function that determines whether the node should be animated
20373 // All nodes animated by default on animate enabled
20374 // Non-animated nodes are positioned immediately when the layout starts
20375 animateFilter: function animateFilter(node, i) {
20376 return true;
20377 },
20378 // The layout animates only after this many milliseconds for animate:true
20379 // (prevents flashing on fast runs)
20380 animationThreshold: 250,
20381 // Number of iterations between consecutive screen positions update
20382 refresh: 20,
20383 // Whether to fit the network view after when done
20384 fit: true,
20385 // Padding on fit
20386 padding: 30,
20387 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20388 boundingBox: undefined,
20389 // Excludes the label when calculating node bounding boxes for the layout algorithm
20390 nodeDimensionsIncludeLabels: false,
20391 // Randomize the initial positions of the nodes (true) or use existing positions (false)
20392 randomize: false,
20393 // Extra spacing between components in non-compound graphs
20394 componentSpacing: 40,
20395 // Node repulsion (non overlapping) multiplier
20396 nodeRepulsion: function nodeRepulsion(node) {
20397 return 2048;
20398 },
20399 // Node repulsion (overlapping) multiplier
20400 nodeOverlap: 4,
20401 // Ideal edge (non nested) length
20402 idealEdgeLength: function idealEdgeLength(edge) {
20403 return 32;
20404 },
20405 // Divisor to compute edge forces
20406 edgeElasticity: function edgeElasticity(edge) {
20407 return 32;
20408 },
20409 // Nesting factor (multiplier) to compute ideal edge length for nested edges
20410 nestingFactor: 1.2,
20411 // Gravity force (constant)
20412 gravity: 1,
20413 // Maximum number of iterations to perform
20414 numIter: 1000,
20415 // Initial temperature (maximum node displacement)
20416 initialTemp: 1000,
20417 // Cooling factor (how the temperature is reduced between consecutive iterations
20418 coolingFactor: 0.99,
20419 // Lower temperature threshold (below this point the layout will end)
20420 minTemp: 1.0
20421 };
20422 /**
20423 * @brief : constructor
20424 * @arg options : object containing layout options
20425 */
20426
20427 function CoseLayout(options) {
20428 this.options = extend({}, defaults$c, options);
20429 this.options.layout = this;
20430 }
20431 /**
20432 * @brief : runs the layout
20433 */
20434
20435
20436 CoseLayout.prototype.run = function () {
20437 var options = this.options;
20438 var cy = options.cy;
20439 var layout = this;
20440 layout.stopped = false;
20441
20442 if (options.animate === true || options.animate === false) {
20443 layout.emit({
20444 type: 'layoutstart',
20445 layout: layout
20446 });
20447 } // Set DEBUG - Global variable
20448
20449
20450 if (true === options.debug) {
20451 DEBUG = true;
20452 } else {
20453 DEBUG = false;
20454 } // Initialize layout info
20455
20456
20457 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
20458
20459 if (DEBUG) {
20460 printLayoutInfo(layoutInfo);
20461 } // If required, randomize node positions
20462
20463
20464 if (options.randomize) {
20465 randomizePositions(layoutInfo);
20466 }
20467
20468 var startTime = performanceNow();
20469
20470 var refresh = function refresh() {
20471 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
20472
20473 if (true === options.fit) {
20474 cy.fit(options.padding);
20475 }
20476 };
20477
20478 var mainLoop = function mainLoop(i) {
20479 if (layout.stopped || i >= options.numIter) {
20480 // logDebug("Layout manually stopped. Stopping computation in step " + i);
20481 return false;
20482 } // Do one step in the phisical simulation
20483
20484
20485 step$1(layoutInfo, options); // Update temperature
20486
20487 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
20488
20489 if (layoutInfo.temperature < options.minTemp) {
20490 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
20491 return false;
20492 }
20493
20494 return true;
20495 };
20496
20497 var done = function done() {
20498 if (options.animate === true || options.animate === false) {
20499 refresh(); // Layout has finished
20500
20501 layout.one('layoutstop', options.stop);
20502 layout.emit({
20503 type: 'layoutstop',
20504 layout: layout
20505 });
20506 } else {
20507 var nodes = options.eles.nodes();
20508 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20509 nodes.layoutPositions(layout, options, getScaledPos);
20510 }
20511 };
20512
20513 var i = 0;
20514 var loopRet = true;
20515
20516 if (options.animate === true) {
20517 var frame = function frame() {
20518 var f = 0;
20519
20520 while (loopRet && f < options.refresh) {
20521 loopRet = mainLoop(i);
20522 i++;
20523 f++;
20524 }
20525
20526 if (!loopRet) {
20527 // it's done
20528 separateComponents(layoutInfo, options);
20529 done();
20530 } else {
20531 var now = performanceNow();
20532
20533 if (now - startTime >= options.animationThreshold) {
20534 refresh();
20535 }
20536
20537 requestAnimationFrame(frame);
20538 }
20539 };
20540
20541 frame();
20542 } else {
20543 while (loopRet) {
20544 loopRet = mainLoop(i);
20545 i++;
20546 }
20547
20548 separateComponents(layoutInfo, options);
20549 done();
20550 }
20551
20552 return this; // chaining
20553 };
20554 /**
20555 * @brief : called on continuous layouts to stop them before they finish
20556 */
20557
20558
20559 CoseLayout.prototype.stop = function () {
20560 this.stopped = true;
20561
20562 if (this.thread) {
20563 this.thread.stop();
20564 }
20565
20566 this.emit('layoutstop');
20567 return this; // chaining
20568 };
20569
20570 CoseLayout.prototype.destroy = function () {
20571 if (this.thread) {
20572 this.thread.stop();
20573 }
20574
20575 return this; // chaining
20576 };
20577 /**
20578 * @brief : Creates an object which is contains all the data
20579 * used in the layout process
20580 * @arg cy : cytoscape.js object
20581 * @return : layoutInfo object initialized
20582 */
20583
20584
20585 var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20586 // Shortcut
20587 var edges = options.eles.edges();
20588 var nodes = options.eles.nodes();
20589 var layoutInfo = {
20590 isCompound: cy.hasCompoundNodes(),
20591 layoutNodes: [],
20592 idToIndex: {},
20593 nodeSize: nodes.size(),
20594 graphSet: [],
20595 indexToGraph: [],
20596 layoutEdges: [],
20597 edgeSize: edges.size(),
20598 temperature: options.initialTemp,
20599 clientWidth: cy.width(),
20600 clientHeight: cy.width(),
20601 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
20602 x1: 0,
20603 y1: 0,
20604 w: cy.width(),
20605 h: cy.height()
20606 })
20607 };
20608 var components = options.eles.components();
20609 var id2cmptId = {};
20610
20611 for (var i = 0; i < components.length; i++) {
20612 var component = components[i];
20613
20614 for (var j = 0; j < component.length; j++) {
20615 var node = component[j];
20616 id2cmptId[node.id()] = i;
20617 }
20618 } // Iterate over all nodes, creating layout nodes
20619
20620
20621 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20622 var n = nodes[i];
20623 var nbb = n.layoutDimensions(options);
20624 var tempNode = {};
20625 tempNode.isLocked = n.locked();
20626 tempNode.id = n.data('id');
20627 tempNode.parentId = n.data('parent');
20628 tempNode.cmptId = id2cmptId[n.id()];
20629 tempNode.children = [];
20630 tempNode.positionX = n.position('x');
20631 tempNode.positionY = n.position('y');
20632 tempNode.offsetX = 0;
20633 tempNode.offsetY = 0;
20634 tempNode.height = nbb.w;
20635 tempNode.width = nbb.h;
20636 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20637 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20638 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20639 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20640 tempNode.padLeft = parseFloat(n.style('padding'));
20641 tempNode.padRight = parseFloat(n.style('padding'));
20642 tempNode.padTop = parseFloat(n.style('padding'));
20643 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20644
20645 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20646
20647 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20648
20649 layoutInfo.idToIndex[tempNode.id] = i;
20650 } // Inline implementation of a queue, used for traversing the graph in BFS order
20651
20652
20653 var queue = [];
20654 var start = 0; // Points to the start the queue
20655
20656 var end = -1; // Points to the end of the queue
20657
20658 var tempGraph = []; // Second pass to add child information and
20659 // initialize queue for hierarchical traversal
20660
20661 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20662 var n = layoutInfo.layoutNodes[i];
20663 var p_id = n.parentId; // Check if node n has a parent node
20664
20665 if (null != p_id) {
20666 // Add node Id to parent's list of children
20667 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20668 } else {
20669 // If a node doesn't have a parent, then it's in the root graph
20670 queue[++end] = n.id;
20671 tempGraph.push(n.id);
20672 }
20673 } // Add root graph to graphSet
20674
20675
20676 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20677
20678 while (start <= end) {
20679 // Get the node to visit and remove it from queue
20680 var node_id = queue[start++];
20681 var node_ix = layoutInfo.idToIndex[node_id];
20682 var node = layoutInfo.layoutNodes[node_ix];
20683 var children = node.children;
20684
20685 if (children.length > 0) {
20686 // Add children nodes as a new graph to graph set
20687 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20688
20689 for (var i = 0; i < children.length; i++) {
20690 queue[++end] = children[i];
20691 }
20692 }
20693 } // Create indexToGraph map
20694
20695
20696 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20697 var graph = layoutInfo.graphSet[i];
20698
20699 for (var j = 0; j < graph.length; j++) {
20700 var index = layoutInfo.idToIndex[graph[j]];
20701 layoutInfo.indexToGraph[index] = i;
20702 }
20703 } // Iterate over all edges, creating Layout Edges
20704
20705
20706 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20707 var e = edges[i];
20708 var tempEdge = {};
20709 tempEdge.id = e.data('id');
20710 tempEdge.sourceId = e.data('source');
20711 tempEdge.targetId = e.data('target'); // Compute ideal length
20712
20713 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20714 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20715
20716 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20717 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20718 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20719 var targetGraph = layoutInfo.indexToGraph[targetIx];
20720
20721 if (sourceGraph != targetGraph) {
20722 // Find lowest common graph ancestor
20723 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20724
20725 var lcaGraph = layoutInfo.graphSet[lca];
20726 var depth = 0; // Source depth
20727
20728 var tempNode = layoutInfo.layoutNodes[sourceIx];
20729
20730 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20731 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20732 depth++;
20733 } // Target depth
20734
20735
20736 tempNode = layoutInfo.layoutNodes[targetIx];
20737
20738 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20739 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20740 depth++;
20741 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20742 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20743 // ". Depth: " + depth);
20744 // Update idealLength
20745
20746
20747 idealLength *= depth * options.nestingFactor;
20748 }
20749
20750 tempEdge.idealLength = idealLength;
20751 tempEdge.elasticity = elasticity;
20752 layoutInfo.layoutEdges.push(tempEdge);
20753 } // Finally, return layoutInfo object
20754
20755
20756 return layoutInfo;
20757 };
20758 /**
20759 * @brief : This function finds the index of the lowest common
20760 * graph ancestor between 2 nodes in the subtree
20761 * (from the graph hierarchy induced tree) whose
20762 * root is graphIx
20763 *
20764 * @arg node1: node1's ID
20765 * @arg node2: node2's ID
20766 * @arg layoutInfo: layoutInfo object
20767 *
20768 */
20769
20770
20771 var findLCA = function findLCA(node1, node2, layoutInfo) {
20772 // Find their common ancester, starting from the root graph
20773 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20774
20775 if (2 > res.count) {
20776 // If aux function couldn't find the common ancester,
20777 // then it is the root graph
20778 return 0;
20779 } else {
20780 return res.graph;
20781 }
20782 };
20783 /**
20784 * @brief : Auxiliary function used for LCA computation
20785 *
20786 * @arg node1 : node1's ID
20787 * @arg node2 : node2's ID
20788 * @arg graphIx : subgraph index
20789 * @arg layoutInfo : layoutInfo object
20790 *
20791 * @return : object of the form {count: X, graph: Y}, where:
20792 * X is the number of ancesters (max: 2) found in
20793 * graphIx (and it's subgraphs),
20794 * Y is the graph index of the lowest graph containing
20795 * all X nodes
20796 */
20797
20798
20799 var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20800 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20801
20802 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20803 return {
20804 count: 2,
20805 graph: graphIx
20806 };
20807 } // Make recursive calls for all subgraphs
20808
20809
20810 var c = 0;
20811
20812 for (var i = 0; i < graph.length; i++) {
20813 var nodeId = graph[i];
20814 var nodeIx = layoutInfo.idToIndex[nodeId];
20815 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20816
20817 if (0 === children.length) {
20818 continue;
20819 }
20820
20821 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20822 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20823
20824 if (0 === result.count) {
20825 // Neither node1 nor node2 are present in this subgraph
20826 continue;
20827 } else if (1 === result.count) {
20828 // One of (node1, node2) is present in this subgraph
20829 c++;
20830
20831 if (2 === c) {
20832 // We've already found both nodes, no need to keep searching
20833 break;
20834 }
20835 } else {
20836 // Both nodes are present in this subgraph
20837 return result;
20838 }
20839 }
20840
20841 return {
20842 count: c,
20843 graph: graphIx
20844 };
20845 };
20846 /**
20847 * @brief: printsLayoutInfo into js console
20848 * Only used for debbuging
20849 */
20850
20851
20852 if (false) {
20853 var printLayoutInfo;
20854 }
20855 /**
20856 * @brief : Randomizes the position of all nodes
20857 */
20858
20859
20860 var randomizePositions = function randomizePositions(layoutInfo, cy) {
20861 var width = layoutInfo.clientWidth;
20862 var height = layoutInfo.clientHeight;
20863
20864 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20865 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20866
20867 if (0 === n.children.length && !n.isLocked) {
20868 n.positionX = Math.random() * width;
20869 n.positionY = Math.random() * height;
20870 }
20871 }
20872 };
20873
20874 var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20875 var bb = layoutInfo.boundingBox;
20876 var coseBB = {
20877 x1: Infinity,
20878 x2: -Infinity,
20879 y1: Infinity,
20880 y2: -Infinity
20881 };
20882
20883 if (options.boundingBox) {
20884 nodes.forEach(function (node) {
20885 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20886 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20887 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20888 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20889 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20890 });
20891 coseBB.w = coseBB.x2 - coseBB.x1;
20892 coseBB.h = coseBB.y2 - coseBB.y1;
20893 }
20894
20895 return function (ele, i) {
20896 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20897
20898 if (options.boundingBox) {
20899 // then add extra bounding box constraint
20900 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20901 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20902 return {
20903 x: bb.x1 + pctX * bb.w,
20904 y: bb.y1 + pctY * bb.h
20905 };
20906 } else {
20907 return {
20908 x: lnode.positionX,
20909 y: lnode.positionY
20910 };
20911 }
20912 };
20913 };
20914 /**
20915 * @brief : Updates the positions of nodes in the network
20916 * @arg layoutInfo : LayoutInfo object
20917 * @arg cy : Cytoscape object
20918 * @arg options : Layout options
20919 */
20920
20921
20922 var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20923 // var s = 'Refreshing positions';
20924 // logDebug(s);
20925 var layout = options.layout;
20926 var nodes = options.eles.nodes();
20927 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20928 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20929
20930 if (true !== layoutInfo.ready) {
20931 // s = 'Triggering layoutready';
20932 // logDebug(s);
20933 layoutInfo.ready = true;
20934 layout.one('layoutready', options.ready);
20935 layout.emit({
20936 type: 'layoutready',
20937 layout: this
20938 });
20939 }
20940 };
20941 /**
20942 * @brief : Logs a debug message in JS console, if DEBUG is ON
20943 */
20944 // var logDebug = function(text) {
20945 // if (DEBUG) {
20946 // console.debug(text);
20947 // }
20948 // };
20949
20950 /**
20951 * @brief : Performs one iteration of the physical simulation
20952 * @arg layoutInfo : LayoutInfo object already initialized
20953 * @arg cy : Cytoscape object
20954 * @arg options : Layout options
20955 */
20956
20957
20958 var step$1 = function step(layoutInfo, options, _step) {
20959 // var s = "\n\n###############################";
20960 // s += "\nSTEP: " + step;
20961 // s += "\n###############################\n";
20962 // logDebug(s);
20963 // Calculate node repulsions
20964 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20965
20966 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20967
20968 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20969
20970 propagateForces(layoutInfo); // Update positions based on calculated forces
20971
20972 updatePositions(layoutInfo);
20973 };
20974 /**
20975 * @brief : Computes the node repulsion forces
20976 */
20977
20978
20979 var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20980 // Go through each of the graphs in graphSet
20981 // Nodes only repel each other if they belong to the same graph
20982 // var s = 'calculateNodeForces';
20983 // logDebug(s);
20984 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20985 var graph = layoutInfo.graphSet[i];
20986 var numNodes = graph.length; // s = "Set: " + graph.toString();
20987 // logDebug(s);
20988 // Now get all the pairs of nodes
20989 // Only get each pair once, (A, B) = (B, A)
20990
20991 for (var j = 0; j < numNodes; j++) {
20992 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20993
20994 for (var k = j + 1; k < numNodes; k++) {
20995 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20996 nodeRepulsion(node1, node2, layoutInfo, options);
20997 }
20998 }
20999 }
21000 };
21001
21002 var randomDistance = function randomDistance(max) {
21003 return -max + 2 * max * Math.random();
21004 };
21005 /**
21006 * @brief : Compute the node repulsion forces between a pair of nodes
21007 */
21008
21009
21010 var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
21011 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
21012 var cmptId1 = node1.cmptId;
21013 var cmptId2 = node2.cmptId;
21014
21015 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
21016 return;
21017 } // Get direction of line connecting both node centers
21018
21019
21020 var directionX = node2.positionX - node1.positionX;
21021 var directionY = node2.positionY - node1.positionY;
21022 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
21023 // If both centers are the same, apply a random force
21024
21025 if (0 === directionX && 0 === directionY) {
21026 directionX = randomDistance(maxRandDist);
21027 directionY = randomDistance(maxRandDist);
21028 }
21029
21030 var overlap = nodesOverlap(node1, node2, directionX, directionY);
21031
21032 if (overlap > 0) {
21033 // s += "\nNodes DO overlap.";
21034 // s += "\nOverlap: " + overlap;
21035 // If nodes overlap, repulsion force is proportional
21036 // to the overlap
21037 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
21038
21039 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
21040
21041 var forceX = force * directionX / distance;
21042 var forceY = force * directionY / distance;
21043 } else {
21044 // s += "\nNodes do NOT overlap.";
21045 // If there's no overlap, force is inversely proportional
21046 // to squared distance
21047 // Get clipping points for both nodes
21048 var point1 = findClippingPoint(node1, directionX, directionY);
21049 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
21050
21051 var distanceX = point2.x - point1.x;
21052 var distanceY = point2.y - point1.y;
21053 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
21054 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
21055 // Compute the module and components of the force vector
21056
21057 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
21058 var forceX = force * distanceX / distance;
21059 var forceY = force * distanceY / distance;
21060 } // Apply force
21061
21062
21063 if (!node1.isLocked) {
21064 node1.offsetX -= forceX;
21065 node1.offsetY -= forceY;
21066 }
21067
21068 if (!node2.isLocked) {
21069 node2.offsetX += forceX;
21070 node2.offsetY += forceY;
21071 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
21072 // logDebug(s);
21073
21074
21075 return;
21076 };
21077 /**
21078 * @brief : Determines whether two nodes overlap or not
21079 * @return : Amount of overlapping (0 => no overlap)
21080 */
21081
21082
21083 var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
21084 if (dX > 0) {
21085 var overlapX = node1.maxX - node2.minX;
21086 } else {
21087 var overlapX = node2.maxX - node1.minX;
21088 }
21089
21090 if (dY > 0) {
21091 var overlapY = node1.maxY - node2.minY;
21092 } else {
21093 var overlapY = node2.maxY - node1.minY;
21094 }
21095
21096 if (overlapX >= 0 && overlapY >= 0) {
21097 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
21098 } else {
21099 return 0;
21100 }
21101 };
21102 /**
21103 * @brief : Finds the point in which an edge (direction dX, dY) intersects
21104 * the rectangular bounding box of it's source/target node
21105 */
21106
21107
21108 var findClippingPoint = function findClippingPoint(node, dX, dY) {
21109 // Shorcuts
21110 var X = node.positionX;
21111 var Y = node.positionY;
21112 var H = node.height || 1;
21113 var W = node.width || 1;
21114 var dirSlope = dY / dX;
21115 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
21116 // " . Height: " + H + ", Width: " + W +
21117 // "\nDirection " + dX + ", " + dY;
21118 //
21119 // Compute intersection
21120
21121 var res = {}; // Case: Vertical direction (up)
21122
21123 if (0 === dX && 0 < dY) {
21124 res.x = X; // s += "\nUp direction";
21125
21126 res.y = Y + H / 2;
21127 return res;
21128 } // Case: Vertical direction (down)
21129
21130
21131 if (0 === dX && 0 > dY) {
21132 res.x = X;
21133 res.y = Y + H / 2; // s += "\nDown direction";
21134
21135 return res;
21136 } // Case: Intersects the right border
21137
21138
21139 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
21140 res.x = X + W / 2;
21141 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
21142
21143 return res;
21144 } // Case: Intersects the left border
21145
21146
21147 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
21148 res.x = X - W / 2;
21149 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
21150
21151 return res;
21152 } // Case: Intersects the top border
21153
21154
21155 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
21156 res.x = X + H * dX / 2 / dY;
21157 res.y = Y + H / 2; // s += "\nTop border";
21158
21159 return res;
21160 } // Case: Intersects the bottom border
21161
21162
21163 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
21164 res.x = X - H * dX / 2 / dY;
21165 res.y = Y - H / 2; // s += "\nBottom border";
21166
21167 return res;
21168 } // s += "\nClipping point found at " + res.x + ", " + res.y;
21169 // logDebug(s);
21170
21171
21172 return res;
21173 };
21174 /**
21175 * @brief : Calculates all edge forces
21176 */
21177
21178
21179 var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
21180 // Iterate over all edges
21181 for (var i = 0; i < layoutInfo.edgeSize; i++) {
21182 // Get edge, source & target nodes
21183 var edge = layoutInfo.layoutEdges[i];
21184 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
21185 var source = layoutInfo.layoutNodes[sourceIx];
21186 var targetIx = layoutInfo.idToIndex[edge.targetId];
21187 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
21188
21189 var directionX = target.positionX - source.positionX;
21190 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
21191 // A random force has already been applied as node repulsion
21192
21193 if (0 === directionX && 0 === directionY) {
21194 continue;
21195 } // Get clipping points for both nodes
21196
21197
21198 var point1 = findClippingPoint(source, directionX, directionY);
21199 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
21200 var lx = point2.x - point1.x;
21201 var ly = point2.y - point1.y;
21202 var l = Math.sqrt(lx * lx + ly * ly);
21203 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
21204
21205 if (0 !== l) {
21206 var forceX = force * lx / l;
21207 var forceY = force * ly / l;
21208 } else {
21209 var forceX = 0;
21210 var forceY = 0;
21211 } // Add this force to target and source nodes
21212
21213
21214 if (!source.isLocked) {
21215 source.offsetX += forceX;
21216 source.offsetY += forceY;
21217 }
21218
21219 if (!target.isLocked) {
21220 target.offsetX -= forceX;
21221 target.offsetY -= forceY;
21222 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
21223 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
21224 // logDebug(s);
21225
21226 }
21227 };
21228 /**
21229 * @brief : Computes gravity forces for all nodes
21230 */
21231
21232
21233 var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
21234 var distThreshold = 1; // var s = 'calculateGravityForces';
21235 // logDebug(s);
21236
21237 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
21238 var graph = layoutInfo.graphSet[i];
21239 var numNodes = graph.length; // s = "Set: " + graph.toString();
21240 // logDebug(s);
21241 // Compute graph center
21242
21243 if (0 === i) {
21244 var centerX = layoutInfo.clientHeight / 2;
21245 var centerY = layoutInfo.clientWidth / 2;
21246 } else {
21247 // Get Parent node for this graph, and use its position as center
21248 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
21249 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
21250 var centerX = parent.positionX;
21251 var centerY = parent.positionY;
21252 } // s = "Center found at: " + centerX + ", " + centerY;
21253 // logDebug(s);
21254 // Apply force to all nodes in graph
21255
21256
21257 for (var j = 0; j < numNodes; j++) {
21258 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
21259
21260 if (node.isLocked) {
21261 continue;
21262 }
21263
21264 var dx = centerX - node.positionX;
21265 var dy = centerY - node.positionY;
21266 var d = Math.sqrt(dx * dx + dy * dy);
21267
21268 if (d > distThreshold) {
21269 var fx = options.gravity * dx / d;
21270 var fy = options.gravity * dy / d;
21271 node.offsetX += fx;
21272 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
21273 } // s += ": skypped since it's too close to center";
21274 // logDebug(s);
21275
21276 }
21277 }
21278 };
21279 /**
21280 * @brief : This function propagates the existing offsets from
21281 * parent nodes to its descendents.
21282 * @arg layoutInfo : layoutInfo Object
21283 * @arg cy : cytoscape Object
21284 * @arg options : Layout options
21285 */
21286
21287
21288 var propagateForces = function propagateForces(layoutInfo, options) {
21289 // Inline implementation of a queue, used for traversing the graph in BFS order
21290 var queue = [];
21291 var start = 0; // Points to the start the queue
21292
21293 var end = -1; // Points to the end of the queue
21294 // logDebug('propagateForces');
21295 // Start by visiting the nodes in the root graph
21296
21297 queue.push.apply(queue, layoutInfo.graphSet[0]);
21298 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
21299
21300 while (start <= end) {
21301 // Get the node to visit and remove it from queue
21302 var nodeId = queue[start++];
21303 var nodeIndex = layoutInfo.idToIndex[nodeId];
21304 var node = layoutInfo.layoutNodes[nodeIndex];
21305 var children = node.children; // We only need to process the node if it's compound
21306
21307 if (0 < children.length && !node.isLocked) {
21308 var offX = node.offsetX;
21309 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
21310 // ". OffsetX: " + offX + ". OffsetY: " + offY;
21311 // s += "\n Children: " + children.toString();
21312 // logDebug(s);
21313
21314 for (var i = 0; i < children.length; i++) {
21315 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
21316
21317 childNode.offsetX += offX;
21318 childNode.offsetY += offY; // Add children to queue to be visited
21319
21320 queue[++end] = children[i];
21321 } // Reset parent offsets
21322
21323
21324 node.offsetX = 0;
21325 node.offsetY = 0;
21326 }
21327 }
21328 };
21329 /**
21330 * @brief : Updates the layout model positions, based on
21331 * the accumulated forces
21332 */
21333
21334
21335 var updatePositions = function updatePositions(layoutInfo, options) {
21336 // var s = 'Updating positions';
21337 // logDebug(s);
21338 // Reset boundaries for compound nodes
21339 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21340 var n = layoutInfo.layoutNodes[i];
21341
21342 if (0 < n.children.length) {
21343 // logDebug("Resetting boundaries of compound node: " + n.id);
21344 n.maxX = undefined;
21345 n.minX = undefined;
21346 n.maxY = undefined;
21347 n.minY = undefined;
21348 }
21349 }
21350
21351 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21352 var n = layoutInfo.layoutNodes[i];
21353
21354 if (0 < n.children.length || n.isLocked) {
21355 // No need to set compound or locked node position
21356 // logDebug("Skipping position update of node: " + n.id);
21357 continue;
21358 } // s = "Node: " + n.id + " Previous position: (" +
21359 // n.positionX + ", " + n.positionY + ").";
21360 // Limit displacement in order to improve stability
21361
21362
21363 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
21364 n.positionX += tempForce.x;
21365 n.positionY += tempForce.y;
21366 n.offsetX = 0;
21367 n.offsetY = 0;
21368 n.minX = n.positionX - n.width;
21369 n.maxX = n.positionX + n.width;
21370 n.minY = n.positionY - n.height;
21371 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
21372 // logDebug(s);
21373 // Update ancestry boudaries
21374
21375 updateAncestryBoundaries(n, layoutInfo);
21376 } // Update size, position of compund nodes
21377
21378
21379 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21380 var n = layoutInfo.layoutNodes[i];
21381
21382 if (0 < n.children.length && !n.isLocked) {
21383 n.positionX = (n.maxX + n.minX) / 2;
21384 n.positionY = (n.maxY + n.minY) / 2;
21385 n.width = n.maxX - n.minX;
21386 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
21387 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
21388 // s += "\nWidth: " + n.width + ", Height: " + n.height;
21389 // logDebug(s);
21390 }
21391 }
21392 };
21393 /**
21394 * @brief : Limits a force (forceX, forceY) to be not
21395 * greater (in modulo) than max.
21396 8 Preserves force direction.
21397 */
21398
21399
21400 var limitForce = function limitForce(forceX, forceY, max) {
21401 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
21402 var force = Math.sqrt(forceX * forceX + forceY * forceY);
21403
21404 if (force > max) {
21405 var res = {
21406 x: max * forceX / force,
21407 y: max * forceY / force
21408 };
21409 } else {
21410 var res = {
21411 x: forceX,
21412 y: forceY
21413 };
21414 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
21415 // logDebug(s);
21416
21417
21418 return res;
21419 };
21420 /**
21421 * @brief : Function used for keeping track of compound node
21422 * sizes, since they should bound all their subnodes.
21423 */
21424
21425
21426 var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
21427 // var s = "Propagating new position/size of node " + node.id;
21428 var parentId = node.parentId;
21429
21430 if (null == parentId) {
21431 // If there's no parent, we are done
21432 // s += ". No parent node.";
21433 // logDebug(s);
21434 return;
21435 } // Get Parent Node
21436
21437
21438 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
21439 var flag = false; // MaxX
21440
21441 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
21442 p.maxX = node.maxX + p.padRight;
21443 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
21444 } // MinX
21445
21446
21447 if (null == p.minX || node.minX - p.padLeft < p.minX) {
21448 p.minX = node.minX - p.padLeft;
21449 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
21450 } // MaxY
21451
21452
21453 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
21454 p.maxY = node.maxY + p.padBottom;
21455 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
21456 } // MinY
21457
21458
21459 if (null == p.minY || node.minY - p.padTop < p.minY) {
21460 p.minY = node.minY - p.padTop;
21461 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
21462 } // If updated boundaries, propagate changes upward
21463
21464
21465 if (flag) {
21466 // logDebug(s);
21467 return updateAncestryBoundaries(p, layoutInfo);
21468 } // s += ". No changes in boundaries/position of parent node " + p.id;
21469 // logDebug(s);
21470
21471
21472 return;
21473 };
21474
21475 var separateComponents = function separateComponents(layoutInfo, options) {
21476 var nodes = layoutInfo.layoutNodes;
21477 var components = [];
21478
21479 for (var i = 0; i < nodes.length; i++) {
21480 var node = nodes[i];
21481 var cid = node.cmptId;
21482 var component = components[cid] = components[cid] || [];
21483 component.push(node);
21484 }
21485
21486 var totalA = 0;
21487
21488 for (var i = 0; i < components.length; i++) {
21489 var c = components[i];
21490
21491 if (!c) {
21492 continue;
21493 }
21494
21495 c.x1 = Infinity;
21496 c.x2 = -Infinity;
21497 c.y1 = Infinity;
21498 c.y2 = -Infinity;
21499
21500 for (var j = 0; j < c.length; j++) {
21501 var n = c[j];
21502 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
21503 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
21504 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
21505 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
21506 }
21507
21508 c.w = c.x2 - c.x1;
21509 c.h = c.y2 - c.y1;
21510 totalA += c.w * c.h;
21511 }
21512
21513 components.sort(function (c1, c2) {
21514 return c2.w * c2.h - c1.w * c1.h;
21515 });
21516 var x = 0;
21517 var y = 0;
21518 var usedW = 0;
21519 var rowH = 0;
21520 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
21521
21522 for (var i = 0; i < components.length; i++) {
21523 var c = components[i];
21524
21525 if (!c) {
21526 continue;
21527 }
21528
21529 for (var j = 0; j < c.length; j++) {
21530 var n = c[j];
21531
21532 if (!n.isLocked) {
21533 n.positionX += x - c.x1;
21534 n.positionY += y - c.y1;
21535 }
21536 }
21537
21538 x += c.w + options.componentSpacing;
21539 usedW += c.w + options.componentSpacing;
21540 rowH = Math.max(rowH, c.h);
21541
21542 if (usedW > maxRowW) {
21543 y += rowH + options.componentSpacing;
21544 x = 0;
21545 usedW = 0;
21546 rowH = 0;
21547 }
21548 }
21549 };
21550
21551 var defaults$d = {
21552 fit: true,
21553 // whether to fit the viewport to the graph
21554 padding: 30,
21555 // padding used on fit
21556 boundingBox: undefined,
21557 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21558 avoidOverlap: true,
21559 // prevents node overlap, may overflow boundingBox if not enough space
21560 avoidOverlapPadding: 10,
21561 // extra spacing around nodes when avoidOverlap: true
21562 nodeDimensionsIncludeLabels: false,
21563 // Excludes the label when calculating node bounding boxes for the layout algorithm
21564 spacingFactor: undefined,
21565 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21566 condense: false,
21567 // uses all available space on false, uses minimal space on true
21568 rows: undefined,
21569 // force num of rows in the grid
21570 cols: undefined,
21571 // force num of columns in the grid
21572 position: function position(node) {},
21573 // returns { row, col } for element
21574 sort: undefined,
21575 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21576 animate: false,
21577 // whether to transition the node positions
21578 animationDuration: 500,
21579 // duration of animation in ms if enabled
21580 animationEasing: undefined,
21581 // easing of animation if enabled
21582 animateFilter: function animateFilter(node, i) {
21583 return true;
21584 },
21585 // 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
21586 ready: undefined,
21587 // callback on layoutready
21588 stop: undefined,
21589 // callback on layoutstop
21590 transform: function transform(node, position) {
21591 return position;
21592 } // transform a given node position. Useful for changing flow direction in discrete layouts
21593
21594 };
21595
21596 function GridLayout(options) {
21597 this.options = extend({}, defaults$d, options);
21598 }
21599
21600 GridLayout.prototype.run = function () {
21601 var params = this.options;
21602 var options = params;
21603 var cy = params.cy;
21604 var eles = options.eles;
21605 var nodes = eles.nodes().not(':parent');
21606
21607 if (options.sort) {
21608 nodes = nodes.sort(options.sort);
21609 }
21610
21611 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21612 x1: 0,
21613 y1: 0,
21614 w: cy.width(),
21615 h: cy.height()
21616 });
21617
21618 if (bb.h === 0 || bb.w === 0) {
21619 nodes.layoutPositions(this, options, function (ele) {
21620 return {
21621 x: bb.x1,
21622 y: bb.y1
21623 };
21624 });
21625 } else {
21626 // width/height * splits^2 = cells where splits is number of times to split width
21627 var cells = nodes.size();
21628 var splits = Math.sqrt(cells * bb.h / bb.w);
21629 var rows = Math.round(splits);
21630 var cols = Math.round(bb.w / bb.h * splits);
21631
21632 var small = function small(val) {
21633 if (val == null) {
21634 return Math.min(rows, cols);
21635 } else {
21636 var min = Math.min(rows, cols);
21637
21638 if (min == rows) {
21639 rows = val;
21640 } else {
21641 cols = val;
21642 }
21643 }
21644 };
21645
21646 var large = function large(val) {
21647 if (val == null) {
21648 return Math.max(rows, cols);
21649 } else {
21650 var max = Math.max(rows, cols);
21651
21652 if (max == rows) {
21653 rows = val;
21654 } else {
21655 cols = val;
21656 }
21657 }
21658 };
21659
21660 var oRows = options.rows;
21661 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21662
21663 if (oRows != null && oCols != null) {
21664 rows = oRows;
21665 cols = oCols;
21666 } else if (oRows != null && oCols == null) {
21667 rows = oRows;
21668 cols = Math.ceil(cells / rows);
21669 } else if (oRows == null && oCols != null) {
21670 cols = oCols;
21671 rows = Math.ceil(cells / cols);
21672 } // otherwise use the automatic values and adjust accordingly
21673 // if rounding was up, see if we can reduce rows or columns
21674 else if (cols * rows > cells) {
21675 var sm = small();
21676 var lg = large(); // reducing the small side takes away the most cells, so try it first
21677
21678 if ((sm - 1) * lg >= cells) {
21679 small(sm - 1);
21680 } else if ((lg - 1) * sm >= cells) {
21681 large(lg - 1);
21682 }
21683 } else {
21684 // if rounding was too low, add rows or columns
21685 while (cols * rows < cells) {
21686 var _sm = small();
21687
21688 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21689
21690
21691 if ((_lg + 1) * _sm >= cells) {
21692 large(_lg + 1);
21693 } else {
21694 small(_sm + 1);
21695 }
21696 }
21697 }
21698
21699 var cellWidth = bb.w / cols;
21700 var cellHeight = bb.h / rows;
21701
21702 if (options.condense) {
21703 cellWidth = 0;
21704 cellHeight = 0;
21705 }
21706
21707 if (options.avoidOverlap) {
21708 for (var i = 0; i < nodes.length; i++) {
21709 var node = nodes[i];
21710 var pos = node._private.position;
21711
21712 if (pos.x == null || pos.y == null) {
21713 // for bb
21714 pos.x = 0;
21715 pos.y = 0;
21716 }
21717
21718 var nbb = node.layoutDimensions(options);
21719 var p = options.avoidOverlapPadding;
21720 var w = nbb.w + p;
21721 var h = nbb.h + p;
21722 cellWidth = Math.max(cellWidth, w);
21723 cellHeight = Math.max(cellHeight, h);
21724 }
21725 }
21726
21727 var cellUsed = {}; // e.g. 'c-0-2' => true
21728
21729 var used = function used(row, col) {
21730 return cellUsed['c-' + row + '-' + col] ? true : false;
21731 };
21732
21733 var use = function use(row, col) {
21734 cellUsed['c-' + row + '-' + col] = true;
21735 }; // to keep track of current cell position
21736
21737
21738 var row = 0;
21739 var col = 0;
21740
21741 var moveToNextCell = function moveToNextCell() {
21742 col++;
21743
21744 if (col >= cols) {
21745 col = 0;
21746 row++;
21747 }
21748 }; // get a cache of all the manual positions
21749
21750
21751 var id2manPos = {};
21752
21753 for (var _i = 0; _i < nodes.length; _i++) {
21754 var _node = nodes[_i];
21755 var rcPos = options.position(_node);
21756
21757 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21758 // must have at least row or col def'd
21759 var _pos = {
21760 row: rcPos.row,
21761 col: rcPos.col
21762 };
21763
21764 if (_pos.col === undefined) {
21765 // find unused col
21766 _pos.col = 0;
21767
21768 while (used(_pos.row, _pos.col)) {
21769 _pos.col++;
21770 }
21771 } else if (_pos.row === undefined) {
21772 // find unused row
21773 _pos.row = 0;
21774
21775 while (used(_pos.row, _pos.col)) {
21776 _pos.row++;
21777 }
21778 }
21779
21780 id2manPos[_node.id()] = _pos;
21781 use(_pos.row, _pos.col);
21782 }
21783 }
21784
21785 var getPos = function getPos(element, i) {
21786 var x, y;
21787
21788 if (element.locked() || element.isParent()) {
21789 return false;
21790 } // see if we have a manual position set
21791
21792
21793 var rcPos = id2manPos[element.id()];
21794
21795 if (rcPos) {
21796 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21797 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21798 } else {
21799 // otherwise set automatically
21800 while (used(row, col)) {
21801 moveToNextCell();
21802 }
21803
21804 x = col * cellWidth + cellWidth / 2 + bb.x1;
21805 y = row * cellHeight + cellHeight / 2 + bb.y1;
21806 use(row, col);
21807 moveToNextCell();
21808 }
21809
21810 return {
21811 x: x,
21812 y: y
21813 };
21814 };
21815
21816 nodes.layoutPositions(this, options, getPos);
21817 }
21818
21819 return this; // chaining
21820 };
21821
21822 var defaults$e = {
21823 ready: function ready() {},
21824 // on layoutready
21825 stop: function stop() {} // on layoutstop
21826
21827 }; // constructor
21828 // options : object containing layout options
21829
21830 function NullLayout(options) {
21831 this.options = extend({}, defaults$e, options);
21832 } // runs the layout
21833
21834
21835 NullLayout.prototype.run = function () {
21836 var options = this.options;
21837 var eles = options.eles; // elements to consider in the layout
21838
21839 var layout = this; // cy is automatically populated for us in the constructor
21840 // (disable eslint for next line as this serves as example layout code to external developers)
21841 // eslint-disable-next-line no-unused-vars
21842
21843 var cy = options.cy;
21844 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21845 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21846
21847 eles.nodes().positions(function () {
21848 return {
21849 x: 0,
21850 y: 0
21851 };
21852 }); // trigger layoutready when each node has had its position set at least once
21853
21854 layout.one('layoutready', options.ready);
21855 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21856
21857 layout.one('layoutstop', options.stop);
21858 layout.emit('layoutstop');
21859 return this; // chaining
21860 }; // called on continuous layouts to stop them before they finish
21861
21862
21863 NullLayout.prototype.stop = function () {
21864 return this; // chaining
21865 };
21866
21867 var defaults$f = {
21868 positions: undefined,
21869 // map of (node id) => (position obj); or function(node){ return somPos; }
21870 zoom: undefined,
21871 // the zoom level to set (prob want fit = false if set)
21872 pan: undefined,
21873 // the pan level to set (prob want fit = false if set)
21874 fit: true,
21875 // whether to fit to viewport
21876 padding: 30,
21877 // padding on fit
21878 animate: false,
21879 // whether to transition the node positions
21880 animationDuration: 500,
21881 // duration of animation in ms if enabled
21882 animationEasing: undefined,
21883 // easing of animation if enabled
21884 animateFilter: function animateFilter(node, i) {
21885 return true;
21886 },
21887 // 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
21888 ready: undefined,
21889 // callback on layoutready
21890 stop: undefined,
21891 // callback on layoutstop
21892 transform: function transform(node, position) {
21893 return position;
21894 } // transform a given node position. Useful for changing flow direction in discrete layouts
21895
21896 };
21897
21898 function PresetLayout(options) {
21899 this.options = extend({}, defaults$f, options);
21900 }
21901
21902 PresetLayout.prototype.run = function () {
21903 var options = this.options;
21904 var eles = options.eles;
21905 var nodes = eles.nodes();
21906 var posIsFn = fn(options.positions);
21907
21908 function getPosition(node) {
21909 if (options.positions == null) {
21910 return copyPosition(node.position());
21911 }
21912
21913 if (posIsFn) {
21914 return options.positions(node);
21915 }
21916
21917 var pos = options.positions[node._private.data.id];
21918
21919 if (pos == null) {
21920 return null;
21921 }
21922
21923 return pos;
21924 }
21925
21926 nodes.layoutPositions(this, options, function (node, i) {
21927 var position = getPosition(node);
21928
21929 if (node.locked() || position == null) {
21930 return false;
21931 }
21932
21933 return position;
21934 });
21935 return this; // chaining
21936 };
21937
21938 var defaults$g = {
21939 fit: true,
21940 // whether to fit to viewport
21941 padding: 30,
21942 // fit padding
21943 boundingBox: undefined,
21944 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21945 animate: false,
21946 // whether to transition the node positions
21947 animationDuration: 500,
21948 // duration of animation in ms if enabled
21949 animationEasing: undefined,
21950 // easing of animation if enabled
21951 animateFilter: function animateFilter(node, i) {
21952 return true;
21953 },
21954 // 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
21955 ready: undefined,
21956 // callback on layoutready
21957 stop: undefined,
21958 // callback on layoutstop
21959 transform: function transform(node, position) {
21960 return position;
21961 } // transform a given node position. Useful for changing flow direction in discrete layouts
21962
21963 };
21964
21965 function RandomLayout(options) {
21966 this.options = extend({}, defaults$g, options);
21967 }
21968
21969 RandomLayout.prototype.run = function () {
21970 var options = this.options;
21971 var cy = options.cy;
21972 var eles = options.eles;
21973 var nodes = eles.nodes().not(':parent');
21974 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21975 x1: 0,
21976 y1: 0,
21977 w: cy.width(),
21978 h: cy.height()
21979 });
21980
21981 var getPos = function getPos(node, i) {
21982 return {
21983 x: bb.x1 + Math.round(Math.random() * bb.w),
21984 y: bb.y1 + Math.round(Math.random() * bb.h)
21985 };
21986 };
21987
21988 nodes.layoutPositions(this, options, getPos);
21989 return this; // chaining
21990 };
21991
21992 var layout = [{
21993 name: 'breadthfirst',
21994 impl: BreadthFirstLayout
21995 }, {
21996 name: 'circle',
21997 impl: CircleLayout
21998 }, {
21999 name: 'concentric',
22000 impl: ConcentricLayout
22001 }, {
22002 name: 'cose',
22003 impl: CoseLayout
22004 }, {
22005 name: 'grid',
22006 impl: GridLayout
22007 }, {
22008 name: 'null',
22009 impl: NullLayout
22010 }, {
22011 name: 'preset',
22012 impl: PresetLayout
22013 }, {
22014 name: 'random',
22015 impl: RandomLayout
22016 }];
22017
22018 function NullRenderer(options) {
22019 this.options = options;
22020 this.notifications = 0; // for testing
22021 }
22022
22023 var noop$1 = function noop() {};
22024
22025 var throwImgErr = function throwImgErr() {
22026 throw new Error('A headless instance can not render images');
22027 };
22028
22029 NullRenderer.prototype = {
22030 recalculateRenderedStyle: noop$1,
22031 notify: function notify() {
22032 this.notifications++;
22033 },
22034 init: noop$1,
22035 isHeadless: function isHeadless() {
22036 return true;
22037 },
22038 png: throwImgErr,
22039 jpg: throwImgErr
22040 };
22041
22042 var BRp = {};
22043 BRp.arrowShapeWidth = 0.3;
22044
22045 BRp.registerArrowShapes = function () {
22046 var arrowShapes = this.arrowShapes = {};
22047 var renderer = this; // Contract for arrow shapes:
22048 // 0, 0 is arrow tip
22049 // (0, 1) is direction towards node
22050 // (1, 0) is right
22051 //
22052 // functional api:
22053 // collide: check x, y in shape
22054 // roughCollide: called before collide, no false negatives
22055 // draw: draw
22056 // spacing: dist(arrowTip, nodeBoundary)
22057 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
22058
22059 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
22060 var x1 = translation.x - size / 2 - padding;
22061 var x2 = translation.x + size / 2 + padding;
22062 var y1 = translation.y - size / 2 - padding;
22063 var y2 = translation.y + size / 2 + padding;
22064 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
22065 return inside;
22066 };
22067
22068 var transform = function transform(x, y, size, angle, translation) {
22069 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
22070 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
22071 var xScaled = xRotated * size;
22072 var yScaled = yRotated * size;
22073 var xTranslated = xScaled + translation.x;
22074 var yTranslated = yScaled + translation.y;
22075 return {
22076 x: xTranslated,
22077 y: yTranslated
22078 };
22079 };
22080
22081 var transformPoints = function transformPoints(pts, size, angle, translation) {
22082 var retPts = [];
22083
22084 for (var i = 0; i < pts.length; i += 2) {
22085 var x = pts[i];
22086 var y = pts[i + 1];
22087 retPts.push(transform(x, y, size, angle, translation));
22088 }
22089
22090 return retPts;
22091 };
22092
22093 var pointsToArr = function pointsToArr(pts) {
22094 var ret = [];
22095
22096 for (var i = 0; i < pts.length; i++) {
22097 var p = pts[i];
22098 ret.push(p.x, p.y);
22099 }
22100
22101 return ret;
22102 };
22103
22104 var standardGap = function standardGap(edge) {
22105 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
22106 };
22107
22108 var defineArrowShape = function defineArrowShape(name, defn) {
22109 if (string(defn)) {
22110 defn = arrowShapes[defn];
22111 }
22112
22113 arrowShapes[name] = extend({
22114 name: name,
22115 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
22116 collide: function collide(x, y, size, angle, translation, padding) {
22117 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22118 var inside = pointInsidePolygonPoints(x, y, points);
22119 return inside;
22120 },
22121 roughCollide: bbCollide,
22122 draw: function draw(context, size, angle, translation) {
22123 var points = transformPoints(this.points, size, angle, translation);
22124 renderer.arrowShapeImpl('polygon')(context, points);
22125 },
22126 spacing: function spacing(edge) {
22127 return 0;
22128 },
22129 gap: standardGap
22130 }, defn);
22131 };
22132
22133 defineArrowShape('none', {
22134 collide: falsify,
22135 roughCollide: falsify,
22136 draw: noop,
22137 spacing: zeroify,
22138 gap: zeroify
22139 });
22140 defineArrowShape('triangle', {
22141 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
22142 });
22143 defineArrowShape('arrow', 'triangle');
22144 defineArrowShape('triangle-backcurve', {
22145 points: arrowShapes['triangle'].points,
22146 controlPoint: [0, -0.15],
22147 roughCollide: bbCollide,
22148 draw: function draw(context, size, angle, translation, edgeWidth) {
22149 var ptsTrans = transformPoints(this.points, size, angle, translation);
22150 var ctrlPt = this.controlPoint;
22151 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
22152 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
22153 },
22154 gap: function gap(edge) {
22155 return standardGap(edge) * 0.8;
22156 }
22157 });
22158 defineArrowShape('triangle-tee', {
22159 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
22160 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
22161 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22162 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22163 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
22164 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
22165 return inside;
22166 },
22167 draw: function draw(context, size, angle, translation, edgeWidth) {
22168 var triPts = transformPoints(this.points, size, angle, translation);
22169 var teePts = transformPoints(this.pointsTee, size, angle, translation);
22170 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
22171 }
22172 });
22173 defineArrowShape('circle-triangle', {
22174 radius: 0.15,
22175 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
22176 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22177 var t = translation;
22178 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
22179 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22180 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
22181 },
22182 draw: function draw(context, size, angle, translation, edgeWidth) {
22183 var triPts = transformPoints(this.pointsTr, size, angle, translation);
22184 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
22185 },
22186 spacing: function spacing(edge) {
22187 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
22188 }
22189 });
22190 defineArrowShape('triangle-cross', {
22191 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
22192 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
22193 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
22194 0.15, -0.4],
22195 crossLinePts: function crossLinePts(size, edgeWidth) {
22196 // shift points so that the distance between the cross points matches edge width
22197 var p = this.baseCrossLinePts.slice();
22198 var shiftFactor = edgeWidth / size;
22199 var y0 = 3;
22200 var y1 = 5;
22201 p[y0] = p[y0] - shiftFactor;
22202 p[y1] = p[y1] - shiftFactor;
22203 return p;
22204 },
22205 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22206 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22207 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
22208 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
22209 return inside;
22210 },
22211 draw: function draw(context, size, angle, translation, edgeWidth) {
22212 var triPts = transformPoints(this.points, size, angle, translation);
22213 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
22214 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
22215 }
22216 });
22217 defineArrowShape('vee', {
22218 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
22219 gap: function gap(edge) {
22220 return standardGap(edge) * 0.525;
22221 }
22222 });
22223 defineArrowShape('circle', {
22224 radius: 0.15,
22225 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22226 var t = translation;
22227 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
22228 return inside;
22229 },
22230 draw: function draw(context, size, angle, translation, edgeWidth) {
22231 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
22232 },
22233 spacing: function spacing(edge) {
22234 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
22235 }
22236 });
22237 defineArrowShape('tee', {
22238 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
22239 spacing: function spacing(edge) {
22240 return 1;
22241 },
22242 gap: function gap(edge) {
22243 return 1;
22244 }
22245 });
22246 defineArrowShape('square', {
22247 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
22248 });
22249 defineArrowShape('diamond', {
22250 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
22251 gap: function gap(edge) {
22252 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
22253 }
22254 });
22255 defineArrowShape('chevron', {
22256 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
22257 gap: function gap(edge) {
22258 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
22259 }
22260 });
22261 };
22262
22263 var BRp$1 = {}; // Project mouse
22264
22265 BRp$1.projectIntoViewport = function (clientX, clientY) {
22266 var cy = this.cy;
22267 var offsets = this.findContainerClientCoords();
22268 var offsetLeft = offsets[0];
22269 var offsetTop = offsets[1];
22270 var scale = offsets[4];
22271 var pan = cy.pan();
22272 var zoom = cy.zoom();
22273 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
22274 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
22275 return [x, y];
22276 };
22277
22278 BRp$1.findContainerClientCoords = function () {
22279 if (this.containerBB) {
22280 return this.containerBB;
22281 }
22282
22283 var container = this.container;
22284 var rect = container.getBoundingClientRect();
22285 var style = window$1.getComputedStyle(container);
22286
22287 var styleValue = function styleValue(name) {
22288 return parseFloat(style.getPropertyValue(name));
22289 };
22290
22291 var padding = {
22292 left: styleValue('padding-left'),
22293 right: styleValue('padding-right'),
22294 top: styleValue('padding-top'),
22295 bottom: styleValue('padding-bottom')
22296 };
22297 var border = {
22298 left: styleValue('border-left-width'),
22299 right: styleValue('border-right-width'),
22300 top: styleValue('border-top-width'),
22301 bottom: styleValue('border-bottom-width')
22302 };
22303 var clientWidth = container.clientWidth;
22304 var clientHeight = container.clientHeight;
22305 var paddingHor = padding.left + padding.right;
22306 var paddingVer = padding.top + padding.bottom;
22307 var borderHor = border.left + border.right;
22308 var scale = rect.width / (clientWidth + borderHor);
22309 var unscaledW = clientWidth - paddingHor;
22310 var unscaledH = clientHeight - paddingVer;
22311 var left = rect.left + padding.left + border.left;
22312 var top = rect.top + padding.top + border.top;
22313 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
22314 };
22315
22316 BRp$1.invalidateContainerClientCoordsCache = function () {
22317 this.containerBB = null;
22318 };
22319
22320 BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
22321 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
22322 };
22323
22324 BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
22325 var self = this;
22326 var r = this;
22327 var eles = r.getCachedZSortedEles();
22328 var near = []; // 1 node max, 1 edge max
22329
22330 var zoom = r.cy.zoom();
22331 var hasCompounds = r.cy.hasCompoundNodes();
22332 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
22333 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
22334 var labelThreshold = (isTouch ? 8 : 2) / zoom;
22335 var minSqDist = Infinity;
22336 var nearEdge;
22337 var nearNode;
22338
22339 if (interactiveElementsOnly) {
22340 eles = eles.interactive;
22341 }
22342
22343 function addEle(ele, sqDist) {
22344 if (ele.isNode()) {
22345 if (nearNode) {
22346 return; // can't replace node
22347 } else {
22348 nearNode = ele;
22349 near.push(ele);
22350 }
22351 }
22352
22353 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
22354 if (nearEdge) {
22355 // then replace existing edge
22356 // can replace only if same z-index
22357 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) {
22358 for (var i = 0; i < near.length; i++) {
22359 if (near[i].isEdge()) {
22360 near[i] = ele;
22361 nearEdge = ele;
22362 minSqDist = sqDist != null ? sqDist : minSqDist;
22363 break;
22364 }
22365 }
22366 }
22367 } else {
22368 near.push(ele);
22369 nearEdge = ele;
22370 minSqDist = sqDist != null ? sqDist : minSqDist;
22371 }
22372 }
22373 }
22374
22375 function checkNode(node) {
22376 var width = node.outerWidth() + 2 * nodeThreshold;
22377 var height = node.outerHeight() + 2 * nodeThreshold;
22378 var hw = width / 2;
22379 var hh = height / 2;
22380 var pos = node.position();
22381
22382 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
22383 && pos.y - hh <= y && y <= pos.y + hh // bb check y
22384 ) {
22385 var shape = r.nodeShapes[self.getNodeShape(node)];
22386
22387 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
22388 addEle(node, 0);
22389 return true;
22390 }
22391 }
22392 }
22393
22394 function checkEdge(edge) {
22395 var _p = edge._private;
22396 var rs = _p.rscratch;
22397 var styleWidth = edge.pstyle('width').pfValue;
22398 var scale = edge.pstyle('arrow-scale').value;
22399 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
22400
22401 var widthSq = width * width;
22402 var width2 = width * 2;
22403 var src = _p.source;
22404 var tgt = _p.target;
22405 var sqDist;
22406
22407 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
22408 var pts = rs.allpts;
22409
22410 for (var i = 0; i + 3 < pts.length; i += 2) {
22411 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]))) {
22412 addEle(edge, sqDist);
22413 return true;
22414 }
22415 }
22416 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22417 var pts = rs.allpts;
22418
22419 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
22420 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]))) {
22421 addEle(edge, sqDist);
22422 return true;
22423 }
22424 }
22425 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
22426
22427
22428 var src = src || _p.source;
22429 var tgt = tgt || _p.target;
22430 var arSize = self.getArrowWidth(styleWidth, scale);
22431 var arrows = [{
22432 name: 'source',
22433 x: rs.arrowStartX,
22434 y: rs.arrowStartY,
22435 angle: rs.srcArrowAngle
22436 }, {
22437 name: 'target',
22438 x: rs.arrowEndX,
22439 y: rs.arrowEndY,
22440 angle: rs.tgtArrowAngle
22441 }, {
22442 name: 'mid-source',
22443 x: rs.midX,
22444 y: rs.midY,
22445 angle: rs.midsrcArrowAngle
22446 }, {
22447 name: 'mid-target',
22448 x: rs.midX,
22449 y: rs.midY,
22450 angle: rs.midtgtArrowAngle
22451 }];
22452
22453 for (var i = 0; i < arrows.length; i++) {
22454 var ar = arrows[i];
22455 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
22456 var edgeWidth = edge.pstyle('width').pfValue;
22457
22458 if (shape.roughCollide(x, y, arSize, ar.angle, {
22459 x: ar.x,
22460 y: ar.y
22461 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
22462 x: ar.x,
22463 y: ar.y
22464 }, edgeWidth, edgeThreshold)) {
22465 addEle(edge);
22466 return true;
22467 }
22468 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
22469
22470
22471 if (hasCompounds && near.length > 0) {
22472 checkNode(src);
22473 checkNode(tgt);
22474 }
22475 }
22476
22477 function preprop(obj, name, pre) {
22478 return getPrefixedProperty(obj, name, pre);
22479 }
22480
22481 function checkLabel(ele, prefix) {
22482 var _p = ele._private;
22483 var th = labelThreshold;
22484 var prefixDash;
22485
22486 if (prefix) {
22487 prefixDash = prefix + '-';
22488 } else {
22489 prefixDash = '';
22490 }
22491
22492 ele.boundingBox();
22493 var bb = _p.labelBounds[prefix || 'main'];
22494 var text = ele.pstyle(prefixDash + 'label').value;
22495 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
22496
22497 if (!eventsEnabled || !text) {
22498 return;
22499 }
22500
22501 var rstyle = _p.rstyle;
22502 var lx = preprop(rstyle, 'labelX', prefix);
22503 var ly = preprop(rstyle, 'labelY', prefix);
22504 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
22505 var lx1 = bb.x1 - th;
22506 var lx2 = bb.x2 + th;
22507 var ly1 = bb.y1 - th;
22508 var ly2 = bb.y2 + th;
22509
22510 if (theta) {
22511 var cos = Math.cos(theta);
22512 var sin = Math.sin(theta);
22513
22514 var rotate = function rotate(x, y) {
22515 x = x - lx;
22516 y = y - ly;
22517 return {
22518 x: x * cos - y * sin + lx,
22519 y: x * sin + y * cos + ly
22520 };
22521 };
22522
22523 var px1y1 = rotate(lx1, ly1);
22524 var px1y2 = rotate(lx1, ly2);
22525 var px2y1 = rotate(lx2, ly1);
22526 var px2y2 = rotate(lx2, ly2);
22527 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
22528
22529 if (pointInsidePolygonPoints(x, y, points)) {
22530 addEle(ele);
22531 return true;
22532 }
22533 } else {
22534 // do a cheaper bb check
22535 if (inBoundingBox(bb, x, y)) {
22536 addEle(ele);
22537 return true;
22538 }
22539 }
22540 }
22541
22542 for (var i = eles.length - 1; i >= 0; i--) {
22543 // reverse order for precedence
22544 var ele = eles[i];
22545
22546 if (ele.isNode()) {
22547 checkNode(ele) || checkLabel(ele);
22548 } else {
22549 // then edge
22550 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
22551 }
22552 }
22553
22554 return near;
22555 }; // 'Give me everything from this box'
22556
22557
22558 BRp$1.getAllInBox = function (x1, y1, x2, y2) {
22559 var eles = this.getCachedZSortedEles().interactive;
22560 var box = [];
22561 var x1c = Math.min(x1, x2);
22562 var x2c = Math.max(x1, x2);
22563 var y1c = Math.min(y1, y2);
22564 var y2c = Math.max(y1, y2);
22565 x1 = x1c;
22566 x2 = x2c;
22567 y1 = y1c;
22568 y2 = y2c;
22569 var boxBb = makeBoundingBox({
22570 x1: x1,
22571 y1: y1,
22572 x2: x2,
22573 y2: y2
22574 });
22575
22576 for (var e = 0; e < eles.length; e++) {
22577 var ele = eles[e];
22578
22579 if (ele.isNode()) {
22580 var node = ele;
22581 var nodeBb = node.boundingBox({
22582 includeNodes: true,
22583 includeEdges: false,
22584 includeLabels: false
22585 });
22586
22587 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22588 box.push(node);
22589 }
22590 } else {
22591 var edge = ele;
22592 var _p = edge._private;
22593 var rs = _p.rscratch;
22594
22595 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22596 continue;
22597 }
22598
22599 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22600 continue;
22601 }
22602
22603 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22604 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22605 var allInside = true;
22606
22607 for (var i = 0; i < pts.length; i++) {
22608 if (!pointInBoundingBox(boxBb, pts[i])) {
22609 allInside = false;
22610 break;
22611 }
22612 }
22613
22614 if (allInside) {
22615 box.push(edge);
22616 }
22617 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22618 box.push(edge);
22619 }
22620 }
22621 }
22622
22623 return box;
22624 };
22625
22626 var BRp$2 = {};
22627
22628 BRp$2.calculateArrowAngles = function (edge) {
22629 var rs = edge._private.rscratch;
22630 var isHaystack = rs.edgeType === 'haystack';
22631 var isBezier = rs.edgeType === 'bezier';
22632 var isMultibezier = rs.edgeType === 'multibezier';
22633 var isSegments = rs.edgeType === 'segments';
22634 var isCompound = rs.edgeType === 'compound';
22635 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22636
22637 var dispX, dispY;
22638 var startX, startY, endX, endY, midX, midY;
22639
22640 if (isHaystack) {
22641 startX = rs.haystackPts[0];
22642 startY = rs.haystackPts[1];
22643 endX = rs.haystackPts[2];
22644 endY = rs.haystackPts[3];
22645 } else {
22646 startX = rs.arrowStartX;
22647 startY = rs.arrowStartY;
22648 endX = rs.arrowEndX;
22649 endY = rs.arrowEndY;
22650 }
22651
22652 midX = rs.midX;
22653 midY = rs.midY; // source
22654 //
22655
22656 if (isSegments) {
22657 dispX = startX - rs.segpts[0];
22658 dispY = startY - rs.segpts[1];
22659 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22660 var pts = rs.allpts;
22661 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22662 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22663 dispX = startX - bX;
22664 dispY = startY - bY;
22665 } else {
22666 dispX = startX - midX;
22667 dispY = startY - midY;
22668 }
22669
22670 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22671 //
22672
22673 var midX = rs.midX;
22674 var midY = rs.midY;
22675
22676 if (isHaystack) {
22677 midX = (startX + endX) / 2;
22678 midY = (startY + endY) / 2;
22679 }
22680
22681 dispX = endX - startX;
22682 dispY = endY - startY;
22683
22684 if (isSegments) {
22685 var pts = rs.allpts;
22686
22687 if (pts.length / 2 % 2 === 0) {
22688 var i2 = pts.length / 2;
22689 var i1 = i2 - 2;
22690 dispX = pts[i2] - pts[i1];
22691 dispY = pts[i2 + 1] - pts[i1 + 1];
22692 } else {
22693 var i2 = pts.length / 2 - 1;
22694 var i1 = i2 - 2;
22695 var i3 = i2 + 2;
22696 dispX = pts[i2] - pts[i1];
22697 dispY = pts[i2 + 1] - pts[i1 + 1];
22698 }
22699 } else if (isMultibezier || isCompound || isSelf) {
22700 var pts = rs.allpts;
22701 var cpts = rs.ctrlpts;
22702 var bp0x, bp0y;
22703 var bp1x, bp1y;
22704
22705 if (cpts.length / 2 % 2 === 0) {
22706 var p0 = pts.length / 2 - 1; // startpt
22707
22708 var ic = p0 + 2;
22709 var p1 = ic + 2;
22710 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22711 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22712 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22713 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22714 } else {
22715 var ic = pts.length / 2 - 1; // ctrpt
22716
22717 var p0 = ic - 2; // startpt
22718
22719 var p1 = ic + 2; // endpt
22720
22721 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22722 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22723 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22724 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22725 }
22726
22727 dispX = bp1x - bp0x;
22728 dispY = bp1y - bp0y;
22729 }
22730
22731 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22732 rs.midDispX = dispX;
22733 rs.midDispY = dispY; // mid source
22734 //
22735
22736 dispX *= -1;
22737 dispY *= -1;
22738
22739 if (isSegments) {
22740 var pts = rs.allpts;
22741
22742 if (pts.length / 2 % 2 === 0) ; else {
22743 var i2 = pts.length / 2 - 1;
22744 var i3 = i2 + 2;
22745 dispX = -(pts[i3] - pts[i2]);
22746 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22747 }
22748 }
22749
22750 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22751 //
22752
22753 if (isSegments) {
22754 dispX = endX - rs.segpts[rs.segpts.length - 2];
22755 dispY = endY - rs.segpts[rs.segpts.length - 1];
22756 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22757 var pts = rs.allpts;
22758 var l = pts.length;
22759 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22760 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22761 dispX = endX - bX;
22762 dispY = endY - bY;
22763 } else {
22764 dispX = endX - midX;
22765 dispY = endY - midY;
22766 }
22767
22768 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22769 };
22770
22771 BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22772 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22773 var cachedVal = cache[edgeWidth + ', ' + scale];
22774
22775 if (cachedVal) {
22776 return cachedVal;
22777 }
22778
22779 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22780 cache[edgeWidth + ', ' + scale] = cachedVal;
22781 return cachedVal;
22782 };
22783
22784 var BRp$3 = {};
22785
22786 BRp$3.findHaystackPoints = function (edges) {
22787 for (var i = 0; i < edges.length; i++) {
22788 var edge = edges[i];
22789 var _p = edge._private;
22790 var rs = _p.rscratch;
22791
22792 if (!rs.haystack) {
22793 var angle = Math.random() * 2 * Math.PI;
22794 rs.source = {
22795 x: Math.cos(angle),
22796 y: Math.sin(angle)
22797 };
22798 angle = Math.random() * 2 * Math.PI;
22799 rs.target = {
22800 x: Math.cos(angle),
22801 y: Math.sin(angle)
22802 };
22803 }
22804
22805 var src = _p.source;
22806 var tgt = _p.target;
22807 var srcPos = src.position();
22808 var tgtPos = tgt.position();
22809 var srcW = src.width();
22810 var tgtW = tgt.width();
22811 var srcH = src.height();
22812 var tgtH = tgt.height();
22813 var radius = edge.pstyle('haystack-radius').value;
22814 var halfRadius = radius / 2; // b/c have to half width/height
22815
22816 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];
22817 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22818 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22819
22820 rs.edgeType = 'haystack';
22821 rs.haystack = true;
22822 this.storeEdgeProjections(edge);
22823 this.calculateArrowAngles(edge);
22824 this.recalculateEdgeLabelProjections(edge);
22825 this.calculateLabelAngles(edge);
22826 }
22827 };
22828
22829 BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22830 // Segments (multiple straight lines)
22831 var rs = edge._private.rscratch;
22832 var posPts = pairInfo.posPts,
22833 intersectionPts = pairInfo.intersectionPts,
22834 vectorNormInverse = pairInfo.vectorNormInverse;
22835 var edgeDistances = edge.pstyle('edge-distances').value;
22836 var segmentWs = edge.pstyle('segment-weights');
22837 var segmentDs = edge.pstyle('segment-distances');
22838 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22839 rs.edgeType = 'segments';
22840 rs.segpts = [];
22841
22842 for (var s = 0; s < segmentsN; s++) {
22843 var w = segmentWs.pfValue[s];
22844 var d = segmentDs.pfValue[s];
22845 var w1 = 1 - w;
22846 var w2 = w;
22847 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22848 var adjustedMidpt = {
22849 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22850 y: midptPts.y1 * w1 + midptPts.y2 * w2
22851 };
22852 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22853 }
22854 };
22855
22856 BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22857 // Self-edge
22858 var rs = edge._private.rscratch;
22859 var dirCounts = pairInfo.dirCounts,
22860 srcPos = pairInfo.srcPos;
22861 var ctrlptDists = edge.pstyle('control-point-distances');
22862 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22863 var loopDir = edge.pstyle('loop-direction').pfValue;
22864 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22865 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22866 rs.edgeType = 'self';
22867 var j = i;
22868 var loopDist = stepSize;
22869
22870 if (edgeIsUnbundled) {
22871 j = 0;
22872 loopDist = ctrlptDist;
22873 }
22874
22875 var loopAngle = loopDir - Math.PI / 2;
22876 var outAngle = loopAngle - loopSwp / 2;
22877 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22878
22879 var dc = String(loopDir + '_' + loopSwp);
22880 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22881 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)];
22882 };
22883
22884 BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22885 // Compound edge
22886 var rs = edge._private.rscratch;
22887 rs.edgeType = 'compound';
22888 var srcPos = pairInfo.srcPos,
22889 tgtPos = pairInfo.tgtPos,
22890 srcW = pairInfo.srcW,
22891 srcH = pairInfo.srcH,
22892 tgtW = pairInfo.tgtW,
22893 tgtH = pairInfo.tgtH;
22894 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22895 var ctrlptDists = edge.pstyle('control-point-distances');
22896 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22897 var j = i;
22898 var loopDist = stepSize;
22899
22900 if (edgeIsUnbundled) {
22901 j = 0;
22902 loopDist = ctrlptDist;
22903 }
22904
22905 var loopW = 50;
22906 var loopaPos = {
22907 x: srcPos.x - srcW / 2,
22908 y: srcPos.y - srcH / 2
22909 };
22910 var loopbPos = {
22911 x: tgtPos.x - tgtW / 2,
22912 y: tgtPos.y - tgtH / 2
22913 };
22914 var loopPos = {
22915 x: Math.min(loopaPos.x, loopbPos.x),
22916 y: Math.min(loopaPos.y, loopbPos.y)
22917 }; // avoids cases with impossible beziers
22918
22919 var minCompoundStretch = 0.5;
22920 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22921 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22922 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];
22923 };
22924
22925 BRp$3.findStraightEdgePoints = function (edge) {
22926 // Straight edge within bundle
22927 edge._private.rscratch.edgeType = 'straight';
22928 };
22929
22930 BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22931 var rs = edge._private.rscratch;
22932 var vectorNormInverse = pairInfo.vectorNormInverse,
22933 posPts = pairInfo.posPts,
22934 intersectionPts = pairInfo.intersectionPts;
22935 var edgeDistances = edge.pstyle('edge-distances').value;
22936 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22937 var ctrlptDists = edge.pstyle('control-point-distances');
22938 var ctrlptWs = edge.pstyle('control-point-weights');
22939 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22940 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22941 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22942
22943 var multi = edgeIsUnbundled;
22944 rs.edgeType = multi ? 'multibezier' : 'bezier';
22945 rs.ctrlpts = [];
22946
22947 for (var b = 0; b < bezierN; b++) {
22948 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22949 var manctrlptDist = void 0;
22950 var sign = signum(normctrlptDist);
22951
22952 if (multi) {
22953 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22954
22955 ctrlptWeight = ctrlptWs.value[b];
22956 }
22957
22958 if (edgeIsUnbundled) {
22959 // multi or single unbundled
22960 manctrlptDist = ctrlptDist;
22961 } else {
22962 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22963 }
22964
22965 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22966 var w1 = 1 - ctrlptWeight;
22967 var w2 = ctrlptWeight;
22968 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22969 var adjustedMidpt = {
22970 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22971 y: midptPts.y1 * w1 + midptPts.y2 * w2
22972 };
22973 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22974 }
22975 };
22976
22977 BRp$3.findTaxiPoints = function (edge, pairInfo) {
22978 // Taxicab geometry with two turns maximum
22979 var rs = edge._private.rscratch;
22980 rs.edgeType = 'segments';
22981 var VERTICAL = 'vertical';
22982 var HORIZONTAL = 'horizontal';
22983 var LEFTWARD = 'leftward';
22984 var RIGHTWARD = 'rightward';
22985 var DOWNWARD = 'downward';
22986 var UPWARD = 'upward';
22987 var AUTO = 'auto';
22988 var posPts = pairInfo.posPts,
22989 srcW = pairInfo.srcW,
22990 srcH = pairInfo.srcH,
22991 tgtW = pairInfo.tgtW,
22992 tgtH = pairInfo.tgtH;
22993 var edgeDistances = edge.pstyle('edge-distances').value;
22994 var dIncludesNodeBody = edgeDistances !== 'node-position';
22995 var taxiDir = edge.pstyle('taxi-direction').value;
22996 var rawTaxiDir = taxiDir; // unprocessed value
22997
22998 var taxiTurn = edge.pstyle('taxi-turn');
22999 var turnIsPercent = taxiTurn.units === '%';
23000 var taxiTurnPfVal = taxiTurn.pfValue;
23001 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
23002
23003 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
23004 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
23005 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
23006 var pdx = posPts.x2 - posPts.x1;
23007 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
23008
23009 var subDWH = function subDWH(dxy, dwh) {
23010 if (dxy > 0) {
23011 return Math.max(dxy - dwh, 0);
23012 } else {
23013 return Math.min(dxy + dwh, 0);
23014 }
23015 };
23016
23017 var dx = subDWH(pdx, dw);
23018 var dy = subDWH(pdy, dh);
23019 var isExplicitDir = false;
23020
23021 if (rawTaxiDir === AUTO) {
23022 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
23023 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
23024 taxiDir = VERTICAL;
23025 isExplicitDir = true;
23026 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
23027 taxiDir = HORIZONTAL;
23028 isExplicitDir = true;
23029 }
23030
23031 var isVert = taxiDir === VERTICAL;
23032 var l = isVert ? dy : dx;
23033 var pl = isVert ? pdy : pdx;
23034 var sgnL = signum(pl);
23035 var forcedDir = false;
23036
23037 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
23038 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
23039 sgnL *= -1;
23040 l = sgnL * Math.abs(l);
23041 forcedDir = true;
23042 }
23043
23044 var d;
23045
23046 if (turnIsPercent) {
23047 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
23048 d = p * l;
23049 } else {
23050 var k = taxiTurnPfVal < 0 ? l : 0;
23051 d = k + taxiTurnPfVal * sgnL;
23052 }
23053
23054 var getIsTooClose = function getIsTooClose(d) {
23055 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
23056 };
23057
23058 var isTooCloseSrc = getIsTooClose(d);
23059 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
23060 var isTooClose = isTooCloseSrc || isTooCloseTgt;
23061
23062 if (isTooClose && !forcedDir) {
23063 // non-ideal routing
23064 if (isVert) {
23065 // vertical fallbacks
23066 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
23067 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
23068
23069 if (lShapeInsideSrc) {
23070 // horizontal Z-shape (direction not respected)
23071 var x = (posPts.x1 + posPts.x2) / 2;
23072 var y1 = posPts.y1,
23073 y2 = posPts.y2;
23074 rs.segpts = [x, y1, x, y2];
23075 } else if (lShapeInsideTgt) {
23076 // vertical Z-shape (distance not respected)
23077 var y = (posPts.y1 + posPts.y2) / 2;
23078 var x1 = posPts.x1,
23079 x2 = posPts.x2;
23080 rs.segpts = [x1, y, x2, y];
23081 } else {
23082 // L-shape fallback (turn distance not respected, but works well with tree siblings)
23083 rs.segpts = [posPts.x1, posPts.y2];
23084 }
23085 } else {
23086 // horizontal fallbacks
23087 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
23088
23089 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
23090
23091 if (_lShapeInsideSrc) {
23092 // vertical Z-shape (direction not respected)
23093 var _y = (posPts.y1 + posPts.y2) / 2;
23094
23095 var _x = posPts.x1,
23096 _x2 = posPts.x2;
23097 rs.segpts = [_x, _y, _x2, _y];
23098 } else if (_lShapeInsideTgt) {
23099 // horizontal Z-shape (turn distance not respected)
23100 var _x3 = (posPts.x1 + posPts.x2) / 2;
23101
23102 var _y2 = posPts.y1,
23103 _y3 = posPts.y2;
23104 rs.segpts = [_x3, _y2, _x3, _y3];
23105 } else {
23106 // L-shape (turn distance not respected, but works well for tree siblings)
23107 rs.segpts = [posPts.x2, posPts.y1];
23108 }
23109 }
23110 } else {
23111 // ideal routing
23112 if (isVert) {
23113 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
23114
23115 var _x4 = posPts.x1,
23116 _x5 = posPts.x2;
23117 rs.segpts = [_x4, _y4, _x5, _y4];
23118 } else {
23119 // horizontal
23120 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
23121
23122 var _y5 = posPts.y1,
23123 _y6 = posPts.y2;
23124 rs.segpts = [_x6, _y5, _x6, _y6];
23125 }
23126 }
23127 };
23128
23129 BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
23130 var rs = edge._private.rscratch; // can only correct beziers for now...
23131
23132 if (rs.edgeType === 'bezier') {
23133 var srcPos = pairInfo.srcPos,
23134 tgtPos = pairInfo.tgtPos,
23135 srcW = pairInfo.srcW,
23136 srcH = pairInfo.srcH,
23137 tgtW = pairInfo.tgtW,
23138 tgtH = pairInfo.tgtH,
23139 srcShape = pairInfo.srcShape,
23140 tgtShape = pairInfo.tgtShape;
23141 var badStart = !number(rs.startX) || !number(rs.startY);
23142 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
23143 var badEnd = !number(rs.endX) || !number(rs.endY);
23144 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
23145 var minCpADistFactor = 3;
23146 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23147 var minCpADist = minCpADistFactor * arrowW;
23148 var startACpDist = dist({
23149 x: rs.ctrlpts[0],
23150 y: rs.ctrlpts[1]
23151 }, {
23152 x: rs.startX,
23153 y: rs.startY
23154 });
23155 var closeStartACp = startACpDist < minCpADist;
23156 var endACpDist = dist({
23157 x: rs.ctrlpts[0],
23158 y: rs.ctrlpts[1]
23159 }, {
23160 x: rs.endX,
23161 y: rs.endY
23162 });
23163 var closeEndACp = endACpDist < minCpADist;
23164 var overlapping = false;
23165
23166 if (badStart || badAStart || closeStartACp) {
23167 overlapping = true; // project control point along line from src centre to outside the src shape
23168 // (otherwise intersection will yield nothing)
23169
23170 var cpD = {
23171 // delta
23172 x: rs.ctrlpts[0] - srcPos.x,
23173 y: rs.ctrlpts[1] - srcPos.y
23174 };
23175 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
23176
23177 var cpM = {
23178 // normalised delta
23179 x: cpD.x / cpL,
23180 y: cpD.y / cpL
23181 };
23182 var radius = Math.max(srcW, srcH);
23183 var cpProj = {
23184 // *2 radius guarantees outside shape
23185 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
23186 y: rs.ctrlpts[1] + cpM.y * 2 * radius
23187 };
23188 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
23189
23190 if (closeStartACp) {
23191 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
23192 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
23193 } else {
23194 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
23195 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
23196 }
23197 }
23198
23199 if (badEnd || badAEnd || closeEndACp) {
23200 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
23201 // (otherwise intersection will yield nothing)
23202
23203 var _cpD = {
23204 // delta
23205 x: rs.ctrlpts[0] - tgtPos.x,
23206 y: rs.ctrlpts[1] - tgtPos.y
23207 };
23208
23209 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
23210
23211
23212 var _cpM = {
23213 // normalised delta
23214 x: _cpD.x / _cpL,
23215 y: _cpD.y / _cpL
23216 };
23217
23218 var _radius = Math.max(srcW, srcH);
23219
23220 var _cpProj = {
23221 // *2 radius guarantees outside shape
23222 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
23223 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
23224 };
23225 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
23226
23227 if (closeEndACp) {
23228 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
23229 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
23230 } else {
23231 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
23232 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
23233 }
23234 }
23235
23236 if (overlapping) {
23237 // recalc endpts
23238 this.findEndpoints(edge);
23239 }
23240 }
23241 };
23242
23243 BRp$3.storeAllpts = function (edge) {
23244 var rs = edge._private.rscratch;
23245
23246 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
23247 rs.allpts = [];
23248 rs.allpts.push(rs.startX, rs.startY);
23249
23250 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
23251 // ctrl pt itself
23252 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
23253
23254 if (b + 3 < rs.ctrlpts.length) {
23255 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
23256 }
23257 }
23258
23259 rs.allpts.push(rs.endX, rs.endY);
23260 var m, mt;
23261
23262 if (rs.ctrlpts.length / 2 % 2 === 0) {
23263 m = rs.allpts.length / 2 - 1;
23264 rs.midX = rs.allpts[m];
23265 rs.midY = rs.allpts[m + 1];
23266 } else {
23267 m = rs.allpts.length / 2 - 3;
23268 mt = 0.5;
23269 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
23270 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
23271 }
23272 } else if (rs.edgeType === 'straight') {
23273 // need to calc these after endpts
23274 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
23275
23276 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
23277 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
23278 } else if (rs.edgeType === 'segments') {
23279 rs.allpts = [];
23280 rs.allpts.push(rs.startX, rs.startY);
23281 rs.allpts.push.apply(rs.allpts, rs.segpts);
23282 rs.allpts.push(rs.endX, rs.endY);
23283
23284 if (rs.segpts.length % 4 === 0) {
23285 var i2 = rs.segpts.length / 2;
23286 var i1 = i2 - 2;
23287 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
23288 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
23289 } else {
23290 var _i = rs.segpts.length / 2 - 1;
23291
23292 rs.midX = rs.segpts[_i];
23293 rs.midY = rs.segpts[_i + 1];
23294 }
23295 }
23296 };
23297
23298 BRp$3.checkForInvalidEdgeWarning = function (edge) {
23299 var rs = edge[0]._private.rscratch;
23300
23301 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
23302 rs.loggedErr = false;
23303 } else {
23304 if (!rs.loggedErr) {
23305 rs.loggedErr = true;
23306 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.');
23307 }
23308 }
23309 };
23310
23311 BRp$3.findEdgeControlPoints = function (edges) {
23312 var _this = this;
23313
23314 if (!edges || edges.length === 0) {
23315 return;
23316 }
23317
23318 var r = this;
23319 var cy = r.cy;
23320 var hasCompounds = cy.hasCompoundNodes();
23321 var hashTable = {
23322 map: new Map$1(),
23323 get: function get(pairId) {
23324 var map2 = this.map.get(pairId[0]);
23325
23326 if (map2 != null) {
23327 return map2.get(pairId[1]);
23328 } else {
23329 return null;
23330 }
23331 },
23332 set: function set(pairId, val) {
23333 var map2 = this.map.get(pairId[0]);
23334
23335 if (map2 == null) {
23336 map2 = new Map$1();
23337 this.map.set(pairId[0], map2);
23338 }
23339
23340 map2.set(pairId[1], val);
23341 }
23342 };
23343 var pairIds = [];
23344 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
23345
23346 for (var i = 0; i < edges.length; i++) {
23347 var edge = edges[i];
23348 var _p = edge._private;
23349 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
23350 // they shouldn't take up space
23351
23352 if (edge.removed() || !edge.takesUpSpace()) {
23353 continue;
23354 }
23355
23356 if (curveStyle === 'haystack') {
23357 haystackEdges.push(edge);
23358 continue;
23359 }
23360
23361 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
23362 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
23363 var src = _p.source;
23364 var tgt = _p.target;
23365 var srcIndex = src.poolIndex();
23366 var tgtIndex = tgt.poolIndex();
23367 var pairId = [srcIndex, tgtIndex].sort();
23368 var tableEntry = hashTable.get(pairId);
23369
23370 if (tableEntry == null) {
23371 tableEntry = {
23372 eles: []
23373 };
23374 hashTable.set(pairId, tableEntry);
23375 pairIds.push(pairId);
23376 }
23377
23378 tableEntry.eles.push(edge);
23379
23380 if (edgeIsUnbundled) {
23381 tableEntry.hasUnbundled = true;
23382 }
23383
23384 if (edgeIsBezier) {
23385 tableEntry.hasBezier = true;
23386 }
23387 } // for each pair (src, tgt), create the ctrl pts
23388 // Nested for loop is OK; total number of iterations for both loops = edgeCount
23389
23390
23391 var _loop = function _loop(p) {
23392 var pairId = pairIds[p];
23393 var pairInfo = hashTable.get(pairId);
23394 var swappedpairInfo = void 0;
23395
23396 if (!pairInfo.hasUnbundled) {
23397 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
23398 return e.isBundledBezier();
23399 });
23400 clearArray(pairInfo.eles);
23401 pllEdges.forEach(function (edge) {
23402 return pairInfo.eles.push(edge);
23403 }); // for each pair id, the edges should be sorted by index
23404
23405 pairInfo.eles.sort(function (edge1, edge2) {
23406 return edge1.poolIndex() - edge2.poolIndex();
23407 });
23408 }
23409
23410 var firstEdge = pairInfo.eles[0];
23411 var src = firstEdge.source();
23412 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
23413
23414 if (src.poolIndex() > tgt.poolIndex()) {
23415 var temp = src;
23416 src = tgt;
23417 tgt = temp;
23418 }
23419
23420 var srcPos = pairInfo.srcPos = src.position();
23421 var tgtPos = pairInfo.tgtPos = tgt.position();
23422 var srcW = pairInfo.srcW = src.outerWidth();
23423 var srcH = pairInfo.srcH = src.outerHeight();
23424 var tgtW = pairInfo.tgtW = tgt.outerWidth();
23425 var tgtH = pairInfo.tgtH = tgt.outerHeight();
23426
23427 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
23428
23429 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
23430
23431 pairInfo.dirCounts = {
23432 'north': 0,
23433 'west': 0,
23434 'south': 0,
23435 'east': 0,
23436 'northwest': 0,
23437 'southwest': 0,
23438 'northeast': 0,
23439 'southeast': 0
23440 };
23441
23442 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
23443 var _edge = pairInfo.eles[_i2];
23444 var rs = _edge[0]._private.rscratch;
23445
23446 var _curveStyle = _edge.pstyle('curve-style').value;
23447
23448 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
23449
23450
23451 var edgeIsSwapped = !src.same(_edge.source());
23452
23453 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
23454 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
23455
23456 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
23457 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
23458
23459 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
23460 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
23461 var intersectionPts = pairInfo.intersectionPts = {
23462 x1: srcOutside[0],
23463 x2: tgtOutside[0],
23464 y1: srcOutside[1],
23465 y2: tgtOutside[1]
23466 };
23467 var posPts = pairInfo.posPts = {
23468 x1: srcPos.x,
23469 x2: tgtPos.x,
23470 y1: srcPos.y,
23471 y2: tgtPos.y
23472 };
23473 var dy = tgtOutside[1] - srcOutside[1];
23474 var dx = tgtOutside[0] - srcOutside[0];
23475 var l = Math.sqrt(dx * dx + dy * dy);
23476 var vector = pairInfo.vector = {
23477 x: dx,
23478 y: dy
23479 };
23480 var vectorNorm = pairInfo.vectorNorm = {
23481 x: vector.x / l,
23482 y: vector.y / l
23483 };
23484 var vectorNormInverse = {
23485 x: -vectorNorm.y,
23486 y: vectorNorm.x
23487 }; // if node shapes overlap, then no ctrl pts to draw
23488
23489 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);
23490 pairInfo.vectorNormInverse = vectorNormInverse;
23491 swappedpairInfo = {
23492 nodesOverlap: pairInfo.nodesOverlap,
23493 dirCounts: pairInfo.dirCounts,
23494 calculatedIntersection: true,
23495 hasBezier: pairInfo.hasBezier,
23496 hasUnbundled: pairInfo.hasUnbundled,
23497 eles: pairInfo.eles,
23498 srcPos: tgtPos,
23499 tgtPos: srcPos,
23500 srcW: tgtW,
23501 srcH: tgtH,
23502 tgtW: srcW,
23503 tgtH: srcH,
23504 srcIntn: tgtIntn,
23505 tgtIntn: srcIntn,
23506 srcShape: tgtShape,
23507 tgtShape: srcShape,
23508 posPts: {
23509 x1: posPts.x2,
23510 y1: posPts.y2,
23511 x2: posPts.x1,
23512 y2: posPts.y1
23513 },
23514 intersectionPts: {
23515 x1: intersectionPts.x2,
23516 y1: intersectionPts.y2,
23517 x2: intersectionPts.x1,
23518 y2: intersectionPts.y1
23519 },
23520 vector: {
23521 x: -vector.x,
23522 y: -vector.y
23523 },
23524 vectorNorm: {
23525 x: -vectorNorm.x,
23526 y: -vectorNorm.y
23527 },
23528 vectorNormInverse: {
23529 x: -vectorNormInverse.x,
23530 y: -vectorNormInverse.y
23531 }
23532 };
23533 }
23534
23535 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
23536 rs.nodesOverlap = passedPairInfo.nodesOverlap;
23537 rs.srcIntn = passedPairInfo.srcIntn;
23538 rs.tgtIntn = passedPairInfo.tgtIntn;
23539
23540 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
23541 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23542 } else if (src === tgt) {
23543 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23544 } else if (_curveStyle === 'segments') {
23545 _this.findSegmentsPoints(_edge, passedPairInfo);
23546 } else if (_curveStyle === 'taxi') {
23547 _this.findTaxiPoints(_edge, passedPairInfo);
23548 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
23549 _this.findStraightEdgePoints(_edge);
23550 } else {
23551 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
23552 }
23553
23554 _this.findEndpoints(_edge);
23555
23556 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
23557
23558 _this.checkForInvalidEdgeWarning(_edge);
23559
23560 _this.storeAllpts(_edge);
23561
23562 _this.storeEdgeProjections(_edge);
23563
23564 _this.calculateArrowAngles(_edge);
23565
23566 _this.recalculateEdgeLabelProjections(_edge);
23567
23568 _this.calculateLabelAngles(_edge);
23569 } // for pair edges
23570
23571 };
23572
23573 for (var p = 0; p < pairIds.length; p++) {
23574 _loop(p);
23575 } // for pair ids
23576 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
23577
23578
23579 this.findHaystackPoints(haystackEdges);
23580 };
23581
23582 function getPts(pts) {
23583 var retPts = [];
23584
23585 if (pts == null) {
23586 return;
23587 }
23588
23589 for (var i = 0; i < pts.length; i += 2) {
23590 var x = pts[i];
23591 var y = pts[i + 1];
23592 retPts.push({
23593 x: x,
23594 y: y
23595 });
23596 }
23597
23598 return retPts;
23599 }
23600
23601 BRp$3.getSegmentPoints = function (edge) {
23602 var rs = edge[0]._private.rscratch;
23603 var type = rs.edgeType;
23604
23605 if (type === 'segments') {
23606 this.recalculateRenderedStyle(edge);
23607 return getPts(rs.segpts);
23608 }
23609 };
23610
23611 BRp$3.getControlPoints = function (edge) {
23612 var rs = edge[0]._private.rscratch;
23613 var type = rs.edgeType;
23614
23615 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23616 this.recalculateRenderedStyle(edge);
23617 return getPts(rs.ctrlpts);
23618 }
23619 };
23620
23621 BRp$3.getEdgeMidpoint = function (edge) {
23622 var rs = edge[0]._private.rscratch;
23623 this.recalculateRenderedStyle(edge);
23624 return {
23625 x: rs.midX,
23626 y: rs.midY
23627 };
23628 };
23629
23630 var BRp$4 = {};
23631
23632 BRp$4.manualEndptToPx = function (node, prop) {
23633 var r = this;
23634 var npos = node.position();
23635 var w = node.outerWidth();
23636 var h = node.outerHeight();
23637
23638 if (prop.value.length === 2) {
23639 var p = [prop.pfValue[0], prop.pfValue[1]];
23640
23641 if (prop.units[0] === '%') {
23642 p[0] = p[0] * w;
23643 }
23644
23645 if (prop.units[1] === '%') {
23646 p[1] = p[1] * h;
23647 }
23648
23649 p[0] += npos.x;
23650 p[1] += npos.y;
23651 return p;
23652 } else {
23653 var angle = prop.pfValue[0];
23654 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23655
23656 var l = 2 * Math.max(w, h);
23657 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23658 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23659 }
23660 };
23661
23662 BRp$4.findEndpoints = function (edge) {
23663 var r = this;
23664 var intersect;
23665 var source = edge.source()[0];
23666 var target = edge.target()[0];
23667 var srcPos = source.position();
23668 var tgtPos = target.position();
23669 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23670 var srcArShape = edge.pstyle('source-arrow-shape').value;
23671 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23672 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23673 var curveStyle = edge.pstyle('curve-style').value;
23674 var rs = edge._private.rscratch;
23675 var et = rs.edgeType;
23676 var taxi = curveStyle === 'taxi';
23677 var self = et === 'self' || et === 'compound';
23678 var bezier = et === 'bezier' || et === 'multibezier' || self;
23679 var multi = et !== 'bezier';
23680 var lines = et === 'straight' || et === 'segments';
23681 var segments = et === 'segments';
23682 var hasEndpts = bezier || multi || lines;
23683 var overrideEndpts = self || taxi;
23684 var srcManEndpt = edge.pstyle('source-endpoint');
23685 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23686 var tgtManEndpt = edge.pstyle('target-endpoint');
23687 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23688 rs.srcManEndpt = srcManEndpt;
23689 rs.tgtManEndpt = tgtManEndpt;
23690 var p1; // last known point of edge on target side
23691
23692 var p2; // last known point of edge on source side
23693
23694 var p1_i; // point to intersect with target shape
23695
23696 var p2_i; // point to intersect with source shape
23697
23698 if (bezier) {
23699 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23700 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23701 p1 = cpEnd;
23702 p2 = cpStart;
23703 } else if (lines) {
23704 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23705 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23706 p1 = tgtArrowFromPt;
23707 p2 = srcArrowFromPt;
23708 }
23709
23710 if (tgtManEndptVal === 'inside-to-node') {
23711 intersect = [tgtPos.x, tgtPos.y];
23712 } else if (tgtManEndpt.units) {
23713 intersect = this.manualEndptToPx(target, tgtManEndpt);
23714 } else if (tgtManEndptVal === 'outside-to-line') {
23715 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23716 } else {
23717 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23718 p1_i = p1;
23719 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23720 p1_i = [srcPos.x, srcPos.y];
23721 }
23722
23723 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23724
23725 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23726 var trs = target._private.rscratch;
23727 var lw = trs.labelWidth;
23728 var lh = trs.labelHeight;
23729 var lx = trs.labelX;
23730 var ly = trs.labelY;
23731 var lw2 = lw / 2;
23732 var lh2 = lh / 2;
23733 var va = target.pstyle('text-valign').value;
23734
23735 if (va === 'top') {
23736 ly -= lh2;
23737 } else if (va === 'bottom') {
23738 ly += lh2;
23739 }
23740
23741 var ha = target.pstyle('text-halign').value;
23742
23743 if (ha === 'left') {
23744 lx -= lw2;
23745 } else if (ha === 'right') {
23746 lx += lw2;
23747 }
23748
23749 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);
23750
23751 if (labelIntersect.length > 0) {
23752 var refPt = srcPos;
23753 var intSqdist = sqdist(refPt, array2point(intersect));
23754 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23755 var minSqDist = intSqdist;
23756
23757 if (labIntSqdist < intSqdist) {
23758 intersect = labelIntersect;
23759 minSqDist = labIntSqdist;
23760 }
23761
23762 if (labelIntersect.length > 2) {
23763 var labInt2SqDist = sqdist(refPt, {
23764 x: labelIntersect[2],
23765 y: labelIntersect[3]
23766 });
23767
23768 if (labInt2SqDist < minSqDist) {
23769 intersect = [labelIntersect[2], labelIntersect[3]];
23770 }
23771 }
23772 }
23773 }
23774 }
23775
23776 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23777 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23778 rs.endX = edgeEnd[0];
23779 rs.endY = edgeEnd[1];
23780 rs.arrowEndX = arrowEnd[0];
23781 rs.arrowEndY = arrowEnd[1];
23782
23783 if (srcManEndptVal === 'inside-to-node') {
23784 intersect = [srcPos.x, srcPos.y];
23785 } else if (srcManEndpt.units) {
23786 intersect = this.manualEndptToPx(source, srcManEndpt);
23787 } else if (srcManEndptVal === 'outside-to-line') {
23788 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23789 } else {
23790 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23791 p2_i = p2;
23792 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23793 p2_i = [tgtPos.x, tgtPos.y];
23794 }
23795
23796 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23797
23798 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23799 var srs = source._private.rscratch;
23800 var _lw = srs.labelWidth;
23801 var _lh = srs.labelHeight;
23802 var _lx = srs.labelX;
23803 var _ly = srs.labelY;
23804
23805 var _lw2 = _lw / 2;
23806
23807 var _lh2 = _lh / 2;
23808
23809 var _va = source.pstyle('text-valign').value;
23810
23811 if (_va === 'top') {
23812 _ly -= _lh2;
23813 } else if (_va === 'bottom') {
23814 _ly += _lh2;
23815 }
23816
23817 var _ha = source.pstyle('text-halign').value;
23818
23819 if (_ha === 'left') {
23820 _lx -= _lw2;
23821 } else if (_ha === 'right') {
23822 _lx += _lw2;
23823 }
23824
23825 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);
23826
23827 if (_labelIntersect.length > 0) {
23828 var _refPt = tgtPos;
23829
23830 var _intSqdist = sqdist(_refPt, array2point(intersect));
23831
23832 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23833
23834 var _minSqDist = _intSqdist;
23835
23836 if (_labIntSqdist < _intSqdist) {
23837 intersect = [_labelIntersect[0], _labelIntersect[1]];
23838 _minSqDist = _labIntSqdist;
23839 }
23840
23841 if (_labelIntersect.length > 2) {
23842 var _labInt2SqDist = sqdist(_refPt, {
23843 x: _labelIntersect[2],
23844 y: _labelIntersect[3]
23845 });
23846
23847 if (_labInt2SqDist < _minSqDist) {
23848 intersect = [_labelIntersect[2], _labelIntersect[3]];
23849 }
23850 }
23851 }
23852 }
23853 }
23854
23855 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23856 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23857 rs.startX = edgeStart[0];
23858 rs.startY = edgeStart[1];
23859 rs.arrowStartX = arrowStart[0];
23860 rs.arrowStartY = arrowStart[1];
23861
23862 if (hasEndpts) {
23863 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23864 rs.badLine = true;
23865 } else {
23866 rs.badLine = false;
23867 }
23868 }
23869 };
23870
23871 BRp$4.getSourceEndpoint = function (edge) {
23872 var rs = edge[0]._private.rscratch;
23873 this.recalculateRenderedStyle(edge);
23874
23875 switch (rs.edgeType) {
23876 case 'haystack':
23877 return {
23878 x: rs.haystackPts[0],
23879 y: rs.haystackPts[1]
23880 };
23881
23882 default:
23883 return {
23884 x: rs.arrowStartX,
23885 y: rs.arrowStartY
23886 };
23887 }
23888 };
23889
23890 BRp$4.getTargetEndpoint = function (edge) {
23891 var rs = edge[0]._private.rscratch;
23892 this.recalculateRenderedStyle(edge);
23893
23894 switch (rs.edgeType) {
23895 case 'haystack':
23896 return {
23897 x: rs.haystackPts[2],
23898 y: rs.haystackPts[3]
23899 };
23900
23901 default:
23902 return {
23903 x: rs.arrowEndX,
23904 y: rs.arrowEndY
23905 };
23906 }
23907 };
23908
23909 var BRp$5 = {};
23910
23911 function pushBezierPts(r, edge, pts) {
23912 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23913 return qbezierAt(p1, p2, p3, t);
23914 };
23915
23916 var _p = edge._private;
23917 var bpts = _p.rstyle.bezierPts;
23918
23919 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23920 var p = r.bezierProjPcts[i];
23921 bpts.push({
23922 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23923 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23924 });
23925 }
23926 }
23927
23928 BRp$5.storeEdgeProjections = function (edge) {
23929 var _p = edge._private;
23930 var rs = _p.rscratch;
23931 var et = rs.edgeType; // clear the cached points state
23932
23933 _p.rstyle.bezierPts = null;
23934 _p.rstyle.linePts = null;
23935 _p.rstyle.haystackPts = null;
23936
23937 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23938 _p.rstyle.bezierPts = [];
23939
23940 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23941 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23942 }
23943 } else if (et === 'segments') {
23944 var lpts = _p.rstyle.linePts = [];
23945
23946 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23947 lpts.push({
23948 x: rs.allpts[i],
23949 y: rs.allpts[i + 1]
23950 });
23951 }
23952 } else if (et === 'haystack') {
23953 var hpts = rs.haystackPts;
23954 _p.rstyle.haystackPts = [{
23955 x: hpts[0],
23956 y: hpts[1]
23957 }, {
23958 x: hpts[2],
23959 y: hpts[3]
23960 }];
23961 }
23962
23963 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23964 };
23965
23966 BRp$5.recalculateEdgeProjections = function (edges) {
23967 this.findEdgeControlPoints(edges);
23968 };
23969
23970 /* global document */
23971
23972 var BRp$6 = {};
23973
23974 BRp$6.recalculateNodeLabelProjection = function (node) {
23975 var content = node.pstyle('label').strValue;
23976
23977 if (emptyString(content)) {
23978 return;
23979 }
23980
23981 var textX, textY;
23982 var _p = node._private;
23983 var nodeWidth = node.width();
23984 var nodeHeight = node.height();
23985 var padding = node.padding();
23986 var nodePos = node.position();
23987 var textHalign = node.pstyle('text-halign').strValue;
23988 var textValign = node.pstyle('text-valign').strValue;
23989 var rs = _p.rscratch;
23990 var rstyle = _p.rstyle;
23991
23992 switch (textHalign) {
23993 case 'left':
23994 textX = nodePos.x - nodeWidth / 2 - padding;
23995 break;
23996
23997 case 'right':
23998 textX = nodePos.x + nodeWidth / 2 + padding;
23999 break;
24000
24001 default:
24002 // e.g. center
24003 textX = nodePos.x;
24004 }
24005
24006 switch (textValign) {
24007 case 'top':
24008 textY = nodePos.y - nodeHeight / 2 - padding;
24009 break;
24010
24011 case 'bottom':
24012 textY = nodePos.y + nodeHeight / 2 + padding;
24013 break;
24014
24015 default:
24016 // e.g. middle
24017 textY = nodePos.y;
24018 }
24019
24020 rs.labelX = textX;
24021 rs.labelY = textY;
24022 rstyle.labelX = textX;
24023 rstyle.labelY = textY;
24024 this.applyLabelDimensions(node);
24025 };
24026
24027 var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
24028 var angle = Math.atan(dy / dx);
24029
24030 if (dx === 0 && angle < 0) {
24031 angle = angle * -1;
24032 }
24033
24034 return angle;
24035 };
24036
24037 var lineAngle = function lineAngle(p0, p1) {
24038 var dx = p1.x - p0.x;
24039 var dy = p1.y - p0.y;
24040 return lineAngleFromDelta(dx, dy);
24041 };
24042
24043 var bezierAngle = function bezierAngle(p0, p1, p2, t) {
24044 var t0 = bound(0, t - 0.001, 1);
24045 var t1 = bound(0, t + 0.001, 1);
24046 var lp0 = qbezierPtAt(p0, p1, p2, t0);
24047 var lp1 = qbezierPtAt(p0, p1, p2, t1);
24048 return lineAngle(lp0, lp1);
24049 };
24050
24051 BRp$6.recalculateEdgeLabelProjections = function (edge) {
24052 var p;
24053 var _p = edge._private;
24054 var rs = _p.rscratch;
24055 var r = this;
24056 var content = {
24057 mid: edge.pstyle('label').strValue,
24058 source: edge.pstyle('source-label').strValue,
24059 target: edge.pstyle('target-label').strValue
24060 };
24061
24062 if (content.mid || content.source || content.target) ; else {
24063 return; // no labels => no calcs
24064 } // add center point to style so bounding box calculations can use it
24065 //
24066
24067
24068 p = {
24069 x: rs.midX,
24070 y: rs.midY
24071 };
24072
24073 var setRs = function setRs(propName, prefix, value) {
24074 setPrefixedProperty(_p.rscratch, propName, prefix, value);
24075 setPrefixedProperty(_p.rstyle, propName, prefix, value);
24076 };
24077
24078 setRs('labelX', null, p.x);
24079 setRs('labelY', null, p.y);
24080 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
24081 setRs('labelAutoAngle', null, midAngle);
24082
24083 var createControlPointInfo = function createControlPointInfo() {
24084 if (createControlPointInfo.cache) {
24085 return createControlPointInfo.cache;
24086 } // use cache so only 1x per edge
24087
24088
24089 var ctrlpts = []; // store each ctrlpt info init
24090
24091 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
24092 var p0 = {
24093 x: rs.allpts[i],
24094 y: rs.allpts[i + 1]
24095 };
24096 var p1 = {
24097 x: rs.allpts[i + 2],
24098 y: rs.allpts[i + 3]
24099 }; // ctrlpt
24100
24101 var p2 = {
24102 x: rs.allpts[i + 4],
24103 y: rs.allpts[i + 5]
24104 };
24105 ctrlpts.push({
24106 p0: p0,
24107 p1: p1,
24108 p2: p2,
24109 startDist: 0,
24110 length: 0,
24111 segments: []
24112 });
24113 }
24114
24115 var bpts = _p.rstyle.bezierPts;
24116 var nProjs = r.bezierProjPcts.length;
24117
24118 function addSegment(cp, p0, p1, t0, t1) {
24119 var length = dist(p0, p1);
24120 var prevSegment = cp.segments[cp.segments.length - 1];
24121 var segment = {
24122 p0: p0,
24123 p1: p1,
24124 t0: t0,
24125 t1: t1,
24126 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
24127 length: length
24128 };
24129 cp.segments.push(segment);
24130 cp.length += length;
24131 } // update each ctrlpt with segment info
24132
24133
24134 for (var _i = 0; _i < ctrlpts.length; _i++) {
24135 var cp = ctrlpts[_i];
24136 var prevCp = ctrlpts[_i - 1];
24137
24138 if (prevCp) {
24139 cp.startDist = prevCp.startDist + prevCp.length;
24140 }
24141
24142 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
24143
24144 for (var j = 0; j < nProjs - 1; j++) {
24145 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
24146 }
24147
24148 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
24149 }
24150
24151 return createControlPointInfo.cache = ctrlpts;
24152 };
24153
24154 var calculateEndProjection = function calculateEndProjection(prefix) {
24155 var angle;
24156 var isSrc = prefix === 'source';
24157
24158 if (!content[prefix]) {
24159 return;
24160 }
24161
24162 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
24163
24164 switch (rs.edgeType) {
24165 case 'self':
24166 case 'compound':
24167 case 'bezier':
24168 case 'multibezier':
24169 {
24170 var cps = createControlPointInfo();
24171 var selected;
24172 var startDist = 0;
24173 var totalDist = 0; // find the segment we're on
24174
24175 for (var i = 0; i < cps.length; i++) {
24176 var _cp = cps[isSrc ? i : cps.length - 1 - i];
24177
24178 for (var j = 0; j < _cp.segments.length; j++) {
24179 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
24180 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
24181 startDist = totalDist;
24182 totalDist += _seg.length;
24183
24184 if (totalDist >= offset || lastSeg) {
24185 selected = {
24186 cp: _cp,
24187 segment: _seg
24188 };
24189 break;
24190 }
24191 }
24192
24193 if (selected) {
24194 break;
24195 }
24196 }
24197
24198 var cp = selected.cp;
24199 var seg = selected.segment;
24200 var tSegment = (offset - startDist) / seg.length;
24201 var segDt = seg.t1 - seg.t0;
24202 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
24203 t = bound(0, t, 1);
24204 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
24205 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
24206 break;
24207 }
24208
24209 case 'straight':
24210 case 'segments':
24211 case 'haystack':
24212 {
24213 var d = 0,
24214 di,
24215 d0;
24216 var p0, p1;
24217 var l = rs.allpts.length;
24218
24219 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
24220 if (isSrc) {
24221 p0 = {
24222 x: rs.allpts[_i2],
24223 y: rs.allpts[_i2 + 1]
24224 };
24225 p1 = {
24226 x: rs.allpts[_i2 + 2],
24227 y: rs.allpts[_i2 + 3]
24228 };
24229 } else {
24230 p0 = {
24231 x: rs.allpts[l - 2 - _i2],
24232 y: rs.allpts[l - 1 - _i2]
24233 };
24234 p1 = {
24235 x: rs.allpts[l - 4 - _i2],
24236 y: rs.allpts[l - 3 - _i2]
24237 };
24238 }
24239
24240 di = dist(p0, p1);
24241 d0 = d;
24242 d += di;
24243
24244 if (d >= offset) {
24245 break;
24246 }
24247 }
24248
24249 var pD = offset - d0;
24250
24251 var _t = pD / di;
24252
24253 _t = bound(0, _t, 1);
24254 p = lineAt(p0, p1, _t);
24255 angle = lineAngle(p0, p1);
24256 break;
24257 }
24258 }
24259
24260 setRs('labelX', prefix, p.x);
24261 setRs('labelY', prefix, p.y);
24262 setRs('labelAutoAngle', prefix, angle);
24263 };
24264
24265 calculateEndProjection('source');
24266 calculateEndProjection('target');
24267 this.applyLabelDimensions(edge);
24268 };
24269
24270 BRp$6.applyLabelDimensions = function (ele) {
24271 this.applyPrefixedLabelDimensions(ele);
24272
24273 if (ele.isEdge()) {
24274 this.applyPrefixedLabelDimensions(ele, 'source');
24275 this.applyPrefixedLabelDimensions(ele, 'target');
24276 }
24277 };
24278
24279 BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
24280 var _p = ele._private;
24281 var text = this.getLabelText(ele, prefix);
24282 var labelDims = this.calculateLabelDimensions(ele, text);
24283 var lineHeight = ele.pstyle('line-height').pfValue;
24284 var textWrap = ele.pstyle('text-wrap').strValue;
24285 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
24286 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
24287 var normPerLineHeight = labelDims.height / numLines;
24288 var labelLineHeight = normPerLineHeight * lineHeight;
24289 var width = labelDims.width;
24290 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
24291 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
24292 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
24293 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
24294 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
24295 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
24296 };
24297
24298 BRp$6.getLabelText = function (ele, prefix) {
24299 var _p = ele._private;
24300 var pfd = prefix ? prefix + '-' : '';
24301 var text = ele.pstyle(pfd + 'label').strValue;
24302 var textTransform = ele.pstyle('text-transform').value;
24303
24304 var rscratch = function rscratch(propName, value) {
24305 if (value) {
24306 setPrefixedProperty(_p.rscratch, propName, prefix, value);
24307 return value;
24308 } else {
24309 return getPrefixedProperty(_p.rscratch, propName, prefix);
24310 }
24311 }; // for empty text, skip all processing
24312
24313
24314 if (!text) {
24315 return '';
24316 }
24317
24318 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
24319 text = text.toUpperCase();
24320 } else if (textTransform == 'lowercase') {
24321 text = text.toLowerCase();
24322 }
24323
24324 var wrapStyle = ele.pstyle('text-wrap').value;
24325
24326 if (wrapStyle === 'wrap') {
24327 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
24328
24329 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
24330 return rscratch('labelWrapCachedText');
24331 }
24332
24333 var zwsp = "\u200B";
24334 var lines = text.split('\n');
24335 var maxW = ele.pstyle('text-max-width').pfValue;
24336 var overflow = ele.pstyle('text-overflow-wrap').value;
24337 var overflowAny = overflow === 'anywhere';
24338 var wrappedLines = [];
24339 var wordsRegex = /[\s\u200b]+/;
24340 var wordSeparator = overflowAny ? '' : ' ';
24341
24342 for (var l = 0; l < lines.length; l++) {
24343 var line = lines[l];
24344 var lineDims = this.calculateLabelDimensions(ele, line);
24345 var lineW = lineDims.width;
24346
24347 if (overflowAny) {
24348 var processedLine = line.split('').join(zwsp);
24349 line = processedLine;
24350 }
24351
24352 if (lineW > maxW) {
24353 // line is too long
24354 var words = line.split(wordsRegex);
24355 var subline = '';
24356
24357 for (var w = 0; w < words.length; w++) {
24358 var word = words[w];
24359 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
24360 var testDims = this.calculateLabelDimensions(ele, testLine);
24361 var testW = testDims.width;
24362
24363 if (testW <= maxW) {
24364 // word fits on current line
24365 subline += word + wordSeparator;
24366 } else {
24367 // word starts new line
24368 if (subline) {
24369 wrappedLines.push(subline);
24370 }
24371
24372 subline = word + wordSeparator;
24373 }
24374 } // if there's remaining text, put it in a wrapped line
24375
24376
24377 if (!subline.match(/^[\s\u200b]+$/)) {
24378 wrappedLines.push(subline);
24379 }
24380 } else {
24381 // line is already short enough
24382 wrappedLines.push(line);
24383 }
24384 } // for
24385
24386
24387 rscratch('labelWrapCachedLines', wrappedLines);
24388 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
24389 rscratch('labelWrapKey', labelKey);
24390 } else if (wrapStyle === 'ellipsis') {
24391 var _maxW = ele.pstyle('text-max-width').pfValue;
24392 var ellipsized = '';
24393 var ellipsis = "\u2026";
24394 var incLastCh = false;
24395
24396 for (var i = 0; i < text.length; i++) {
24397 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
24398
24399 if (widthWithNextCh > _maxW) {
24400 break;
24401 }
24402
24403 ellipsized += text[i];
24404
24405 if (i === text.length - 1) {
24406 incLastCh = true;
24407 }
24408 }
24409
24410 if (!incLastCh) {
24411 ellipsized += ellipsis;
24412 }
24413
24414 return ellipsized;
24415 } // if ellipsize
24416
24417
24418 return text;
24419 };
24420
24421 BRp$6.getLabelJustification = function (ele) {
24422 var justification = ele.pstyle('text-justification').strValue;
24423 var textHalign = ele.pstyle('text-halign').strValue;
24424
24425 if (justification === 'auto') {
24426 if (ele.isNode()) {
24427 switch (textHalign) {
24428 case 'left':
24429 return 'right';
24430
24431 case 'right':
24432 return 'left';
24433
24434 default:
24435 return 'center';
24436 }
24437 } else {
24438 return 'center';
24439 }
24440 } else {
24441 return justification;
24442 }
24443 };
24444
24445 BRp$6.calculateLabelDimensions = function (ele, text) {
24446 var r = this;
24447 var cacheKey = hashString(text, ele._private.labelDimsKey);
24448 var cache = r.labelDimCache || (r.labelDimCache = []);
24449 var existingVal = cache[cacheKey];
24450
24451 if (existingVal != null) {
24452 return existingVal;
24453 }
24454
24455 var padding = 6; // add padding around text dims, as the measurement isn't that accurate
24456
24457 var fStyle = ele.pstyle('font-style').strValue;
24458 var size = ele.pstyle('font-size').pfValue;
24459 var family = ele.pstyle('font-family').strValue;
24460 var weight = ele.pstyle('font-weight').strValue;
24461 var canvas = this.labelCalcCanvas;
24462 var c2d = this.labelCalcCanvasContext;
24463
24464 if (!canvas) {
24465 canvas = this.labelCalcCanvas = document.createElement('canvas');
24466 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
24467 var ds = canvas.style;
24468 ds.position = 'absolute';
24469 ds.left = '-9999px';
24470 ds.top = '-9999px';
24471 ds.zIndex = '-1';
24472 ds.visibility = 'hidden';
24473 ds.pointerEvents = 'none';
24474 }
24475
24476 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
24477 var width = 0;
24478 var height = 0;
24479 var lines = text.split('\n');
24480
24481 for (var i = 0; i < lines.length; i++) {
24482 var line = lines[i];
24483 var metrics = c2d.measureText(line);
24484 var w = Math.ceil(metrics.width);
24485 var h = size;
24486 width = Math.max(w, width);
24487 height += h;
24488 }
24489
24490 width += padding;
24491 height += padding;
24492 return cache[cacheKey] = {
24493 width: width,
24494 height: height
24495 };
24496 };
24497
24498 BRp$6.calculateLabelAngle = function (ele, prefix) {
24499 var _p = ele._private;
24500 var rs = _p.rscratch;
24501 var isEdge = ele.isEdge();
24502 var prefixDash = prefix ? prefix + '-' : '';
24503 var rot = ele.pstyle(prefixDash + 'text-rotation');
24504 var rotStr = rot.strValue;
24505
24506 if (rotStr === 'none') {
24507 return 0;
24508 } else if (isEdge && rotStr === 'autorotate') {
24509 return rs.labelAutoAngle;
24510 } else if (rotStr === 'autorotate') {
24511 return 0;
24512 } else {
24513 return rot.pfValue;
24514 }
24515 };
24516
24517 BRp$6.calculateLabelAngles = function (ele) {
24518 var r = this;
24519 var isEdge = ele.isEdge();
24520 var _p = ele._private;
24521 var rs = _p.rscratch;
24522 rs.labelAngle = r.calculateLabelAngle(ele);
24523
24524 if (isEdge) {
24525 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
24526 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
24527 }
24528 };
24529
24530 var BRp$7 = {};
24531 var TOO_SMALL_CUT_RECT = 28;
24532 var warnedCutRect = false;
24533
24534 BRp$7.getNodeShape = function (node) {
24535 var r = this;
24536 var shape = node.pstyle('shape').value;
24537
24538 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
24539 if (!warnedCutRect) {
24540 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
24541 warnedCutRect = true;
24542 }
24543
24544 return 'rectangle';
24545 }
24546
24547 if (node.isParent()) {
24548 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
24549 return shape;
24550 } else {
24551 return 'rectangle';
24552 }
24553 }
24554
24555 if (shape === 'polygon') {
24556 var points = node.pstyle('shape-polygon-points').value;
24557 return r.nodeShapes.makePolygon(points).name;
24558 }
24559
24560 return shape;
24561 };
24562
24563 var BRp$8 = {};
24564
24565 BRp$8.registerCalculationListeners = function () {
24566 var cy = this.cy;
24567 var elesToUpdate = cy.collection();
24568 var r = this;
24569
24570 var enqueue = function enqueue(eles) {
24571 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
24572 elesToUpdate.merge(eles);
24573
24574 if (dirtyStyleCaches) {
24575 for (var i = 0; i < eles.length; i++) {
24576 var ele = eles[i];
24577 var _p = ele._private;
24578 var rstyle = _p.rstyle;
24579 rstyle.clean = false;
24580 rstyle.cleanConnected = false;
24581 }
24582 }
24583 };
24584
24585 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24586 var ele = e.target;
24587 enqueue(ele);
24588 }).on('style.* background.*', function onDirtyStyle(e) {
24589 var ele = e.target;
24590 enqueue(ele, false);
24591 });
24592
24593 var updateEleCalcs = function updateEleCalcs(willDraw) {
24594 if (willDraw) {
24595 var fns = r.onUpdateEleCalcsFns;
24596
24597 for (var i = 0; i < elesToUpdate.length; i++) {
24598 var ele = elesToUpdate[i];
24599 var rstyle = ele._private.rstyle;
24600
24601 if (ele.isNode() && !rstyle.cleanConnected) {
24602 enqueue(ele.connectedEdges());
24603 rstyle.cleanConnected = true;
24604 }
24605 }
24606
24607 if (fns) {
24608 for (var _i = 0; _i < fns.length; _i++) {
24609 var fn = fns[_i];
24610 fn(willDraw, elesToUpdate);
24611 }
24612 }
24613
24614 r.recalculateRenderedStyle(elesToUpdate);
24615 elesToUpdate = cy.collection();
24616 }
24617 };
24618
24619 r.flushRenderedStyleQueue = function () {
24620 updateEleCalcs(true);
24621 };
24622
24623 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24624 };
24625
24626 BRp$8.onUpdateEleCalcs = function (fn) {
24627 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24628 fns.push(fn);
24629 };
24630
24631 BRp$8.recalculateRenderedStyle = function (eles, useCache) {
24632 var isCleanConnected = function isCleanConnected(ele) {
24633 return ele._private.rstyle.cleanConnected;
24634 };
24635
24636 var edges = [];
24637 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24638
24639 if (this.destroyed) {
24640 return;
24641 } // use cache by default for perf
24642
24643
24644 if (useCache === undefined) {
24645 useCache = true;
24646 }
24647
24648 for (var i = 0; i < eles.length; i++) {
24649 var ele = eles[i];
24650 var _p = ele._private;
24651 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24652 // (and a request for recalc may come in between frames)
24653
24654 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24655 rstyle.clean = false;
24656 } // only update if dirty and in graph
24657
24658
24659 if (useCache && rstyle.clean || ele.removed()) {
24660 continue;
24661 } // only update if not display: none
24662
24663
24664 if (ele.pstyle('display').value === 'none') {
24665 continue;
24666 }
24667
24668 if (_p.group === 'nodes') {
24669 nodes.push(ele);
24670 } else {
24671 // edges
24672 edges.push(ele);
24673 }
24674
24675 rstyle.clean = true;
24676 } // update node data from projections
24677
24678
24679 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24680 var _ele = nodes[_i2];
24681 var _p2 = _ele._private;
24682 var _rstyle = _p2.rstyle;
24683
24684 var pos = _ele.position();
24685
24686 this.recalculateNodeLabelProjection(_ele);
24687 _rstyle.nodeX = pos.x;
24688 _rstyle.nodeY = pos.y;
24689 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24690 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24691 }
24692
24693 this.recalculateEdgeProjections(edges); // update edge data from projections
24694
24695 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24696 var _ele2 = edges[_i3];
24697 var _p3 = _ele2._private;
24698 var _rstyle2 = _p3.rstyle;
24699 var rs = _p3.rscratch; // update rstyle positions
24700
24701 _rstyle2.srcX = rs.arrowStartX;
24702 _rstyle2.srcY = rs.arrowStartY;
24703 _rstyle2.tgtX = rs.arrowEndX;
24704 _rstyle2.tgtY = rs.arrowEndY;
24705 _rstyle2.midX = rs.midX;
24706 _rstyle2.midY = rs.midY;
24707 _rstyle2.labelAngle = rs.labelAngle;
24708 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24709 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24710 }
24711 };
24712
24713 var BRp$9 = {};
24714
24715 BRp$9.updateCachedGrabbedEles = function () {
24716 var eles = this.cachedZSortedEles;
24717
24718 if (!eles) {
24719 // just let this be recalculated on the next z sort tick
24720 return;
24721 }
24722
24723 eles.drag = [];
24724 eles.nondrag = [];
24725 var grabTargets = [];
24726
24727 for (var i = 0; i < eles.length; i++) {
24728 var ele = eles[i];
24729 var rs = ele._private.rscratch;
24730
24731 if (ele.grabbed() && !ele.isParent()) {
24732 grabTargets.push(ele);
24733 } else if (rs.inDragLayer) {
24734 eles.drag.push(ele);
24735 } else {
24736 eles.nondrag.push(ele);
24737 }
24738 } // put the grab target nodes last so it's on top of its neighbourhood
24739
24740
24741 for (var i = 0; i < grabTargets.length; i++) {
24742 var ele = grabTargets[i];
24743 eles.drag.push(ele);
24744 }
24745 };
24746
24747 BRp$9.invalidateCachedZSortedEles = function () {
24748 this.cachedZSortedEles = null;
24749 };
24750
24751 BRp$9.getCachedZSortedEles = function (forceRecalc) {
24752 if (forceRecalc || !this.cachedZSortedEles) {
24753 var eles = this.cy.mutableElements().toArray();
24754 eles.sort(zIndexSort);
24755 eles.interactive = eles.filter(function (ele) {
24756 return ele.interactive();
24757 });
24758 this.cachedZSortedEles = eles;
24759 this.updateCachedGrabbedEles();
24760 } else {
24761 eles = this.cachedZSortedEles;
24762 }
24763
24764 return eles;
24765 };
24766
24767 var BRp$a = {};
24768 [BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24769 extend(BRp$a, props);
24770 });
24771
24772 var BRp$b = {};
24773
24774 BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24775 var r = this;
24776 var imageCache = r.imageCache = r.imageCache || {};
24777 var cache = imageCache[url];
24778
24779 if (cache) {
24780 if (!cache.image.complete) {
24781 cache.image.addEventListener('load', onLoad);
24782 }
24783
24784 return cache.image;
24785 } else {
24786 cache = imageCache[url] = imageCache[url] || {};
24787 var image = cache.image = new Image(); // eslint-disable-line no-undef
24788
24789 image.addEventListener('load', onLoad);
24790 image.addEventListener('error', function () {
24791 image.error = true;
24792 }); // #1582 safari doesn't load data uris with crossOrigin properly
24793 // https://bugs.webkit.org/show_bug.cgi?id=123978
24794
24795 var dataUriPrefix = 'data:';
24796 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24797
24798 if (!isDataUri) {
24799 image.crossOrigin = crossOrigin; // prevent tainted canvas
24800 }
24801
24802 image.src = url;
24803 return image;
24804 }
24805 };
24806
24807 var BRp$c = {};
24808 /* global document, window, ResizeObserver, MutationObserver */
24809
24810 BRp$c.registerBinding = function (target, event, handler, useCapture) {
24811 // eslint-disable-line no-unused-vars
24812 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24813
24814 var b = this.binder(target);
24815 return b.on.apply(b, args);
24816 };
24817
24818 BRp$c.binder = function (tgt) {
24819 var r = this;
24820 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24821
24822 if (r.supportsPassiveEvents == null) {
24823 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24824 var supportsPassive = false;
24825
24826 try {
24827 var opts = Object.defineProperty({}, 'passive', {
24828 get: function get() {
24829 supportsPassive = true;
24830 return true;
24831 }
24832 });
24833 window.addEventListener('test', null, opts);
24834 } catch (err) {// not supported
24835 }
24836
24837 r.supportsPassiveEvents = supportsPassive;
24838 }
24839
24840 var on = function on(event, handler, useCapture) {
24841 var args = Array.prototype.slice.call(arguments);
24842
24843 if (tgtIsDom && r.supportsPassiveEvents) {
24844 // replace useCapture w/ opts obj
24845 args[2] = {
24846 capture: useCapture != null ? useCapture : false,
24847 passive: false,
24848 once: false
24849 };
24850 }
24851
24852 r.bindings.push({
24853 target: tgt,
24854 args: args
24855 });
24856 (tgt.addEventListener || tgt.on).apply(tgt, args);
24857 return this;
24858 };
24859
24860 return {
24861 on: on,
24862 addEventListener: on,
24863 addListener: on,
24864 bind: on
24865 };
24866 };
24867
24868 BRp$c.nodeIsDraggable = function (node) {
24869 return node && node.isNode() && !node.locked() && node.grabbable();
24870 };
24871
24872 BRp$c.nodeIsGrabbable = function (node) {
24873 return this.nodeIsDraggable(node) && node.interactive();
24874 };
24875
24876 BRp$c.load = function () {
24877 var r = this;
24878
24879 var isSelected = function isSelected(ele) {
24880 return ele.selected();
24881 };
24882
24883 var triggerEvents = function triggerEvents(target, names, e, position) {
24884 if (target == null) {
24885 target = r.cy;
24886 }
24887
24888 for (var i = 0; i < names.length; i++) {
24889 var name = names[i];
24890 target.emit({
24891 originalEvent: e,
24892 type: name,
24893 position: position
24894 });
24895 }
24896 };
24897
24898 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24899 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24900 };
24901
24902 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24903 var allowPassthrough = true;
24904
24905 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24906 // a grabbable compound node below the ele => no passthrough panning
24907 for (var i = 0; downs && i < downs.length; i++) {
24908 var down = downs[i];
24909
24910 if (down.isNode() && down.isParent()) {
24911 allowPassthrough = false;
24912 break;
24913 }
24914 }
24915 } else {
24916 allowPassthrough = true;
24917 }
24918
24919 return allowPassthrough;
24920 };
24921
24922 var setGrabbed = function setGrabbed(ele) {
24923 ele[0]._private.grabbed = true;
24924 };
24925
24926 var setFreed = function setFreed(ele) {
24927 ele[0]._private.grabbed = false;
24928 };
24929
24930 var setInDragLayer = function setInDragLayer(ele) {
24931 ele[0]._private.rscratch.inDragLayer = true;
24932 };
24933
24934 var setOutDragLayer = function setOutDragLayer(ele) {
24935 ele[0]._private.rscratch.inDragLayer = false;
24936 };
24937
24938 var setGrabTarget = function setGrabTarget(ele) {
24939 ele[0]._private.rscratch.isGrabTarget = true;
24940 };
24941
24942 var removeGrabTarget = function removeGrabTarget(ele) {
24943 ele[0]._private.rscratch.isGrabTarget = false;
24944 };
24945
24946 var addToDragList = function addToDragList(ele, opts) {
24947 var list = opts.addToList;
24948 var listHasEle = list.has(ele);
24949
24950 if (!listHasEle) {
24951 list.merge(ele);
24952 setGrabbed(ele);
24953 }
24954 }; // helper function to determine which child nodes and inner edges
24955 // of a compound node to be dragged as well as the grabbed and selected nodes
24956
24957
24958 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24959 if (!node.cy().hasCompoundNodes()) {
24960 return;
24961 }
24962
24963 if (opts.inDragLayer == null && opts.addToList == null) {
24964 return;
24965 } // nothing to do
24966
24967
24968 var innerNodes = node.descendants();
24969
24970 if (opts.inDragLayer) {
24971 innerNodes.forEach(setInDragLayer);
24972 innerNodes.connectedEdges().forEach(setInDragLayer);
24973 }
24974
24975 if (opts.addToList) {
24976 opts.addToList.unmerge(innerNodes);
24977 }
24978 }; // adds the given nodes and its neighbourhood to the drag layer
24979
24980
24981 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24982 opts = opts || {};
24983 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24984
24985 if (opts.inDragLayer) {
24986 nodes.forEach(setInDragLayer);
24987 nodes.neighborhood().stdFilter(function (ele) {
24988 return !hasCompoundNodes || ele.isEdge();
24989 }).forEach(setInDragLayer);
24990 }
24991
24992 if (opts.addToList) {
24993 nodes.forEach(function (ele) {
24994 addToDragList(ele, opts);
24995 });
24996 }
24997
24998 addDescendantsToDrag(nodes, opts); // always add to drag
24999 // also add nodes and edges related to the topmost ancestor
25000
25001 updateAncestorsInDragLayer(nodes, {
25002 inDragLayer: opts.inDragLayer
25003 });
25004 r.updateCachedGrabbedEles();
25005 };
25006
25007 var addNodeToDrag = addNodesToDrag;
25008
25009 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
25010 if (!grabbedEles) {
25011 return;
25012 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
25013
25014
25015 r.getCachedZSortedEles().forEach(function (ele) {
25016 setFreed(ele);
25017 setOutDragLayer(ele);
25018 removeGrabTarget(ele);
25019 });
25020 r.updateCachedGrabbedEles();
25021 }; // helper function to determine which ancestor nodes and edges should go
25022 // to the drag layer (or should be removed from drag layer).
25023
25024
25025 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
25026 if (opts.inDragLayer == null && opts.addToList == null) {
25027 return;
25028 } // nothing to do
25029
25030
25031 if (!node.cy().hasCompoundNodes()) {
25032 return;
25033 } // find top-level parent
25034
25035
25036 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
25037
25038 if (parent.same(node)) {
25039 return;
25040 }
25041
25042 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
25043 var edges = nodes.connectedEdges();
25044
25045 if (opts.inDragLayer) {
25046 edges.forEach(setInDragLayer);
25047 nodes.forEach(setInDragLayer);
25048 }
25049
25050 if (opts.addToList) {
25051 nodes.forEach(function (ele) {
25052 addToDragList(ele, opts);
25053 });
25054 }
25055 };
25056
25057 var blurActiveDomElement = function blurActiveDomElement() {
25058 if (document.activeElement != null && document.activeElement.blur != null) {
25059 document.activeElement.blur();
25060 }
25061 };
25062
25063 var haveMutationsApi = typeof MutationObserver !== 'undefined';
25064 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
25065
25066 if (haveMutationsApi) {
25067 r.removeObserver = new MutationObserver(function (mutns) {
25068 // eslint-disable-line no-undef
25069 for (var i = 0; i < mutns.length; i++) {
25070 var mutn = mutns[i];
25071 var rNodes = mutn.removedNodes;
25072
25073 if (rNodes) {
25074 for (var j = 0; j < rNodes.length; j++) {
25075 var rNode = rNodes[j];
25076
25077 if (rNode === r.container) {
25078 r.destroy();
25079 break;
25080 }
25081 }
25082 }
25083 }
25084 });
25085
25086 if (r.container.parentNode) {
25087 r.removeObserver.observe(r.container.parentNode, {
25088 childList: true
25089 });
25090 }
25091 } else {
25092 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
25093 // eslint-disable-line no-unused-vars
25094 r.destroy();
25095 });
25096 }
25097
25098 var onResize = lodash_debounce(function () {
25099 r.cy.resize();
25100 }, 100);
25101
25102 if (haveMutationsApi) {
25103 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
25104
25105 r.styleObserver.observe(r.container, {
25106 attributes: true
25107 });
25108 } // auto resize
25109
25110
25111 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
25112
25113 if (haveResizeObserverApi) {
25114 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
25115
25116 r.resizeObserver.observe(r.container);
25117 }
25118
25119 var forEachUp = function forEachUp(domEle, fn) {
25120 while (domEle != null) {
25121 fn(domEle);
25122 domEle = domEle.parentNode;
25123 }
25124 };
25125
25126 var invalidateCoords = function invalidateCoords() {
25127 r.invalidateContainerClientCoordsCache();
25128 };
25129
25130 forEachUp(r.container, function (domEle) {
25131 r.registerBinding(domEle, 'transitionend', invalidateCoords);
25132 r.registerBinding(domEle, 'animationend', invalidateCoords);
25133 r.registerBinding(domEle, 'scroll', invalidateCoords);
25134 }); // stop right click menu from appearing on cy
25135
25136 r.registerBinding(r.container, 'contextmenu', function (e) {
25137 e.preventDefault();
25138 });
25139
25140 var inBoxSelection = function inBoxSelection() {
25141 return r.selection[4] !== 0;
25142 };
25143
25144 var eventInContainer = function eventInContainer(e) {
25145 // save cycles if mouse events aren't to be captured
25146 var containerPageCoords = r.findContainerClientCoords();
25147 var x = containerPageCoords[0];
25148 var y = containerPageCoords[1];
25149 var width = containerPageCoords[2];
25150 var height = containerPageCoords[3];
25151 var positions = e.touches ? e.touches : [e];
25152 var atLeastOnePosInside = false;
25153
25154 for (var i = 0; i < positions.length; i++) {
25155 var p = positions[i];
25156
25157 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
25158 atLeastOnePosInside = true;
25159 break;
25160 }
25161 }
25162
25163 if (!atLeastOnePosInside) {
25164 return false;
25165 }
25166
25167 var container = r.container;
25168 var target = e.target;
25169 var tParent = target.parentNode;
25170 var containerIsTarget = false;
25171
25172 while (tParent) {
25173 if (tParent === container) {
25174 containerIsTarget = true;
25175 break;
25176 }
25177
25178 tParent = tParent.parentNode;
25179 }
25180
25181 if (!containerIsTarget) {
25182 return false;
25183 } // if target is outisde cy container, then this event is not for us
25184
25185
25186 return true;
25187 }; // Primary key
25188
25189
25190 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
25191 if (!eventInContainer(e)) {
25192 return;
25193 }
25194
25195 e.preventDefault();
25196 blurActiveDomElement();
25197 r.hoverData.capture = true;
25198 r.hoverData.which = e.which;
25199 var cy = r.cy;
25200 var gpos = [e.clientX, e.clientY];
25201 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
25202 var select = r.selection;
25203 var nears = r.findNearestElements(pos[0], pos[1], true, false);
25204 var near = nears[0];
25205 var draggedElements = r.dragData.possibleDragElements;
25206 r.hoverData.mdownPos = pos;
25207 r.hoverData.mdownGPos = gpos;
25208
25209 var checkForTaphold = function checkForTaphold() {
25210 r.hoverData.tapholdCancelled = false;
25211 clearTimeout(r.hoverData.tapholdTimeout);
25212 r.hoverData.tapholdTimeout = setTimeout(function () {
25213 if (r.hoverData.tapholdCancelled) {
25214 return;
25215 } else {
25216 var ele = r.hoverData.down;
25217
25218 if (ele) {
25219 ele.emit({
25220 originalEvent: e,
25221 type: 'taphold',
25222 position: {
25223 x: pos[0],
25224 y: pos[1]
25225 }
25226 });
25227 } else {
25228 cy.emit({
25229 originalEvent: e,
25230 type: 'taphold',
25231 position: {
25232 x: pos[0],
25233 y: pos[1]
25234 }
25235 });
25236 }
25237 }
25238 }, r.tapholdDuration);
25239 }; // Right click button
25240
25241
25242 if (e.which == 3) {
25243 r.hoverData.cxtStarted = true;
25244 var cxtEvt = {
25245 originalEvent: e,
25246 type: 'cxttapstart',
25247 position: {
25248 x: pos[0],
25249 y: pos[1]
25250 }
25251 };
25252
25253 if (near) {
25254 near.activate();
25255 near.emit(cxtEvt);
25256 r.hoverData.down = near;
25257 } else {
25258 cy.emit(cxtEvt);
25259 }
25260
25261 r.hoverData.downTime = new Date().getTime();
25262 r.hoverData.cxtDragged = false; // Primary button
25263 } else if (e.which == 1) {
25264 if (near) {
25265 near.activate();
25266 } // Element dragging
25267
25268
25269 {
25270 // If something is under the cursor and it is draggable, prepare to grab it
25271 if (near != null) {
25272 if (r.nodeIsGrabbable(near)) {
25273 var makeEvent = function makeEvent(type) {
25274 return {
25275 originalEvent: e,
25276 type: type,
25277 position: {
25278 x: pos[0],
25279 y: pos[1]
25280 }
25281 };
25282 };
25283
25284 var triggerGrab = function triggerGrab(ele) {
25285 ele.emit(makeEvent('grab'));
25286 };
25287
25288 setGrabTarget(near);
25289
25290 if (!near.selected()) {
25291 draggedElements = r.dragData.possibleDragElements = cy.collection();
25292 addNodeToDrag(near, {
25293 addToList: draggedElements
25294 });
25295 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
25296 } else {
25297 draggedElements = r.dragData.possibleDragElements = cy.collection();
25298 var selectedNodes = cy.$(function (ele) {
25299 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
25300 });
25301 addNodesToDrag(selectedNodes, {
25302 addToList: draggedElements
25303 });
25304 near.emit(makeEvent('grabon'));
25305 selectedNodes.forEach(triggerGrab);
25306 }
25307
25308 r.redrawHint('eles', true);
25309 r.redrawHint('drag', true);
25310 }
25311 }
25312
25313 r.hoverData.down = near;
25314 r.hoverData.downs = nears;
25315 r.hoverData.downTime = new Date().getTime();
25316 }
25317 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
25318 x: pos[0],
25319 y: pos[1]
25320 });
25321
25322 if (near == null) {
25323 select[4] = 1;
25324 r.data.bgActivePosistion = {
25325 x: pos[0],
25326 y: pos[1]
25327 };
25328 r.redrawHint('select', true);
25329 r.redraw();
25330 } else if (near.pannable()) {
25331 select[4] = 1; // for future pan
25332 }
25333
25334 checkForTaphold();
25335 } // Initialize selection box coordinates
25336
25337
25338 select[0] = select[2] = pos[0];
25339 select[1] = select[3] = pos[1];
25340 }, false);
25341 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
25342 // eslint-disable-line no-undef
25343 var capture = r.hoverData.capture;
25344
25345 if (!capture && !eventInContainer(e)) {
25346 return;
25347 }
25348
25349 var preventDefault = false;
25350 var cy = r.cy;
25351 var zoom = cy.zoom();
25352 var gpos = [e.clientX, e.clientY];
25353 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
25354 var mdownPos = r.hoverData.mdownPos;
25355 var mdownGPos = r.hoverData.mdownGPos;
25356 var select = r.selection;
25357 var near = null;
25358
25359 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
25360 near = r.findNearestElement(pos[0], pos[1], true, false);
25361 }
25362
25363 var last = r.hoverData.last;
25364 var down = r.hoverData.down;
25365 var disp = [pos[0] - select[2], pos[1] - select[3]];
25366 var draggedElements = r.dragData.possibleDragElements;
25367 var isOverThresholdDrag;
25368
25369 if (mdownGPos) {
25370 var dx = gpos[0] - mdownGPos[0];
25371 var dx2 = dx * dx;
25372 var dy = gpos[1] - mdownGPos[1];
25373 var dy2 = dy * dy;
25374 var dist2 = dx2 + dy2;
25375 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
25376 }
25377
25378 var multSelKeyDown = isMultSelKeyDown(e);
25379
25380 if (isOverThresholdDrag) {
25381 r.hoverData.tapholdCancelled = true;
25382 }
25383
25384 var updateDragDelta = function updateDragDelta() {
25385 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
25386
25387 if (dragDelta.length === 0) {
25388 dragDelta.push(disp[0]);
25389 dragDelta.push(disp[1]);
25390 } else {
25391 dragDelta[0] += disp[0];
25392 dragDelta[1] += disp[1];
25393 }
25394 };
25395
25396 preventDefault = true;
25397 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
25398 x: pos[0],
25399 y: pos[1]
25400 });
25401
25402 var goIntoBoxMode = function goIntoBoxMode() {
25403 r.data.bgActivePosistion = undefined;
25404
25405 if (!r.hoverData.selecting) {
25406 cy.emit({
25407 originalEvent: e,
25408 type: 'boxstart',
25409 position: {
25410 x: pos[0],
25411 y: pos[1]
25412 }
25413 });
25414 }
25415
25416 select[4] = 1;
25417 r.hoverData.selecting = true;
25418 r.redrawHint('select', true);
25419 r.redraw();
25420 }; // trigger context drag if rmouse down
25421
25422
25423 if (r.hoverData.which === 3) {
25424 // but only if over threshold
25425 if (isOverThresholdDrag) {
25426 var cxtEvt = {
25427 originalEvent: e,
25428 type: 'cxtdrag',
25429 position: {
25430 x: pos[0],
25431 y: pos[1]
25432 }
25433 };
25434
25435 if (down) {
25436 down.emit(cxtEvt);
25437 } else {
25438 cy.emit(cxtEvt);
25439 }
25440
25441 r.hoverData.cxtDragged = true;
25442
25443 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
25444 if (r.hoverData.cxtOver) {
25445 r.hoverData.cxtOver.emit({
25446 originalEvent: e,
25447 type: 'cxtdragout',
25448 position: {
25449 x: pos[0],
25450 y: pos[1]
25451 }
25452 });
25453 }
25454
25455 r.hoverData.cxtOver = near;
25456
25457 if (near) {
25458 near.emit({
25459 originalEvent: e,
25460 type: 'cxtdragover',
25461 position: {
25462 x: pos[0],
25463 y: pos[1]
25464 }
25465 });
25466 }
25467 }
25468 } // Check if we are drag panning the entire graph
25469
25470 } else if (r.hoverData.dragging) {
25471 preventDefault = true;
25472
25473 if (cy.panningEnabled() && cy.userPanningEnabled()) {
25474 var deltaP;
25475
25476 if (r.hoverData.justStartedPan) {
25477 var mdPos = r.hoverData.mdownPos;
25478 deltaP = {
25479 x: (pos[0] - mdPos[0]) * zoom,
25480 y: (pos[1] - mdPos[1]) * zoom
25481 };
25482 r.hoverData.justStartedPan = false;
25483 } else {
25484 deltaP = {
25485 x: disp[0] * zoom,
25486 y: disp[1] * zoom
25487 };
25488 }
25489
25490 cy.panBy(deltaP);
25491 r.hoverData.dragged = true;
25492 } // Needs reproject due to pan changing viewport
25493
25494
25495 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
25496 } else if (select[4] == 1 && (down == null || down.pannable())) {
25497 if (isOverThresholdDrag) {
25498 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
25499 goIntoBoxMode();
25500 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
25501 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
25502
25503 if (allowPassthrough) {
25504 r.hoverData.dragging = true;
25505 r.hoverData.justStartedPan = true;
25506 select[4] = 0;
25507 r.data.bgActivePosistion = array2point(mdownPos);
25508 r.redrawHint('select', true);
25509 r.redraw();
25510 }
25511 }
25512
25513 if (down && down.pannable() && down.active()) {
25514 down.unactivate();
25515 }
25516 }
25517 } else {
25518 if (down && down.pannable() && down.active()) {
25519 down.unactivate();
25520 }
25521
25522 if ((!down || !down.grabbed()) && near != last) {
25523 if (last) {
25524 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
25525 x: pos[0],
25526 y: pos[1]
25527 });
25528 }
25529
25530 if (near) {
25531 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
25532 x: pos[0],
25533 y: pos[1]
25534 });
25535 }
25536
25537 r.hoverData.last = near;
25538 }
25539
25540 if (down) {
25541 if (isOverThresholdDrag) {
25542 // then we can take action
25543 if (cy.boxSelectionEnabled() && multSelKeyDown) {
25544 // then selection overrides
25545 if (down && down.grabbed()) {
25546 freeDraggedElements(draggedElements);
25547 down.emit('freeon');
25548 draggedElements.emit('free');
25549
25550 if (r.dragData.didDrag) {
25551 down.emit('dragfreeon');
25552 draggedElements.emit('dragfree');
25553 }
25554 }
25555
25556 goIntoBoxMode();
25557 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
25558 // drag node
25559 var justStartedDrag = !r.dragData.didDrag;
25560
25561 if (justStartedDrag) {
25562 r.redrawHint('eles', true);
25563 }
25564
25565 r.dragData.didDrag = true; // indicate that we actually did drag the node
25566
25567 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
25568
25569 if (!r.hoverData.draggingEles) {
25570 addNodesToDrag(draggedElements, {
25571 inDragLayer: true
25572 });
25573 }
25574
25575 var totalShift = {
25576 x: 0,
25577 y: 0
25578 };
25579
25580 if (number(disp[0]) && number(disp[1])) {
25581 totalShift.x += disp[0];
25582 totalShift.y += disp[1];
25583
25584 if (justStartedDrag) {
25585 var dragDelta = r.hoverData.dragDelta;
25586
25587 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25588 totalShift.x += dragDelta[0];
25589 totalShift.y += dragDelta[1];
25590 }
25591 }
25592 }
25593
25594 for (var i = 0; i < draggedElements.length; i++) {
25595 var dEle = draggedElements[i];
25596
25597 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
25598 toTrigger.push(dEle);
25599 }
25600 }
25601
25602 r.hoverData.draggingEles = true;
25603 toTrigger.silentShift(totalShift).emit('position drag');
25604 r.redrawHint('drag', true);
25605 r.redraw();
25606 }
25607 } else {
25608 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25609 updateDragDelta();
25610 }
25611 } // prevent the dragging from triggering text selection on the page
25612
25613
25614 preventDefault = true;
25615 }
25616
25617 select[2] = pos[0];
25618 select[3] = pos[1];
25619
25620 if (preventDefault) {
25621 if (e.stopPropagation) e.stopPropagation();
25622 if (e.preventDefault) e.preventDefault();
25623 return false;
25624 }
25625 }, false);
25626 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25627 // eslint-disable-line no-undef
25628 var capture = r.hoverData.capture;
25629
25630 if (!capture) {
25631 return;
25632 }
25633
25634 r.hoverData.capture = false;
25635 var cy = r.cy;
25636 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25637 var select = r.selection;
25638 var near = r.findNearestElement(pos[0], pos[1], true, false);
25639 var draggedElements = r.dragData.possibleDragElements;
25640 var down = r.hoverData.down;
25641 var multSelKeyDown = isMultSelKeyDown(e);
25642
25643 if (r.data.bgActivePosistion) {
25644 r.redrawHint('select', true);
25645 r.redraw();
25646 }
25647
25648 r.hoverData.tapholdCancelled = true;
25649 r.data.bgActivePosistion = undefined; // not active bg now
25650
25651 if (down) {
25652 down.unactivate();
25653 }
25654
25655 if (r.hoverData.which === 3) {
25656 var cxtEvt = {
25657 originalEvent: e,
25658 type: 'cxttapend',
25659 position: {
25660 x: pos[0],
25661 y: pos[1]
25662 }
25663 };
25664
25665 if (down) {
25666 down.emit(cxtEvt);
25667 } else {
25668 cy.emit(cxtEvt);
25669 }
25670
25671 if (!r.hoverData.cxtDragged) {
25672 var cxtTap = {
25673 originalEvent: e,
25674 type: 'cxttap',
25675 position: {
25676 x: pos[0],
25677 y: pos[1]
25678 }
25679 };
25680
25681 if (down) {
25682 down.emit(cxtTap);
25683 } else {
25684 cy.emit(cxtTap);
25685 }
25686 }
25687
25688 r.hoverData.cxtDragged = false;
25689 r.hoverData.which = null;
25690 } else if (r.hoverData.which === 1) {
25691 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25692 x: pos[0],
25693 y: pos[1]
25694 });
25695
25696 if (!r.dragData.didDrag // didn't move a node around
25697 && !r.hoverData.dragged // didn't pan
25698 && !r.hoverData.selecting // not box selection
25699 && !r.hoverData.isOverThresholdDrag // didn't move too much
25700 ) {
25701 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
25702 x: pos[0],
25703 y: pos[1]
25704 });
25705 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25706
25707
25708 if (down == null && // not mousedown on node
25709 !r.dragData.didDrag // didn't move the node around
25710 && !r.hoverData.selecting // not box selection
25711 && !r.hoverData.dragged // didn't pan
25712 && !isMultSelKeyDown(e)) {
25713 cy.$(isSelected).unselect(['tapunselect']);
25714
25715 if (draggedElements.length > 0) {
25716 r.redrawHint('eles', true);
25717 }
25718
25719 r.dragData.possibleDragElements = draggedElements = cy.collection();
25720 } // Single selection
25721
25722
25723 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25724 if (near != null && near._private.selectable) {
25725 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25726 if (near.selected()) {
25727 near.unselect(['tapunselect']);
25728 } else {
25729 near.select(['tapselect']);
25730 }
25731 } else {
25732 if (!multSelKeyDown) {
25733 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25734 near.select(['tapselect']);
25735 }
25736 }
25737
25738 r.redrawHint('eles', true);
25739 }
25740 }
25741
25742 if (r.hoverData.selecting) {
25743 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25744 r.redrawHint('select', true);
25745
25746 if (box.length > 0) {
25747 r.redrawHint('eles', true);
25748 }
25749
25750 cy.emit({
25751 type: 'boxend',
25752 originalEvent: e,
25753 position: {
25754 x: pos[0],
25755 y: pos[1]
25756 }
25757 });
25758
25759 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25760 return ele.selectable() && !ele.selected();
25761 };
25762
25763 if (cy.selectionType() === 'additive') {
25764 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25765 } else {
25766 if (!multSelKeyDown) {
25767 cy.$(isSelected).unmerge(box).unselect();
25768 }
25769
25770 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25771 } // always need redraw in case eles unselectable
25772
25773
25774 r.redraw();
25775 } // Cancel drag pan
25776
25777
25778 if (r.hoverData.dragging) {
25779 r.hoverData.dragging = false;
25780 r.redrawHint('select', true);
25781 r.redrawHint('eles', true);
25782 r.redraw();
25783 }
25784
25785 if (!select[4]) {
25786 r.redrawHint('drag', true);
25787 r.redrawHint('eles', true);
25788 var downWasGrabbed = down && down.grabbed();
25789 freeDraggedElements(draggedElements);
25790
25791 if (downWasGrabbed) {
25792 down.emit('freeon');
25793 draggedElements.emit('free');
25794
25795 if (r.dragData.didDrag) {
25796 down.emit('dragfreeon');
25797 draggedElements.emit('dragfree');
25798 }
25799 }
25800 }
25801 } // else not right mouse
25802
25803
25804 select[4] = 0;
25805 r.hoverData.down = null;
25806 r.hoverData.cxtStarted = false;
25807 r.hoverData.draggingEles = false;
25808 r.hoverData.selecting = false;
25809 r.hoverData.isOverThresholdDrag = false;
25810 r.dragData.didDrag = false;
25811 r.hoverData.dragged = false;
25812 r.hoverData.dragDelta = [];
25813 r.hoverData.mdownPos = null;
25814 r.hoverData.mdownGPos = null;
25815 }, false);
25816
25817 var wheelHandler = function wheelHandler(e) {
25818 if (r.scrollingPage) {
25819 return;
25820 } // while scrolling, ignore wheel-to-zoom
25821
25822
25823 var cy = r.cy;
25824 var zoom = cy.zoom();
25825 var pan = cy.pan();
25826 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25827 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25828
25829 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25830 // if pan dragging or cxt dragging, wheel movements make no zoom
25831 e.preventDefault();
25832 return;
25833 }
25834
25835 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25836 e.preventDefault();
25837 r.data.wheelZooming = true;
25838 clearTimeout(r.data.wheelTimeout);
25839 r.data.wheelTimeout = setTimeout(function () {
25840 r.data.wheelZooming = false;
25841 r.redrawHint('eles', true);
25842 r.redraw();
25843 }, 150);
25844 var diff;
25845
25846 if (e.deltaY != null) {
25847 diff = e.deltaY / -250;
25848 } else if (e.wheelDeltaY != null) {
25849 diff = e.wheelDeltaY / 1000;
25850 } else {
25851 diff = e.wheelDelta / 1000;
25852 }
25853
25854 diff = diff * r.wheelSensitivity;
25855 var needsWheelFix = e.deltaMode === 1;
25856
25857 if (needsWheelFix) {
25858 // fixes slow wheel events on ff/linux and ff/windows
25859 diff *= 33;
25860 }
25861
25862 var newZoom = cy.zoom() * Math.pow(10, diff);
25863
25864 if (e.type === 'gesturechange') {
25865 newZoom = r.gestureStartZoom * e.scale;
25866 }
25867
25868 cy.zoom({
25869 level: newZoom,
25870 renderedPosition: {
25871 x: rpos[0],
25872 y: rpos[1]
25873 }
25874 });
25875 }
25876 }; // Functions to help with whether mouse wheel should trigger zooming
25877 // --
25878
25879
25880 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25881 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25882 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25883 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25884
25885 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25886 // eslint-disable-line no-unused-vars
25887 r.scrollingPage = true;
25888 clearTimeout(r.scrollingPageTimeout);
25889 r.scrollingPageTimeout = setTimeout(function () {
25890 r.scrollingPage = false;
25891 }, 250);
25892 }, true); // desktop safari pinch to zoom start
25893
25894 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25895 r.gestureStartZoom = r.cy.zoom();
25896
25897 if (!r.hasTouchStarted) {
25898 // don't affect touch devices like iphone
25899 e.preventDefault();
25900 }
25901 }, true);
25902 r.registerBinding(r.container, 'gesturechange', function (e) {
25903 if (!r.hasTouchStarted) {
25904 // don't affect touch devices like iphone
25905 wheelHandler(e);
25906 }
25907 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25908 // Handle mouseout on Cytoscape container
25909
25910 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25911 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25912 r.cy.emit({
25913 originalEvent: e,
25914 type: 'mouseout',
25915 position: {
25916 x: pos[0],
25917 y: pos[1]
25918 }
25919 });
25920 }, false);
25921 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25922 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25923 r.cy.emit({
25924 originalEvent: e,
25925 type: 'mouseover',
25926 position: {
25927 x: pos[0],
25928 y: pos[1]
25929 }
25930 });
25931 }, false);
25932 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25933
25934 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25935
25936 var center1, modelCenter1; // center point on start pinch to zoom
25937
25938 var offsetLeft, offsetTop;
25939 var containerWidth, containerHeight;
25940 var twoFingersStartInside;
25941
25942 var distance = function distance(x1, y1, x2, y2) {
25943 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25944 };
25945
25946 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25947 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25948 };
25949
25950 var touchstartHandler;
25951 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25952 r.hasTouchStarted = true;
25953
25954 if (!eventInContainer(e)) {
25955 return;
25956 }
25957
25958 blurActiveDomElement();
25959 r.touchData.capture = true;
25960 r.data.bgActivePosistion = undefined;
25961 var cy = r.cy;
25962 var now = r.touchData.now;
25963 var earlier = r.touchData.earlier;
25964
25965 if (e.touches[0]) {
25966 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25967 now[0] = pos[0];
25968 now[1] = pos[1];
25969 }
25970
25971 if (e.touches[1]) {
25972 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25973 now[2] = pos[0];
25974 now[3] = pos[1];
25975 }
25976
25977 if (e.touches[2]) {
25978 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25979 now[4] = pos[0];
25980 now[5] = pos[1];
25981 } // record starting points for pinch-to-zoom
25982
25983
25984 if (e.touches[1]) {
25985 r.touchData.singleTouchMoved = true;
25986 freeDraggedElements(r.dragData.touchDragEles);
25987 var offsets = r.findContainerClientCoords();
25988 offsetLeft = offsets[0];
25989 offsetTop = offsets[1];
25990 containerWidth = offsets[2];
25991 containerHeight = offsets[3];
25992 f1x1 = e.touches[0].clientX - offsetLeft;
25993 f1y1 = e.touches[0].clientY - offsetTop;
25994 f2x1 = e.touches[1].clientX - offsetLeft;
25995 f2y1 = e.touches[1].clientY - offsetTop;
25996 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25997 var pan = cy.pan();
25998 var zoom = cy.zoom();
25999 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
26000 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
26001 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
26002 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
26003
26004 var cxtDistThreshold = 200;
26005 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
26006
26007 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
26008 var near1 = r.findNearestElement(now[0], now[1], true, true);
26009 var near2 = r.findNearestElement(now[2], now[3], true, true);
26010
26011 if (near1 && near1.isNode()) {
26012 near1.activate().emit({
26013 originalEvent: e,
26014 type: 'cxttapstart',
26015 position: {
26016 x: now[0],
26017 y: now[1]
26018 }
26019 });
26020 r.touchData.start = near1;
26021 } else if (near2 && near2.isNode()) {
26022 near2.activate().emit({
26023 originalEvent: e,
26024 type: 'cxttapstart',
26025 position: {
26026 x: now[0],
26027 y: now[1]
26028 }
26029 });
26030 r.touchData.start = near2;
26031 } else {
26032 cy.emit({
26033 originalEvent: e,
26034 type: 'cxttapstart',
26035 position: {
26036 x: now[0],
26037 y: now[1]
26038 }
26039 });
26040 }
26041
26042 if (r.touchData.start) {
26043 r.touchData.start._private.grabbed = false;
26044 }
26045
26046 r.touchData.cxt = true;
26047 r.touchData.cxtDragged = false;
26048 r.data.bgActivePosistion = undefined;
26049 r.redraw();
26050 return;
26051 }
26052 }
26053
26054 if (e.touches[2]) {
26055 // ignore
26056 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
26057 if (cy.boxSelectionEnabled()) {
26058 e.preventDefault();
26059 }
26060 } else if (e.touches[1]) ; else if (e.touches[0]) {
26061 var nears = r.findNearestElements(now[0], now[1], true, true);
26062 var near = nears[0];
26063
26064 if (near != null) {
26065 near.activate();
26066 r.touchData.start = near;
26067 r.touchData.starts = nears;
26068
26069 if (r.nodeIsGrabbable(near)) {
26070 var draggedEles = r.dragData.touchDragEles = cy.collection();
26071 var selectedNodes = null;
26072 r.redrawHint('eles', true);
26073 r.redrawHint('drag', true);
26074
26075 if (near.selected()) {
26076 // reset drag elements, since near will be added again
26077 selectedNodes = cy.$(function (ele) {
26078 return ele.selected() && r.nodeIsGrabbable(ele);
26079 });
26080 addNodesToDrag(selectedNodes, {
26081 addToList: draggedEles
26082 });
26083 } else {
26084 addNodeToDrag(near, {
26085 addToList: draggedEles
26086 });
26087 }
26088
26089 setGrabTarget(near);
26090
26091 var makeEvent = function makeEvent(type) {
26092 return {
26093 originalEvent: e,
26094 type: type,
26095 position: {
26096 x: now[0],
26097 y: now[1]
26098 }
26099 };
26100 };
26101
26102 near.emit(makeEvent('grabon'));
26103
26104 if (selectedNodes) {
26105 selectedNodes.forEach(function (n) {
26106 n.emit(makeEvent('grab'));
26107 });
26108 } else {
26109 near.emit(makeEvent('grab'));
26110 }
26111 }
26112 }
26113
26114 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
26115 x: now[0],
26116 y: now[1]
26117 });
26118
26119 if (near == null) {
26120 r.data.bgActivePosistion = {
26121 x: pos[0],
26122 y: pos[1]
26123 };
26124 r.redrawHint('select', true);
26125 r.redraw();
26126 } // Tap, taphold
26127 // -----
26128
26129
26130 r.touchData.singleTouchMoved = false;
26131 r.touchData.singleTouchStartTime = +new Date();
26132 clearTimeout(r.touchData.tapholdTimeout);
26133 r.touchData.tapholdTimeout = setTimeout(function () {
26134 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
26135 && !r.touchData.selecting // box selection shouldn't allow taphold through
26136 ) {
26137 triggerEvents(r.touchData.start, ['taphold'], e, {
26138 x: now[0],
26139 y: now[1]
26140 });
26141 }
26142 }, r.tapholdDuration);
26143 }
26144
26145 if (e.touches.length >= 1) {
26146 var sPos = r.touchData.startPosition = [];
26147
26148 for (var i = 0; i < now.length; i++) {
26149 sPos[i] = earlier[i] = now[i];
26150 }
26151
26152 var touch0 = e.touches[0];
26153 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
26154 }
26155 }, false);
26156 var touchmoveHandler;
26157 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
26158 // eslint-disable-line no-undef
26159 var capture = r.touchData.capture;
26160
26161 if (!capture && !eventInContainer(e)) {
26162 return;
26163 }
26164
26165 var select = r.selection;
26166 var cy = r.cy;
26167 var now = r.touchData.now;
26168 var earlier = r.touchData.earlier;
26169 var zoom = cy.zoom();
26170
26171 if (e.touches[0]) {
26172 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26173 now[0] = pos[0];
26174 now[1] = pos[1];
26175 }
26176
26177 if (e.touches[1]) {
26178 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26179 now[2] = pos[0];
26180 now[3] = pos[1];
26181 }
26182
26183 if (e.touches[2]) {
26184 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26185 now[4] = pos[0];
26186 now[5] = pos[1];
26187 }
26188
26189 var startGPos = r.touchData.startGPosition;
26190 var isOverThresholdDrag;
26191
26192 if (capture && e.touches[0] && startGPos) {
26193 var disp = [];
26194
26195 for (var j = 0; j < now.length; j++) {
26196 disp[j] = now[j] - earlier[j];
26197 }
26198
26199 var dx = e.touches[0].clientX - startGPos[0];
26200 var dx2 = dx * dx;
26201 var dy = e.touches[0].clientY - startGPos[1];
26202 var dy2 = dy * dy;
26203 var dist2 = dx2 + dy2;
26204 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
26205 } // context swipe cancelling
26206
26207
26208 if (capture && r.touchData.cxt) {
26209 e.preventDefault();
26210 var f1x2 = e.touches[0].clientX - offsetLeft,
26211 f1y2 = e.touches[0].clientY - offsetTop;
26212 var f2x2 = e.touches[1].clientX - offsetLeft,
26213 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
26214
26215 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
26216 var factorSq = distance2Sq / distance1Sq;
26217 var distThreshold = 150;
26218 var distThresholdSq = distThreshold * distThreshold;
26219 var factorThreshold = 1.5;
26220 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
26221
26222 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
26223 r.touchData.cxt = false;
26224 r.data.bgActivePosistion = undefined;
26225 r.redrawHint('select', true);
26226 var cxtEvt = {
26227 originalEvent: e,
26228 type: 'cxttapend',
26229 position: {
26230 x: now[0],
26231 y: now[1]
26232 }
26233 };
26234
26235 if (r.touchData.start) {
26236 r.touchData.start.unactivate().emit(cxtEvt);
26237 r.touchData.start = null;
26238 } else {
26239 cy.emit(cxtEvt);
26240 }
26241 }
26242 } // context swipe
26243
26244
26245 if (capture && r.touchData.cxt) {
26246 var cxtEvt = {
26247 originalEvent: e,
26248 type: 'cxtdrag',
26249 position: {
26250 x: now[0],
26251 y: now[1]
26252 }
26253 };
26254 r.data.bgActivePosistion = undefined;
26255 r.redrawHint('select', true);
26256
26257 if (r.touchData.start) {
26258 r.touchData.start.emit(cxtEvt);
26259 } else {
26260 cy.emit(cxtEvt);
26261 }
26262
26263 if (r.touchData.start) {
26264 r.touchData.start._private.grabbed = false;
26265 }
26266
26267 r.touchData.cxtDragged = true;
26268 var near = r.findNearestElement(now[0], now[1], true, true);
26269
26270 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
26271 if (r.touchData.cxtOver) {
26272 r.touchData.cxtOver.emit({
26273 originalEvent: e,
26274 type: 'cxtdragout',
26275 position: {
26276 x: now[0],
26277 y: now[1]
26278 }
26279 });
26280 }
26281
26282 r.touchData.cxtOver = near;
26283
26284 if (near) {
26285 near.emit({
26286 originalEvent: e,
26287 type: 'cxtdragover',
26288 position: {
26289 x: now[0],
26290 y: now[1]
26291 }
26292 });
26293 }
26294 } // box selection
26295
26296 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
26297 e.preventDefault();
26298 r.data.bgActivePosistion = undefined;
26299 this.lastThreeTouch = +new Date();
26300
26301 if (!r.touchData.selecting) {
26302 cy.emit({
26303 originalEvent: e,
26304 type: 'boxstart',
26305 position: {
26306 x: now[0],
26307 y: now[1]
26308 }
26309 });
26310 }
26311
26312 r.touchData.selecting = true;
26313 r.touchData.didSelect = true;
26314 select[4] = 1;
26315
26316 if (!select || select.length === 0 || select[0] === undefined) {
26317 select[0] = (now[0] + now[2] + now[4]) / 3;
26318 select[1] = (now[1] + now[3] + now[5]) / 3;
26319 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
26320 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
26321 } else {
26322 select[2] = (now[0] + now[2] + now[4]) / 3;
26323 select[3] = (now[1] + now[3] + now[5]) / 3;
26324 }
26325
26326 r.redrawHint('select', true);
26327 r.redraw(); // pinch to zoom
26328 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
26329 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
26330 // two fingers => pinch to zoom
26331 e.preventDefault();
26332 r.data.bgActivePosistion = undefined;
26333 r.redrawHint('select', true);
26334 var draggedEles = r.dragData.touchDragEles;
26335
26336 if (draggedEles) {
26337 r.redrawHint('drag', true);
26338
26339 for (var i = 0; i < draggedEles.length; i++) {
26340 var de_p = draggedEles[i]._private;
26341 de_p.grabbed = false;
26342 de_p.rscratch.inDragLayer = false;
26343 }
26344 }
26345
26346 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
26347
26348 var f1x2 = e.touches[0].clientX - offsetLeft,
26349 f1y2 = e.touches[0].clientY - offsetTop;
26350 var f2x2 = e.touches[1].clientX - offsetLeft,
26351 f2y2 = e.touches[1].clientY - offsetTop;
26352 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
26353 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
26354
26355 var factor = distance2 / distance1;
26356
26357 if (twoFingersStartInside) {
26358 // delta finger1
26359 var df1x = f1x2 - f1x1;
26360 var df1y = f1y2 - f1y1; // delta finger 2
26361
26362 var df2x = f2x2 - f2x1;
26363 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
26364 // i.e. so pinching cancels out and moving together pans
26365
26366 var tx = (df1x + df2x) / 2;
26367 var ty = (df1y + df2y) / 2; // now calculate the zoom
26368
26369 var zoom1 = cy.zoom();
26370 var zoom2 = zoom1 * factor;
26371 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
26372
26373 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
26374 var ctry = modelCenter1[1] * zoom1 + pan1.y;
26375 var pan2 = {
26376 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
26377 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
26378 }; // remove dragged eles
26379
26380 if (_start && _start.active()) {
26381 var draggedEles = r.dragData.touchDragEles;
26382 freeDraggedElements(draggedEles);
26383 r.redrawHint('drag', true);
26384 r.redrawHint('eles', true);
26385
26386 _start.unactivate().emit('freeon');
26387
26388 draggedEles.emit('free');
26389
26390 if (r.dragData.didDrag) {
26391 _start.emit('dragfreeon');
26392
26393 draggedEles.emit('dragfree');
26394 }
26395 }
26396
26397 cy.viewport({
26398 zoom: zoom2,
26399 pan: pan2,
26400 cancelOnFailedZoom: true
26401 });
26402 distance1 = distance2;
26403 f1x1 = f1x2;
26404 f1y1 = f1y2;
26405 f2x1 = f2x2;
26406 f2y1 = f2y2;
26407 r.pinching = true;
26408 } // Re-project
26409
26410
26411 if (e.touches[0]) {
26412 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26413 now[0] = pos[0];
26414 now[1] = pos[1];
26415 }
26416
26417 if (e.touches[1]) {
26418 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26419 now[2] = pos[0];
26420 now[3] = pos[1];
26421 }
26422
26423 if (e.touches[2]) {
26424 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26425 now[4] = pos[0];
26426 now[5] = pos[1];
26427 }
26428 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
26429 ) {
26430 var start = r.touchData.start;
26431 var last = r.touchData.last;
26432 var near;
26433
26434 if (!r.hoverData.draggingEles && !r.swipePanning) {
26435 near = r.findNearestElement(now[0], now[1], true, true);
26436 }
26437
26438 if (capture && start != null) {
26439 e.preventDefault();
26440 } // dragging nodes
26441
26442
26443 if (capture && start != null && r.nodeIsDraggable(start)) {
26444 if (isOverThresholdDrag) {
26445 // then dragging can happen
26446 var draggedEles = r.dragData.touchDragEles;
26447 var justStartedDrag = !r.dragData.didDrag;
26448
26449 if (justStartedDrag) {
26450 addNodesToDrag(draggedEles, {
26451 inDragLayer: true
26452 });
26453 }
26454
26455 r.dragData.didDrag = true;
26456 var totalShift = {
26457 x: 0,
26458 y: 0
26459 };
26460
26461 if (number(disp[0]) && number(disp[1])) {
26462 totalShift.x += disp[0];
26463 totalShift.y += disp[1];
26464
26465 if (justStartedDrag) {
26466 r.redrawHint('eles', true);
26467 var dragDelta = r.touchData.dragDelta;
26468
26469 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
26470 totalShift.x += dragDelta[0];
26471 totalShift.y += dragDelta[1];
26472 }
26473 }
26474 }
26475
26476 r.hoverData.draggingEles = true;
26477 draggedEles.silentShift(totalShift).emit('position drag');
26478 r.redrawHint('drag', true);
26479
26480 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
26481 r.redrawHint('eles', true);
26482 }
26483
26484 r.redraw();
26485 } else {
26486 // otherise keep track of drag delta for later
26487 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
26488
26489 if (dragDelta.length === 0) {
26490 dragDelta.push(disp[0]);
26491 dragDelta.push(disp[1]);
26492 } else {
26493 dragDelta[0] += disp[0];
26494 dragDelta[1] += disp[1];
26495 }
26496 }
26497 } // touchmove
26498
26499
26500 {
26501 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
26502 x: now[0],
26503 y: now[1]
26504 });
26505
26506 if ((!start || !start.grabbed()) && near != last) {
26507 if (last) {
26508 last.emit({
26509 originalEvent: e,
26510 type: 'tapdragout',
26511 position: {
26512 x: now[0],
26513 y: now[1]
26514 }
26515 });
26516 }
26517
26518 if (near) {
26519 near.emit({
26520 originalEvent: e,
26521 type: 'tapdragover',
26522 position: {
26523 x: now[0],
26524 y: now[1]
26525 }
26526 });
26527 }
26528 }
26529
26530 r.touchData.last = near;
26531 } // check to cancel taphold
26532
26533 if (capture) {
26534 for (var i = 0; i < now.length; i++) {
26535 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
26536 r.touchData.singleTouchMoved = true;
26537 }
26538 }
26539 } // panning
26540
26541
26542 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
26543 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
26544
26545 if (allowPassthrough) {
26546 e.preventDefault();
26547
26548 if (!r.data.bgActivePosistion) {
26549 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
26550 }
26551
26552 if (r.swipePanning) {
26553 cy.panBy({
26554 x: disp[0] * zoom,
26555 y: disp[1] * zoom
26556 });
26557 } else if (isOverThresholdDrag) {
26558 r.swipePanning = true;
26559 cy.panBy({
26560 x: dx * zoom,
26561 y: dy * zoom
26562 });
26563
26564 if (start) {
26565 start.unactivate();
26566 r.redrawHint('select', true);
26567 r.touchData.start = null;
26568 }
26569 }
26570 } // Re-project
26571
26572
26573 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26574 now[0] = pos[0];
26575 now[1] = pos[1];
26576 }
26577 }
26578
26579 for (var j = 0; j < now.length; j++) {
26580 earlier[j] = now[j];
26581 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26582
26583
26584 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26585 r.data.bgActivePosistion = undefined;
26586 r.redrawHint('select', true);
26587 r.redraw();
26588 }
26589 }, false);
26590 var touchcancelHandler;
26591 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26592 // eslint-disable-line no-unused-vars
26593 var start = r.touchData.start;
26594 r.touchData.capture = false;
26595
26596 if (start) {
26597 start.unactivate();
26598 }
26599 });
26600 var touchendHandler;
26601 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26602 // eslint-disable-line no-unused-vars
26603 var start = r.touchData.start;
26604 var capture = r.touchData.capture;
26605
26606 if (capture) {
26607 if (e.touches.length === 0) {
26608 r.touchData.capture = false;
26609 }
26610
26611 e.preventDefault();
26612 } else {
26613 return;
26614 }
26615
26616 var select = r.selection;
26617 r.swipePanning = false;
26618 r.hoverData.draggingEles = false;
26619 var cy = r.cy;
26620 var zoom = cy.zoom();
26621 var now = r.touchData.now;
26622 var earlier = r.touchData.earlier;
26623
26624 if (e.touches[0]) {
26625 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26626 now[0] = pos[0];
26627 now[1] = pos[1];
26628 }
26629
26630 if (e.touches[1]) {
26631 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26632 now[2] = pos[0];
26633 now[3] = pos[1];
26634 }
26635
26636 if (e.touches[2]) {
26637 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26638 now[4] = pos[0];
26639 now[5] = pos[1];
26640 }
26641
26642 if (start) {
26643 start.unactivate();
26644 }
26645
26646 var ctxTapend;
26647
26648 if (r.touchData.cxt) {
26649 ctxTapend = {
26650 originalEvent: e,
26651 type: 'cxttapend',
26652 position: {
26653 x: now[0],
26654 y: now[1]
26655 }
26656 };
26657
26658 if (start) {
26659 start.emit(ctxTapend);
26660 } else {
26661 cy.emit(ctxTapend);
26662 }
26663
26664 if (!r.touchData.cxtDragged) {
26665 var ctxTap = {
26666 originalEvent: e,
26667 type: 'cxttap',
26668 position: {
26669 x: now[0],
26670 y: now[1]
26671 }
26672 };
26673
26674 if (start) {
26675 start.emit(ctxTap);
26676 } else {
26677 cy.emit(ctxTap);
26678 }
26679 }
26680
26681 if (r.touchData.start) {
26682 r.touchData.start._private.grabbed = false;
26683 }
26684
26685 r.touchData.cxt = false;
26686 r.touchData.start = null;
26687 r.redraw();
26688 return;
26689 } // no more box selection if we don't have three fingers
26690
26691
26692 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26693 r.touchData.selecting = false;
26694 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26695 select[0] = undefined;
26696 select[1] = undefined;
26697 select[2] = undefined;
26698 select[3] = undefined;
26699 select[4] = 0;
26700 r.redrawHint('select', true);
26701 cy.emit({
26702 type: 'boxend',
26703 originalEvent: e,
26704 position: {
26705 x: now[0],
26706 y: now[1]
26707 }
26708 });
26709
26710 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26711 return ele.selectable() && !ele.selected();
26712 };
26713
26714 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26715
26716 if (box.nonempty()) {
26717 r.redrawHint('eles', true);
26718 }
26719
26720 r.redraw();
26721 }
26722
26723 if (start != null) {
26724 start.unactivate();
26725 }
26726
26727 if (e.touches[2]) {
26728 r.data.bgActivePosistion = undefined;
26729 r.redrawHint('select', true);
26730 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26731 r.data.bgActivePosistion = undefined;
26732 r.redrawHint('select', true);
26733 var draggedEles = r.dragData.touchDragEles;
26734
26735 if (start != null) {
26736 var startWasGrabbed = start._private.grabbed;
26737 freeDraggedElements(draggedEles);
26738 r.redrawHint('drag', true);
26739 r.redrawHint('eles', true);
26740
26741 if (startWasGrabbed) {
26742 start.emit('freeon');
26743 draggedEles.emit('free');
26744
26745 if (r.dragData.didDrag) {
26746 start.emit('dragfreeon');
26747 draggedEles.emit('dragfree');
26748 }
26749 }
26750
26751 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26752 x: now[0],
26753 y: now[1]
26754 });
26755 start.unactivate();
26756 r.touchData.start = null;
26757 } else {
26758 var near = r.findNearestElement(now[0], now[1], true, true);
26759 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26760 x: now[0],
26761 y: now[1]
26762 });
26763 }
26764
26765 var dx = r.touchData.startPosition[0] - now[0];
26766 var dx2 = dx * dx;
26767 var dy = r.touchData.startPosition[1] - now[1];
26768 var dy2 = dy * dy;
26769 var dist2 = dx2 + dy2;
26770 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26771
26772 if (!r.touchData.singleTouchMoved) {
26773 if (!start) {
26774 cy.$(':selected').unselect(['tapunselect']);
26775 }
26776
26777 triggerEvents(start, ['tap', 'vclick'], e, {
26778 x: now[0],
26779 y: now[1]
26780 });
26781 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26782
26783
26784 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26785 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26786 ) {
26787 if (cy.selectionType() === 'single') {
26788 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26789 start.select(['tapselect']);
26790 } else {
26791 if (start.selected()) {
26792 start.unselect(['tapunselect']);
26793 } else {
26794 start.select(['tapselect']);
26795 }
26796 }
26797
26798 r.redrawHint('eles', true);
26799 }
26800
26801 r.touchData.singleTouchMoved = true;
26802 }
26803
26804 for (var j = 0; j < now.length; j++) {
26805 earlier[j] = now[j];
26806 }
26807
26808 r.dragData.didDrag = false; // reset for next touchstart
26809
26810 if (e.touches.length === 0) {
26811 r.touchData.dragDelta = [];
26812 r.touchData.startPosition = null;
26813 r.touchData.startGPosition = null;
26814 r.touchData.didSelect = false;
26815 }
26816
26817 if (e.touches.length < 2) {
26818 if (e.touches.length === 1) {
26819 // the old start global pos'n may not be the same finger that remains
26820 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26821 }
26822
26823 r.pinching = false;
26824 r.redrawHint('eles', true);
26825 r.redraw();
26826 } //r.redraw();
26827
26828 }, false); // fallback compatibility layer for ms pointer events
26829
26830 if (typeof TouchEvent === 'undefined') {
26831 var pointers = [];
26832
26833 var makeTouch = function makeTouch(e) {
26834 return {
26835 clientX: e.clientX,
26836 clientY: e.clientY,
26837 force: 1,
26838 identifier: e.pointerId,
26839 pageX: e.pageX,
26840 pageY: e.pageY,
26841 radiusX: e.width / 2,
26842 radiusY: e.height / 2,
26843 screenX: e.screenX,
26844 screenY: e.screenY,
26845 target: e.target
26846 };
26847 };
26848
26849 var makePointer = function makePointer(e) {
26850 return {
26851 event: e,
26852 touch: makeTouch(e)
26853 };
26854 };
26855
26856 var addPointer = function addPointer(e) {
26857 pointers.push(makePointer(e));
26858 };
26859
26860 var removePointer = function removePointer(e) {
26861 for (var i = 0; i < pointers.length; i++) {
26862 var p = pointers[i];
26863
26864 if (p.event.pointerId === e.pointerId) {
26865 pointers.splice(i, 1);
26866 return;
26867 }
26868 }
26869 };
26870
26871 var updatePointer = function updatePointer(e) {
26872 var p = pointers.filter(function (p) {
26873 return p.event.pointerId === e.pointerId;
26874 })[0];
26875 p.event = e;
26876 p.touch = makeTouch(e);
26877 };
26878
26879 var addTouchesToEvent = function addTouchesToEvent(e) {
26880 e.touches = pointers.map(function (p) {
26881 return p.touch;
26882 });
26883 };
26884
26885 var pointerIsMouse = function pointerIsMouse(e) {
26886 return e.pointerType === 'mouse' || e.pointerType === 4;
26887 };
26888
26889 r.registerBinding(r.container, 'pointerdown', function (e) {
26890 if (pointerIsMouse(e)) {
26891 return;
26892 } // mouse already handled
26893
26894
26895 e.preventDefault();
26896 addPointer(e);
26897 addTouchesToEvent(e);
26898 touchstartHandler(e);
26899 });
26900 r.registerBinding(r.container, 'pointerup', function (e) {
26901 if (pointerIsMouse(e)) {
26902 return;
26903 } // mouse already handled
26904
26905
26906 removePointer(e);
26907 addTouchesToEvent(e);
26908 touchendHandler(e);
26909 });
26910 r.registerBinding(r.container, 'pointercancel', function (e) {
26911 if (pointerIsMouse(e)) {
26912 return;
26913 } // mouse already handled
26914
26915
26916 removePointer(e);
26917 addTouchesToEvent(e);
26918 touchcancelHandler(e);
26919 });
26920 r.registerBinding(r.container, 'pointermove', function (e) {
26921 if (pointerIsMouse(e)) {
26922 return;
26923 } // mouse already handled
26924
26925
26926 e.preventDefault();
26927 updatePointer(e);
26928 addTouchesToEvent(e);
26929 touchmoveHandler(e);
26930 });
26931 }
26932 };
26933
26934 var BRp$d = {};
26935
26936 BRp$d.generatePolygon = function (name, points) {
26937 return this.nodeShapes[name] = {
26938 renderer: this,
26939 name: name,
26940 points: points,
26941 draw: function draw(context, centerX, centerY, width, height) {
26942 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26943 },
26944 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26945 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26946 },
26947 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26948 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26949 }
26950 };
26951 };
26952
26953 BRp$d.generateEllipse = function () {
26954 return this.nodeShapes['ellipse'] = {
26955 renderer: this,
26956 name: 'ellipse',
26957 draw: function draw(context, centerX, centerY, width, height) {
26958 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26959 },
26960 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26961 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26962 },
26963 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26964 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26965 }
26966 };
26967 };
26968
26969 BRp$d.generateRoundPolygon = function (name, points) {
26970 // Pre-compute control points
26971 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26972 // the unit vectors.
26973 // For simplicity the layout will be:
26974 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26975 var allPoints = new Array(points.length * 2);
26976
26977 for (var i = 0; i < points.length / 2; i++) {
26978 var sourceIndex = i * 2;
26979 var destIndex = void 0;
26980
26981 if (i < points.length / 2 - 1) {
26982 destIndex = (i + 1) * 2;
26983 } else {
26984 destIndex = 0;
26985 }
26986
26987 allPoints[i * 4] = points[sourceIndex];
26988 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26989 var xDest = points[destIndex] - points[sourceIndex];
26990 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26991 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26992 allPoints[i * 4 + 2] = xDest / norm;
26993 allPoints[i * 4 + 3] = yDest / norm;
26994 }
26995
26996 return this.nodeShapes[name] = {
26997 renderer: this,
26998 name: name,
26999 points: allPoints,
27000 draw: function draw(context, centerX, centerY, width, height) {
27001 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
27002 },
27003 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27004 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
27005 },
27006 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27007 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
27008 }
27009 };
27010 };
27011
27012 BRp$d.generateRoundRectangle = function () {
27013 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
27014 renderer: this,
27015 name: 'round-rectangle',
27016 points: generateUnitNgonPointsFitToSquare(4, 0),
27017 draw: function draw(context, centerX, centerY, width, height) {
27018 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27019 },
27020 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27021 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27022 },
27023 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27024 var cornerRadius = getRoundRectangleRadius(width, height);
27025 var diam = cornerRadius * 2; // Check hBox
27026
27027 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27028 return true;
27029 } // Check vBox
27030
27031
27032 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27033 return true;
27034 } // Check top left quarter circle
27035
27036
27037 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
27038 return true;
27039 } // Check top right quarter circle
27040
27041
27042 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
27043 return true;
27044 } // Check bottom right quarter circle
27045
27046
27047 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27048 return true;
27049 } // Check bottom left quarter circle
27050
27051
27052 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27053 return true;
27054 }
27055
27056 return false;
27057 }
27058 };
27059 };
27060
27061 BRp$d.generateCutRectangle = function () {
27062 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
27063 renderer: this,
27064 name: 'cut-rectangle',
27065 cornerLength: getCutRectangleCornerLength(),
27066 points: generateUnitNgonPointsFitToSquare(4, 0),
27067 draw: function draw(context, centerX, centerY, width, height) {
27068 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27069 },
27070 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
27071 var cl = this.cornerLength;
27072 var hh = height / 2;
27073 var hw = width / 2;
27074 var xBegin = centerX - hw;
27075 var xEnd = centerX + hw;
27076 var yBegin = centerY - hh;
27077 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
27078
27079 return {
27080 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
27081 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
27082 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
27083 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
27084 };
27085 },
27086 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27087 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27088 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
27089 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27090 },
27091 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27092 // Check hBox
27093 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
27094 return true;
27095 } // Check vBox
27096
27097
27098 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
27099 return true;
27100 }
27101
27102 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
27103 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
27104 }
27105 };
27106 };
27107
27108 BRp$d.generateBarrel = function () {
27109 return this.nodeShapes['barrel'] = {
27110 renderer: this,
27111 name: 'barrel',
27112 points: generateUnitNgonPointsFitToSquare(4, 0),
27113 draw: function draw(context, centerX, centerY, width, height) {
27114 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27115 },
27116 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27117 // use two fixed t values for the bezier curve approximation
27118 var t0 = 0.15;
27119 var t1 = 0.5;
27120 var t2 = 0.85;
27121 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27122
27123 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
27124 // approximate curve pts based on the two t values
27125 var m0 = qbezierPtAt({
27126 x: pts[0],
27127 y: pts[1]
27128 }, {
27129 x: pts[2],
27130 y: pts[3]
27131 }, {
27132 x: pts[4],
27133 y: pts[5]
27134 }, t0);
27135 var m1 = qbezierPtAt({
27136 x: pts[0],
27137 y: pts[1]
27138 }, {
27139 x: pts[2],
27140 y: pts[3]
27141 }, {
27142 x: pts[4],
27143 y: pts[5]
27144 }, t1);
27145 var m2 = qbezierPtAt({
27146 x: pts[0],
27147 y: pts[1]
27148 }, {
27149 x: pts[2],
27150 y: pts[3]
27151 }, {
27152 x: pts[4],
27153 y: pts[5]
27154 }, t2);
27155 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
27156 };
27157
27158 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
27159 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27160 },
27161 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
27162 var hh = height / 2;
27163 var hw = width / 2;
27164 var xBegin = centerX - hw;
27165 var xEnd = centerX + hw;
27166 var yBegin = centerY - hh;
27167 var yEnd = centerY + hh;
27168 var curveConstants = getBarrelCurveConstants(width, height);
27169 var hOffset = curveConstants.heightOffset;
27170 var wOffset = curveConstants.widthOffset;
27171 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
27172
27173 var pts = {
27174 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
27175 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
27176 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
27177 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
27178 };
27179 pts.topLeft.isTop = true;
27180 pts.topRight.isTop = true;
27181 pts.bottomLeft.isBottom = true;
27182 pts.bottomRight.isBottom = true;
27183 return pts;
27184 },
27185 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27186 var curveConstants = getBarrelCurveConstants(width, height);
27187 var hOffset = curveConstants.heightOffset;
27188 var wOffset = curveConstants.widthOffset; // Check hBox
27189
27190 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
27191 return true;
27192 } // Check vBox
27193
27194
27195 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
27196 return true;
27197 }
27198
27199 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
27200
27201 var getCurveT = function getCurveT(x, y, curvePts) {
27202 var x0 = curvePts[4];
27203 var x1 = curvePts[2];
27204 var x2 = curvePts[0];
27205 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
27206
27207 var y2 = curvePts[1];
27208 var xMin = Math.min(x0, x2);
27209 var xMax = Math.max(x0, x2);
27210 var yMin = Math.min(y0, y2);
27211 var yMax = Math.max(y0, y2);
27212
27213 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
27214 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
27215 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
27216 var validRoots = roots.filter(function (r) {
27217 return 0 <= r && r <= 1;
27218 });
27219
27220 if (validRoots.length > 0) {
27221 return validRoots[0];
27222 }
27223 }
27224
27225 return null;
27226 };
27227
27228 var curveRegions = Object.keys(barrelCurvePts);
27229
27230 for (var i = 0; i < curveRegions.length; i++) {
27231 var corner = curveRegions[i];
27232 var cornerPts = barrelCurvePts[corner];
27233 var t = getCurveT(x, y, cornerPts);
27234
27235 if (t == null) {
27236 continue;
27237 }
27238
27239 var y0 = cornerPts[5];
27240 var y1 = cornerPts[3];
27241 var y2 = cornerPts[1];
27242 var bezY = qbezierAt(y0, y1, y2, t);
27243
27244 if (cornerPts.isTop && bezY <= y) {
27245 return true;
27246 }
27247
27248 if (cornerPts.isBottom && y <= bezY) {
27249 return true;
27250 }
27251 }
27252
27253 return false;
27254 }
27255 };
27256 };
27257
27258 BRp$d.generateBottomRoundrectangle = function () {
27259 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
27260 renderer: this,
27261 name: 'bottom-round-rectangle',
27262 points: generateUnitNgonPointsFitToSquare(4, 0),
27263 draw: function draw(context, centerX, centerY, width, height) {
27264 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27265 },
27266 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27267 var topStartX = nodeX - (width / 2 + padding);
27268 var topStartY = nodeY - (height / 2 + padding);
27269 var topEndY = topStartY;
27270 var topEndX = nodeX + (width / 2 + padding);
27271 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
27272
27273 if (topIntersections.length > 0) {
27274 return topIntersections;
27275 }
27276
27277 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27278 },
27279 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27280 var cornerRadius = getRoundRectangleRadius(width, height);
27281 var diam = 2 * cornerRadius; // Check hBox
27282
27283 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27284 return true;
27285 } // Check vBox
27286
27287
27288 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27289 return true;
27290 } // check non-rounded top side
27291
27292
27293 var outerWidth = width / 2 + 2 * padding;
27294 var outerHeight = height / 2 + 2 * padding;
27295 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
27296
27297 if (pointInsidePolygonPoints(x, y, points)) {
27298 return true;
27299 } // Check bottom right quarter circle
27300
27301
27302 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27303 return true;
27304 } // Check bottom left quarter circle
27305
27306
27307 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27308 return true;
27309 }
27310
27311 return false;
27312 }
27313 };
27314 };
27315
27316 BRp$d.registerNodeShapes = function () {
27317 var nodeShapes = this.nodeShapes = {};
27318 var renderer = this;
27319 this.generateEllipse();
27320 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
27321 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
27322 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
27323 nodeShapes['square'] = nodeShapes['rectangle'];
27324 this.generateRoundRectangle();
27325 this.generateCutRectangle();
27326 this.generateBarrel();
27327 this.generateBottomRoundrectangle();
27328 {
27329 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
27330 this.generatePolygon('diamond', diamondPoints);
27331 this.generateRoundPolygon('round-diamond', diamondPoints);
27332 }
27333 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27334 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27335 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27336 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27337 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27338 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27339 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
27340 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
27341 var star5Points = new Array(20);
27342 {
27343 var outerPoints = generateUnitNgonPoints(5, 0);
27344 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
27345
27346 var innerRadius = 0.5 * (3 - Math.sqrt(5));
27347 innerRadius *= 1.57;
27348
27349 for (var i = 0; i < innerPoints.length / 2; i++) {
27350 innerPoints[i * 2] *= innerRadius;
27351 innerPoints[i * 2 + 1] *= innerRadius;
27352 }
27353
27354 for (var i = 0; i < 20 / 4; i++) {
27355 star5Points[i * 4] = outerPoints[i * 2];
27356 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
27357 star5Points[i * 4 + 2] = innerPoints[i * 2];
27358 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
27359 }
27360 }
27361 star5Points = fitPolygonToSquare(star5Points);
27362 this.generatePolygon('star', star5Points);
27363 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
27364 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
27365 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]);
27366 {
27367 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
27368 this.generatePolygon('tag', tagPoints);
27369 this.generateRoundPolygon('round-tag', tagPoints);
27370 }
27371
27372 nodeShapes.makePolygon = function (points) {
27373 // use caching on user-specified polygons so they are as fast as native shapes
27374 var key = points.join('$');
27375 var name = 'polygon-' + key;
27376 var shape;
27377
27378 if (shape = this[name]) {
27379 // got cached shape
27380 return shape;
27381 } // create and cache new shape
27382
27383
27384 return renderer.generatePolygon(name, points);
27385 };
27386 };
27387
27388 var BRp$e = {};
27389
27390 BRp$e.timeToRender = function () {
27391 return this.redrawTotalTime / this.redrawCount;
27392 };
27393
27394 BRp$e.redraw = function (options) {
27395 options = options || staticEmptyObject();
27396 var r = this;
27397
27398 if (r.averageRedrawTime === undefined) {
27399 r.averageRedrawTime = 0;
27400 }
27401
27402 if (r.lastRedrawTime === undefined) {
27403 r.lastRedrawTime = 0;
27404 }
27405
27406 if (r.lastDrawTime === undefined) {
27407 r.lastDrawTime = 0;
27408 }
27409
27410 r.requestedFrame = true;
27411 r.renderOptions = options;
27412 };
27413
27414 BRp$e.beforeRender = function (fn, priority) {
27415 // the renderer can't add tick callbacks when destroyed
27416 if (this.destroyed) {
27417 return;
27418 }
27419
27420 if (priority == null) {
27421 error('Priority is not optional for beforeRender');
27422 }
27423
27424 var cbs = this.beforeRenderCallbacks;
27425 cbs.push({
27426 fn: fn,
27427 priority: priority
27428 }); // higher priority callbacks executed first
27429
27430 cbs.sort(function (a, b) {
27431 return b.priority - a.priority;
27432 });
27433 };
27434
27435 var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
27436 var cbs = r.beforeRenderCallbacks;
27437
27438 for (var i = 0; i < cbs.length; i++) {
27439 cbs[i].fn(willDraw, startTime);
27440 }
27441 };
27442
27443 BRp$e.startRenderLoop = function () {
27444 var r = this;
27445 var cy = r.cy;
27446
27447 if (r.renderLoopStarted) {
27448 return;
27449 } else {
27450 r.renderLoopStarted = true;
27451 }
27452
27453 var renderFn = function renderFn(requestTime) {
27454 if (r.destroyed) {
27455 return;
27456 }
27457
27458 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
27459 beforeRenderCallbacks(r, true, requestTime);
27460 var startTime = performanceNow();
27461 r.render(r.renderOptions);
27462 var endTime = r.lastDrawTime = performanceNow();
27463
27464 if (r.averageRedrawTime === undefined) {
27465 r.averageRedrawTime = endTime - startTime;
27466 }
27467
27468 if (r.redrawCount === undefined) {
27469 r.redrawCount = 0;
27470 }
27471
27472 r.redrawCount++;
27473
27474 if (r.redrawTotalTime === undefined) {
27475 r.redrawTotalTime = 0;
27476 }
27477
27478 var duration = endTime - startTime;
27479 r.redrawTotalTime += duration;
27480 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
27481
27482 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
27483 r.requestedFrame = false;
27484 } else {
27485 beforeRenderCallbacks(r, false, requestTime);
27486 }
27487
27488 r.skipFrame = false;
27489 requestAnimationFrame(renderFn);
27490 };
27491
27492 requestAnimationFrame(renderFn);
27493 };
27494
27495 var BaseRenderer = function BaseRenderer(options) {
27496 this.init(options);
27497 };
27498
27499 var BR = BaseRenderer;
27500 var BRp$f = BR.prototype;
27501 BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
27502
27503 BRp$f.init = function (options) {
27504 var r = this;
27505 r.options = options;
27506 r.cy = options.cy;
27507 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
27508
27509 if (window$1) {
27510 var document = window$1.document;
27511 var head = document.head;
27512 var stylesheetId = '__________cytoscape_stylesheet';
27513 var className = '__________cytoscape_container';
27514 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
27515
27516 if (ctr.className.indexOf(className) < 0) {
27517 ctr.className = (ctr.className || '') + ' ' + className;
27518 }
27519
27520 if (!stylesheetAlreadyExists) {
27521 var stylesheet = document.createElement('style');
27522 stylesheet.id = stylesheetId;
27523 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
27524 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
27525 }
27526
27527 var computedStyle = window$1.getComputedStyle(ctr);
27528 var position = computedStyle.getPropertyValue('position');
27529
27530 if (position === 'static') {
27531 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
27532 }
27533 }
27534
27535 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
27536
27537 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
27538
27539 r.hoverData = {
27540 down: null,
27541 last: null,
27542 downTime: null,
27543 triggerMode: null,
27544 dragging: false,
27545 initialPan: [null, null],
27546 capture: false
27547 };
27548 r.dragData = {
27549 possibleDragElements: []
27550 };
27551 r.touchData = {
27552 start: null,
27553 capture: false,
27554 // These 3 fields related to tap, taphold events
27555 startPosition: [null, null, null, null, null, null],
27556 singleTouchStartTime: null,
27557 singleTouchMoved: true,
27558 now: [null, null, null, null, null, null],
27559 earlier: [null, null, null, null, null, null]
27560 };
27561 r.redraws = 0;
27562 r.showFps = options.showFps;
27563 r.debug = options.debug;
27564 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27565 r.textureOnViewport = options.textureOnViewport;
27566 r.wheelSensitivity = options.wheelSensitivity;
27567 r.motionBlurEnabled = options.motionBlur; // on by default
27568
27569 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
27570 r.motionBlur = options.motionBlur; // for initial kick off
27571
27572 r.motionBlurOpacity = options.motionBlurOpacity;
27573 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27574 r.motionBlurPxRatio = 1;
27575 r.mbPxRBlurry = 1; //0.8;
27576
27577 r.minMbLowQualFrames = 4;
27578 r.fullQualityMb = false;
27579 r.clearedForMotionBlur = [];
27580 r.desktopTapThreshold = options.desktopTapThreshold;
27581 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27582 r.touchTapThreshold = options.touchTapThreshold;
27583 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27584 r.tapholdDuration = 500;
27585 r.bindings = [];
27586 r.beforeRenderCallbacks = [];
27587 r.beforeRenderPriorities = {
27588 // higher priority execs before lower one
27589 animations: 400,
27590 eleCalcs: 300,
27591 eleTxrDeq: 200,
27592 lyrTxrDeq: 150,
27593 lyrTxrSkip: 100
27594 };
27595 r.registerNodeShapes();
27596 r.registerArrowShapes();
27597 r.registerCalculationListeners();
27598 };
27599
27600 BRp$f.notify = function (eventName, eles) {
27601 var r = this;
27602 var cy = r.cy; // the renderer can't be notified after it's destroyed
27603
27604 if (this.destroyed) {
27605 return;
27606 }
27607
27608 if (eventName === 'init') {
27609 r.load();
27610 return;
27611 }
27612
27613 if (eventName === 'destroy') {
27614 r.destroy();
27615 return;
27616 }
27617
27618 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27619 r.invalidateCachedZSortedEles();
27620 }
27621
27622 if (eventName === 'viewport') {
27623 r.redrawHint('select', true);
27624 }
27625
27626 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27627 r.invalidateContainerClientCoordsCache();
27628 r.matchCanvasSize(r.container);
27629 }
27630
27631 r.redrawHint('eles', true);
27632 r.redrawHint('drag', true);
27633 this.startRenderLoop();
27634 this.redraw();
27635 };
27636
27637 BRp$f.destroy = function () {
27638 var r = this;
27639 r.destroyed = true;
27640 r.cy.stopAnimationLoop();
27641
27642 for (var i = 0; i < r.bindings.length; i++) {
27643 var binding = r.bindings[i];
27644 var b = binding;
27645 var tgt = b.target;
27646 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27647 }
27648
27649 r.bindings = [];
27650 r.beforeRenderCallbacks = [];
27651 r.onUpdateEleCalcsFns = [];
27652
27653 if (r.removeObserver) {
27654 r.removeObserver.disconnect();
27655 }
27656
27657 if (r.styleObserver) {
27658 r.styleObserver.disconnect();
27659 }
27660
27661 if (r.resizeObserver) {
27662 r.resizeObserver.disconnect();
27663 }
27664
27665 if (r.labelCalcDiv) {
27666 try {
27667 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27668 } catch (e) {// ie10 issue #1014
27669 }
27670 }
27671 };
27672
27673 BRp$f.isHeadless = function () {
27674 return false;
27675 };
27676
27677 [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
27678 extend(BRp$f, props);
27679 });
27680
27681 var fullFpsTime = 1000 / 60; // assume 60 frames per second
27682
27683 var defs = {
27684 setupDequeueing: function setupDequeueing(opts) {
27685 return function setupDequeueingImpl() {
27686 var self = this;
27687 var r = this.renderer;
27688
27689 if (self.dequeueingSetup) {
27690 return;
27691 } else {
27692 self.dequeueingSetup = true;
27693 }
27694
27695 var queueRedraw = lodash_debounce(function () {
27696 r.redrawHint('eles', true);
27697 r.redrawHint('drag', true);
27698 r.redraw();
27699 }, opts.deqRedrawThreshold);
27700
27701 var dequeue = function dequeue(willDraw, frameStartTime) {
27702 var startTime = performanceNow();
27703 var avgRenderTime = r.averageRedrawTime;
27704 var renderTime = r.lastRedrawTime;
27705 var deqd = [];
27706 var extent = r.cy.extent();
27707 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27708 // queue won't automatically be flushed before dequeueing starts
27709
27710 if (!willDraw) {
27711 r.flushRenderedStyleQueue();
27712 }
27713
27714 while (true) {
27715 // eslint-disable-line no-constant-condition
27716 var now = performanceNow();
27717 var duration = now - startTime;
27718 var frameDuration = now - frameStartTime;
27719
27720 if (renderTime < fullFpsTime) {
27721 // if we're rendering faster than the ideal fps, then do dequeueing
27722 // during all of the remaining frame time
27723 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27724
27725 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27726 break;
27727 }
27728 } else {
27729 if (willDraw) {
27730 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27731 break;
27732 }
27733 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27734 break;
27735 }
27736 }
27737
27738 var thisDeqd = opts.deq(self, pixelRatio, extent);
27739
27740 if (thisDeqd.length > 0) {
27741 for (var i = 0; i < thisDeqd.length; i++) {
27742 deqd.push(thisDeqd[i]);
27743 }
27744 } else {
27745 break;
27746 }
27747 } // callbacks on dequeue
27748
27749
27750 if (deqd.length > 0) {
27751 opts.onDeqd(self, deqd);
27752
27753 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27754 queueRedraw();
27755 }
27756 }
27757 };
27758
27759 var priority = opts.priority || noop;
27760 r.beforeRender(dequeue, priority(self));
27761 };
27762 }
27763 };
27764
27765 // Uses keys so elements may share the same cache.
27766
27767 var ElementTextureCacheLookup =
27768 /*#__PURE__*/
27769 function () {
27770 function ElementTextureCacheLookup(getKey) {
27771 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27772
27773 _classCallCheck(this, ElementTextureCacheLookup);
27774
27775 this.idsByKey = new Map$1();
27776 this.keyForId = new Map$1();
27777 this.cachesByLvl = new Map$1();
27778 this.lvls = [];
27779 this.getKey = getKey;
27780 this.doesEleInvalidateKey = doesEleInvalidateKey;
27781 }
27782
27783 _createClass(ElementTextureCacheLookup, [{
27784 key: "getIdsFor",
27785 value: function getIdsFor(key) {
27786 if (key == null) {
27787 error("Can not get id list for null key");
27788 }
27789
27790 var idsByKey = this.idsByKey;
27791 var ids = this.idsByKey.get(key);
27792
27793 if (!ids) {
27794 ids = new Set$1();
27795 idsByKey.set(key, ids);
27796 }
27797
27798 return ids;
27799 }
27800 }, {
27801 key: "addIdForKey",
27802 value: function addIdForKey(key, id) {
27803 if (key != null) {
27804 this.getIdsFor(key).add(id);
27805 }
27806 }
27807 }, {
27808 key: "deleteIdForKey",
27809 value: function deleteIdForKey(key, id) {
27810 if (key != null) {
27811 this.getIdsFor(key)["delete"](id);
27812 }
27813 }
27814 }, {
27815 key: "getNumberOfIdsForKey",
27816 value: function getNumberOfIdsForKey(key) {
27817 if (key == null) {
27818 return 0;
27819 } else {
27820 return this.getIdsFor(key).size;
27821 }
27822 }
27823 }, {
27824 key: "updateKeyMappingFor",
27825 value: function updateKeyMappingFor(ele) {
27826 var id = ele.id();
27827 var prevKey = this.keyForId.get(id);
27828 var currKey = this.getKey(ele);
27829 this.deleteIdForKey(prevKey, id);
27830 this.addIdForKey(currKey, id);
27831 this.keyForId.set(id, currKey);
27832 }
27833 }, {
27834 key: "deleteKeyMappingFor",
27835 value: function deleteKeyMappingFor(ele) {
27836 var id = ele.id();
27837 var prevKey = this.keyForId.get(id);
27838 this.deleteIdForKey(prevKey, id);
27839 this.keyForId["delete"](id);
27840 }
27841 }, {
27842 key: "keyHasChangedFor",
27843 value: function keyHasChangedFor(ele) {
27844 var id = ele.id();
27845 var prevKey = this.keyForId.get(id);
27846 var newKey = this.getKey(ele);
27847 return prevKey !== newKey;
27848 }
27849 }, {
27850 key: "isInvalid",
27851 value: function isInvalid(ele) {
27852 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27853 }
27854 }, {
27855 key: "getCachesAt",
27856 value: function getCachesAt(lvl) {
27857 var cachesByLvl = this.cachesByLvl,
27858 lvls = this.lvls;
27859 var caches = cachesByLvl.get(lvl);
27860
27861 if (!caches) {
27862 caches = new Map$1();
27863 cachesByLvl.set(lvl, caches);
27864 lvls.push(lvl);
27865 }
27866
27867 return caches;
27868 }
27869 }, {
27870 key: "getCache",
27871 value: function getCache(key, lvl) {
27872 return this.getCachesAt(lvl).get(key);
27873 }
27874 }, {
27875 key: "get",
27876 value: function get(ele, lvl) {
27877 var key = this.getKey(ele);
27878 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27879
27880 if (cache != null) {
27881 this.updateKeyMappingFor(ele);
27882 }
27883
27884 return cache;
27885 }
27886 }, {
27887 key: "getForCachedKey",
27888 value: function getForCachedKey(ele, lvl) {
27889 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27890
27891 var cache = this.getCache(key, lvl);
27892 return cache;
27893 }
27894 }, {
27895 key: "hasCache",
27896 value: function hasCache(key, lvl) {
27897 return this.getCachesAt(lvl).has(key);
27898 }
27899 }, {
27900 key: "has",
27901 value: function has(ele, lvl) {
27902 var key = this.getKey(ele);
27903 return this.hasCache(key, lvl);
27904 }
27905 }, {
27906 key: "setCache",
27907 value: function setCache(key, lvl, cache) {
27908 cache.key = key;
27909 this.getCachesAt(lvl).set(key, cache);
27910 }
27911 }, {
27912 key: "set",
27913 value: function set(ele, lvl, cache) {
27914 var key = this.getKey(ele);
27915 this.setCache(key, lvl, cache);
27916 this.updateKeyMappingFor(ele);
27917 }
27918 }, {
27919 key: "deleteCache",
27920 value: function deleteCache(key, lvl) {
27921 this.getCachesAt(lvl)["delete"](key);
27922 }
27923 }, {
27924 key: "delete",
27925 value: function _delete(ele, lvl) {
27926 var key = this.getKey(ele);
27927 this.deleteCache(key, lvl);
27928 }
27929 }, {
27930 key: "invalidateKey",
27931 value: function invalidateKey(key) {
27932 var _this = this;
27933
27934 this.lvls.forEach(function (lvl) {
27935 return _this.deleteCache(key, lvl);
27936 });
27937 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27938
27939 }, {
27940 key: "invalidate",
27941 value: function invalidate(ele) {
27942 var id = ele.id();
27943 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27944
27945 this.deleteKeyMappingFor(ele);
27946 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27947
27948 if (entireKeyInvalidated) {
27949 // clear mapping for current key
27950 this.invalidateKey(key);
27951 }
27952
27953 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27954 }
27955 }]);
27956
27957 return ElementTextureCacheLookup;
27958 }();
27959
27960 var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27961
27962 var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27963
27964 var minLvl = -4; // when scaling smaller than that we don't need to re-render
27965
27966 var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27967
27968 var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27969
27970 var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27971
27972 var defTxrWidth = 1024; // default/minimum texture width
27973
27974 var maxTxrW = 1024; // the maximum width of a texture
27975
27976 var maxTxrH = 1024; // the maximum height of a texture
27977
27978 var minUtility = 0.2; // if usage of texture is less than this, it is retired
27979
27980 var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27981
27982 var maxFullnessChecks = 10; // dequeued after this many checks
27983
27984 var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27985
27986 var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27987
27988 var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27989
27990 var deqFastCost = 0.9; // % of frame time to be used when >60fps
27991
27992 var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27993
27994 var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27995
27996 var getTxrReasons = {
27997 dequeue: 'dequeue',
27998 downscale: 'downscale',
27999 highQuality: 'highQuality'
28000 };
28001 var initDefaults = defaults({
28002 getKey: null,
28003 doesEleInvalidateKey: falsify,
28004 drawElement: null,
28005 getBoundingBox: null,
28006 getRotationPoint: null,
28007 getRotationOffset: null,
28008 isVisible: trueify,
28009 allowEdgeTxrCaching: true,
28010 allowParentTxrCaching: true
28011 });
28012
28013 var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
28014 var self = this;
28015 self.renderer = renderer;
28016 self.onDequeues = [];
28017 var opts = initDefaults(initOptions);
28018 extend(self, opts);
28019 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
28020 self.setupDequeueing();
28021 };
28022
28023 var ETCp = ElementTextureCache.prototype;
28024 ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
28025
28026 ETCp.getTextureQueue = function (txrH) {
28027 var self = this;
28028 self.eleImgCaches = self.eleImgCaches || {};
28029 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
28030 }; // the list of usused textures which can be recycled (in use in texture queue)
28031
28032
28033 ETCp.getRetiredTextureQueue = function (txrH) {
28034 var self = this;
28035 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
28036 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
28037 return rtxtrQ;
28038 }; // queue of element draw requests at different scale levels
28039
28040
28041 ETCp.getElementQueue = function () {
28042 var self = this;
28043 var q = self.eleCacheQueue = self.eleCacheQueue || new heap$1(function (a, b) {
28044 return b.reqs - a.reqs;
28045 });
28046 return q;
28047 }; // queue of element draw requests at different scale levels (element id lookup)
28048
28049
28050 ETCp.getElementKeyToQueue = function () {
28051 var self = this;
28052 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
28053 return k2q;
28054 };
28055
28056 ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
28057 var self = this;
28058 var r = this.renderer;
28059 var zoom = r.cy.zoom();
28060 var lookup = this.lookup;
28061
28062 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
28063 return null;
28064 }
28065
28066 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
28067 return null;
28068 }
28069
28070 if (lvl == null) {
28071 lvl = Math.ceil(log2(zoom * pxRatio));
28072 }
28073
28074 if (lvl < minLvl) {
28075 lvl = minLvl;
28076 } else if (zoom >= maxZoom || lvl > maxLvl) {
28077 return null;
28078 }
28079
28080 var scale = Math.pow(2, lvl);
28081 var eleScaledH = bb.h * scale;
28082 var eleScaledW = bb.w * scale;
28083 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
28084
28085 if (!this.isVisible(ele, scaledLabelShown)) {
28086 return null;
28087 }
28088
28089 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
28090
28091 if (eleCache && eleCache.invalidated) {
28092 eleCache.invalidated = false;
28093 eleCache.texture.invalidatedWidth -= eleCache.width;
28094 }
28095
28096 if (eleCache) {
28097 return eleCache;
28098 }
28099
28100 var txrH; // which texture height this ele belongs to
28101
28102 if (eleScaledH <= minTxrH) {
28103 txrH = minTxrH;
28104 } else if (eleScaledH <= txrStepH) {
28105 txrH = txrStepH;
28106 } else {
28107 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
28108 }
28109
28110 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
28111 return null; // caching large elements is not efficient
28112 }
28113
28114 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
28115
28116 var txr = txrQ[txrQ.length - 2];
28117
28118 var addNewTxr = function addNewTxr() {
28119 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
28120 }; // try the last one if there is no second last one
28121
28122
28123 if (!txr) {
28124 txr = txrQ[txrQ.length - 1];
28125 } // if the last one doesn't exist, we need a first one
28126
28127
28128 if (!txr) {
28129 txr = addNewTxr();
28130 } // if there's no room in the current texture, we need a new one
28131
28132
28133 if (txr.width - txr.usedWidth < eleScaledW) {
28134 txr = addNewTxr();
28135 }
28136
28137 var scalableFrom = function scalableFrom(otherCache) {
28138 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
28139 };
28140
28141 var deqing = reason && reason === getTxrReasons.dequeue;
28142 var highQualityReq = reason && reason === getTxrReasons.highQuality;
28143 var downscaleReq = reason && reason === getTxrReasons.downscale;
28144 var higherCache; // the nearest cache with a higher level
28145
28146 for (var l = lvl + 1; l <= maxLvl; l++) {
28147 var c = lookup.get(ele, l);
28148
28149 if (c) {
28150 higherCache = c;
28151 break;
28152 }
28153 }
28154
28155 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
28156
28157 var downscale = function downscale() {
28158 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
28159 }; // reset ele area in texture
28160
28161
28162 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28163 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
28164
28165 if (scalableFrom(oneUpCache)) {
28166 // then we can relatively cheaply rescale the existing image w/o rerendering
28167 downscale();
28168 } else if (scalableFrom(higherCache)) {
28169 // then use the higher cache for now and queue the next level down
28170 // to cheaply scale towards the smaller level
28171 if (highQualityReq) {
28172 for (var _l = higherCache.level; _l > lvl; _l--) {
28173 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
28174 }
28175
28176 downscale();
28177 } else {
28178 self.queueElement(ele, higherCache.level - 1);
28179 return higherCache;
28180 }
28181 } else {
28182 var lowerCache; // the nearest cache with a lower level
28183
28184 if (!deqing && !highQualityReq && !downscaleReq) {
28185 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
28186 var _c = lookup.get(ele, _l2);
28187
28188 if (_c) {
28189 lowerCache = _c;
28190 break;
28191 }
28192 }
28193 }
28194
28195 if (scalableFrom(lowerCache)) {
28196 // then use the lower quality cache for now and queue the better one for later
28197 self.queueElement(ele, lvl);
28198 return lowerCache;
28199 }
28200
28201 txr.context.translate(txr.usedWidth, 0);
28202 txr.context.scale(scale, scale);
28203 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
28204 txr.context.scale(1 / scale, 1 / scale);
28205 txr.context.translate(-txr.usedWidth, 0);
28206 }
28207
28208 eleCache = {
28209 x: txr.usedWidth,
28210 texture: txr,
28211 level: lvl,
28212 scale: scale,
28213 width: eleScaledW,
28214 height: eleScaledH,
28215 scaledLabelShown: scaledLabelShown
28216 };
28217 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
28218 txr.eleCaches.push(eleCache);
28219 lookup.set(ele, lvl, eleCache);
28220 self.checkTextureFullness(txr);
28221 return eleCache;
28222 };
28223
28224 ETCp.invalidateElements = function (eles) {
28225 for (var i = 0; i < eles.length; i++) {
28226 this.invalidateElement(eles[i]);
28227 }
28228 };
28229
28230 ETCp.invalidateElement = function (ele) {
28231 var self = this;
28232 var lookup = self.lookup;
28233 var caches = [];
28234 var invalid = lookup.isInvalid(ele);
28235
28236 if (!invalid) {
28237 return; // override the invalidation request if the element key has not changed
28238 }
28239
28240 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
28241 var cache = lookup.getForCachedKey(ele, lvl);
28242
28243 if (cache) {
28244 caches.push(cache);
28245 }
28246 }
28247
28248 var noOtherElesUseCache = lookup.invalidate(ele);
28249
28250 if (noOtherElesUseCache) {
28251 for (var i = 0; i < caches.length; i++) {
28252 var _cache = caches[i];
28253 var txr = _cache.texture; // remove space from the texture it belongs to
28254
28255 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
28256
28257 _cache.invalidated = true; // retire the texture if its utility is low
28258
28259 self.checkTextureUtility(txr);
28260 }
28261 } // remove from queue since the old req was for the old state
28262
28263
28264 self.removeFromQueue(ele);
28265 };
28266
28267 ETCp.checkTextureUtility = function (txr) {
28268 // invalidate all entries in the cache if the cache size is small
28269 if (txr.invalidatedWidth >= minUtility * txr.width) {
28270 this.retireTexture(txr);
28271 }
28272 };
28273
28274 ETCp.checkTextureFullness = function (txr) {
28275 // if texture has been mostly filled and passed over several times, remove
28276 // it from the queue so we don't need to waste time looking at it to put new things
28277 var self = this;
28278 var txrQ = self.getTextureQueue(txr.height);
28279
28280 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
28281 removeFromArray(txrQ, txr);
28282 } else {
28283 txr.fullnessChecks++;
28284 }
28285 };
28286
28287 ETCp.retireTexture = function (txr) {
28288 var self = this;
28289 var txrH = txr.height;
28290 var txrQ = self.getTextureQueue(txrH);
28291 var lookup = this.lookup; // retire the texture from the active / searchable queue:
28292
28293 removeFromArray(txrQ, txr);
28294 txr.retired = true; // remove the refs from the eles to the caches:
28295
28296 var eleCaches = txr.eleCaches;
28297
28298 for (var i = 0; i < eleCaches.length; i++) {
28299 var eleCache = eleCaches[i];
28300 lookup.deleteCache(eleCache.key, eleCache.level);
28301 }
28302
28303 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
28304
28305 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28306 rtxtrQ.push(txr);
28307 };
28308
28309 ETCp.addTexture = function (txrH, minW) {
28310 var self = this;
28311 var txrQ = self.getTextureQueue(txrH);
28312 var txr = {};
28313 txrQ.push(txr);
28314 txr.eleCaches = [];
28315 txr.height = txrH;
28316 txr.width = Math.max(defTxrWidth, minW);
28317 txr.usedWidth = 0;
28318 txr.invalidatedWidth = 0;
28319 txr.fullnessChecks = 0;
28320 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
28321 txr.context = txr.canvas.getContext('2d');
28322 return txr;
28323 };
28324
28325 ETCp.recycleTexture = function (txrH, minW) {
28326 var self = this;
28327 var txrQ = self.getTextureQueue(txrH);
28328 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28329
28330 for (var i = 0; i < rtxtrQ.length; i++) {
28331 var txr = rtxtrQ[i];
28332
28333 if (txr.width >= minW) {
28334 txr.retired = false;
28335 txr.usedWidth = 0;
28336 txr.invalidatedWidth = 0;
28337 txr.fullnessChecks = 0;
28338 clearArray(txr.eleCaches);
28339 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28340 txr.context.clearRect(0, 0, txr.width, txr.height);
28341 removeFromArray(rtxtrQ, txr);
28342 txrQ.push(txr);
28343 return txr;
28344 }
28345 }
28346 };
28347
28348 ETCp.queueElement = function (ele, lvl) {
28349 var self = this;
28350 var q = self.getElementQueue();
28351 var k2q = self.getElementKeyToQueue();
28352 var key = this.getKey(ele);
28353 var existingReq = k2q[key];
28354
28355 if (existingReq) {
28356 // use the max lvl b/c in between lvls are cheap to make
28357 existingReq.level = Math.max(existingReq.level, lvl);
28358 existingReq.eles.merge(ele);
28359 existingReq.reqs++;
28360 q.updateItem(existingReq);
28361 } else {
28362 var req = {
28363 eles: ele.spawn().merge(ele),
28364 level: lvl,
28365 reqs: 1,
28366 key: key
28367 };
28368 q.push(req);
28369 k2q[key] = req;
28370 }
28371 };
28372
28373 ETCp.dequeue = function (pxRatio
28374 /*, extent*/
28375 ) {
28376 var self = this;
28377 var q = self.getElementQueue();
28378 var k2q = self.getElementKeyToQueue();
28379 var dequeued = [];
28380 var lookup = self.lookup;
28381
28382 for (var i = 0; i < maxDeqSize; i++) {
28383 if (q.size() > 0) {
28384 var req = q.pop();
28385 var key = req.key;
28386 var ele = req.eles[0]; // all eles have the same key
28387
28388 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
28389
28390 k2q[key] = null; // dequeueing isn't necessary with an existing cache
28391
28392 if (cacheExists) {
28393 continue;
28394 }
28395
28396 dequeued.push(req);
28397 var bb = self.getBoundingBox(ele);
28398 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
28399 } else {
28400 break;
28401 }
28402 }
28403
28404 return dequeued;
28405 };
28406
28407 ETCp.removeFromQueue = function (ele) {
28408 var self = this;
28409 var q = self.getElementQueue();
28410 var k2q = self.getElementKeyToQueue();
28411 var key = this.getKey(ele);
28412 var req = k2q[key];
28413
28414 if (req != null) {
28415 if (req.eles.length === 1) {
28416 // remove if last ele in the req
28417 // bring to front of queue
28418 req.reqs = MAX_INT;
28419 q.updateItem(req);
28420 q.pop(); // remove from queue
28421
28422 k2q[key] = null; // remove from lookup map
28423 } else {
28424 // otherwise just remove ele from req
28425 req.eles.unmerge(ele);
28426 }
28427 }
28428 };
28429
28430 ETCp.onDequeue = function (fn) {
28431 this.onDequeues.push(fn);
28432 };
28433
28434 ETCp.offDequeue = function (fn) {
28435 removeFromArray(this.onDequeues, fn);
28436 };
28437
28438 ETCp.setupDequeueing = defs.setupDequeueing({
28439 deqRedrawThreshold: deqRedrawThreshold,
28440 deqCost: deqCost,
28441 deqAvgCost: deqAvgCost,
28442 deqNoDrawCost: deqNoDrawCost,
28443 deqFastCost: deqFastCost,
28444 deq: function deq(self, pxRatio, extent) {
28445 return self.dequeue(pxRatio, extent);
28446 },
28447 onDeqd: function onDeqd(self, deqd) {
28448 for (var i = 0; i < self.onDequeues.length; i++) {
28449 var fn = self.onDequeues[i];
28450 fn(deqd);
28451 }
28452 },
28453 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
28454 for (var i = 0; i < deqd.length; i++) {
28455 var eles = deqd[i].eles;
28456
28457 for (var j = 0; j < eles.length; j++) {
28458 var bb = eles[j].boundingBox();
28459
28460 if (boundingBoxesIntersect(bb, extent)) {
28461 return true;
28462 }
28463 }
28464 }
28465
28466 return false;
28467 },
28468 priority: function priority(self) {
28469 return self.renderer.beforeRenderPriorities.eleTxrDeq;
28470 }
28471 });
28472
28473 var defNumLayers = 1; // default number of layers to use
28474
28475 var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
28476
28477 var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
28478
28479 var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
28480
28481 var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
28482
28483 var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
28484
28485 var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
28486
28487 var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
28488
28489 var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
28490
28491 var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
28492
28493 var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
28494
28495 var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
28496
28497 var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
28498
28499 var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
28500 // var log = function(){ console.log.apply( console, arguments ); };
28501
28502 var LayeredTextureCache = function LayeredTextureCache(renderer) {
28503 var self = this;
28504 var r = self.renderer = renderer;
28505 var cy = r.cy;
28506 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
28507
28508 self.firstGet = true;
28509 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
28510 self.skipping = false;
28511 self.eleTxrDeqs = cy.collection();
28512 self.scheduleElementRefinement = lodash_debounce(function () {
28513 self.refineElementTextures(self.eleTxrDeqs);
28514 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
28515 }, refineEleDebounceTime);
28516 r.beforeRender(function (willDraw, now) {
28517 if (now - self.lastInvalidationTime <= invalidThreshold) {
28518 self.skipping = true;
28519 } else {
28520 self.skipping = false;
28521 }
28522 }, r.beforeRenderPriorities.lyrTxrSkip);
28523
28524 var qSort = function qSort(a, b) {
28525 return b.reqs - a.reqs;
28526 };
28527
28528 self.layersQueue = new heap$1(qSort);
28529 self.setupDequeueing();
28530 };
28531
28532 var LTCp = LayeredTextureCache.prototype;
28533 var layerIdPool = 0;
28534 var MAX_INT$1 = Math.pow(2, 53) - 1;
28535
28536 LTCp.makeLayer = function (bb, lvl) {
28537 var scale = Math.pow(2, lvl);
28538 var w = Math.ceil(bb.w * scale);
28539 var h = Math.ceil(bb.h * scale);
28540 var canvas = this.renderer.makeOffscreenCanvas(w, h);
28541 var layer = {
28542 id: layerIdPool = ++layerIdPool % MAX_INT$1,
28543 bb: bb,
28544 level: lvl,
28545 width: w,
28546 height: h,
28547 canvas: canvas,
28548 context: canvas.getContext('2d'),
28549 eles: [],
28550 elesQueue: [],
28551 reqs: 0
28552 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
28553
28554 var cxt = layer.context;
28555 var dx = -layer.bb.x1;
28556 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
28557
28558 cxt.scale(scale, scale);
28559 cxt.translate(dx, dy);
28560 return layer;
28561 };
28562
28563 LTCp.getLayers = function (eles, pxRatio, lvl) {
28564 var self = this;
28565 var r = self.renderer;
28566 var cy = r.cy;
28567 var zoom = cy.zoom();
28568 var firstGet = self.firstGet;
28569 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28570 //log eles.map(function(ele){ return ele.id() }) );
28571
28572 if (lvl == null) {
28573 lvl = Math.ceil(log2(zoom * pxRatio));
28574
28575 if (lvl < minLvl$1) {
28576 lvl = minLvl$1;
28577 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
28578 return null;
28579 }
28580 }
28581
28582 self.validateLayersElesOrdering(lvl, eles);
28583 var layersByLvl = self.layersByLevel;
28584 var scale = Math.pow(2, lvl);
28585 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28586 var bb;
28587 var lvlComplete = self.levelIsComplete(lvl, eles);
28588 var tmpLayers;
28589
28590 var checkTempLevels = function checkTempLevels() {
28591 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28592 self.validateLayersElesOrdering(l, eles);
28593
28594 if (self.levelIsComplete(l, eles)) {
28595 tmpLayers = layersByLvl[l];
28596 return true;
28597 }
28598 };
28599
28600 var checkLvls = function checkLvls(dir) {
28601 if (tmpLayers) {
28602 return;
28603 }
28604
28605 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
28606 if (canUseAsTmpLvl(l)) {
28607 break;
28608 }
28609 }
28610 };
28611
28612 checkLvls(+1);
28613 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28614
28615 for (var i = layers.length - 1; i >= 0; i--) {
28616 var layer = layers[i];
28617
28618 if (layer.invalid) {
28619 removeFromArray(layers, layer);
28620 }
28621 }
28622 };
28623
28624 if (!lvlComplete) {
28625 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28626 // and later queue the current layerset so we can get the proper quality level soon
28627 checkTempLevels();
28628 } else {
28629 // log('level complete, using existing layers\n--');
28630 return layers;
28631 }
28632
28633 var getBb = function getBb() {
28634 if (!bb) {
28635 bb = makeBoundingBox();
28636
28637 for (var i = 0; i < eles.length; i++) {
28638 updateBoundingBox(bb, eles[i].boundingBox());
28639 }
28640 }
28641
28642 return bb;
28643 };
28644
28645 var makeLayer = function makeLayer(opts) {
28646 opts = opts || {};
28647 var after = opts.after;
28648 getBb();
28649 var area = bb.w * scale * (bb.h * scale);
28650
28651 if (area > maxLayerArea) {
28652 return null;
28653 }
28654
28655 var layer = self.makeLayer(bb, lvl);
28656
28657 if (after != null) {
28658 var index = layers.indexOf(after) + 1;
28659 layers.splice(index, 0, layer);
28660 } else if (opts.insert === undefined || opts.insert) {
28661 // no after specified => first layer made so put at start
28662 layers.unshift(layer);
28663 } // if( tmpLayers ){
28664 //self.queueLayer( layer );
28665 // }
28666
28667
28668 return layer;
28669 };
28670
28671 if (self.skipping && !firstGet) {
28672 // log('skip layers');
28673 return null;
28674 } // log('do layers');
28675
28676
28677 var layer = null;
28678 var maxElesPerLayer = eles.length / defNumLayers;
28679 var allowLazyQueueing = !firstGet;
28680
28681 for (var i = 0; i < eles.length; i++) {
28682 var ele = eles[i];
28683 var rs = ele._private.rscratch;
28684 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28685
28686 var existingLayer = caches[lvl];
28687
28688 if (existingLayer) {
28689 // reuse layer for later eles
28690 // log('reuse layer for', ele.id());
28691 layer = existingLayer;
28692 continue;
28693 }
28694
28695 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28696 // log('make new layer for ele %s', ele.id());
28697 layer = makeLayer({
28698 insert: true,
28699 after: layer
28700 }); // if now layer can be built then we can't use layers at this level
28701
28702 if (!layer) {
28703 return null;
28704 } // log('new layer with id %s', layer.id);
28705
28706 }
28707
28708 if (tmpLayers || allowLazyQueueing) {
28709 // log('queue ele %s in layer %s', ele.id(), layer.id);
28710 self.queueLayer(layer, ele);
28711 } else {
28712 // log('draw ele %s in layer %s', ele.id(), layer.id);
28713 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28714 }
28715
28716 layer.eles.push(ele);
28717 caches[lvl] = layer;
28718 } // log('--');
28719
28720
28721 if (tmpLayers) {
28722 // then we only queued the current layerset and can't draw it yet
28723 return tmpLayers;
28724 }
28725
28726 if (allowLazyQueueing) {
28727 // log('lazy queue level', lvl);
28728 return null;
28729 }
28730
28731 return layers;
28732 }; // a layer may want to use an ele cache of a higher level to avoid blurriness
28733 // so the layer level might not equal the ele level
28734
28735
28736 LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28737 return lvl;
28738 };
28739
28740 LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28741 var self = this;
28742 var r = this.renderer;
28743 var context = layer.context;
28744 var bb = ele.boundingBox();
28745
28746 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28747 return;
28748 }
28749
28750 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28751
28752 {
28753 r.setImgSmoothing(context, false);
28754 }
28755
28756 {
28757 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28758 }
28759
28760 {
28761 r.setImgSmoothing(context, true);
28762 }
28763 };
28764
28765 LTCp.levelIsComplete = function (lvl, eles) {
28766 var self = this;
28767 var layers = self.layersByLevel[lvl];
28768
28769 if (!layers || layers.length === 0) {
28770 return false;
28771 }
28772
28773 var numElesInLayers = 0;
28774
28775 for (var i = 0; i < layers.length; i++) {
28776 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28777
28778 if (layer.reqs > 0) {
28779 return false;
28780 } // if the layer is invalid, the level is not complete
28781
28782
28783 if (layer.invalid) {
28784 return false;
28785 }
28786
28787 numElesInLayers += layer.eles.length;
28788 } // we should have exactly the number of eles passed in to be complete
28789
28790
28791 if (numElesInLayers !== eles.length) {
28792 return false;
28793 }
28794
28795 return true;
28796 };
28797
28798 LTCp.validateLayersElesOrdering = function (lvl, eles) {
28799 var layers = this.layersByLevel[lvl];
28800
28801 if (!layers) {
28802 return;
28803 } // if in a layer the eles are not in the same order, then the layer is invalid
28804 // (i.e. there is an ele in between the eles in the layer)
28805
28806
28807 for (var i = 0; i < layers.length; i++) {
28808 var layer = layers[i];
28809 var offset = -1; // find the offset
28810
28811 for (var j = 0; j < eles.length; j++) {
28812 if (layer.eles[0] === eles[j]) {
28813 offset = j;
28814 break;
28815 }
28816 }
28817
28818 if (offset < 0) {
28819 // then the layer has nonexistant elements and is invalid
28820 this.invalidateLayer(layer);
28821 continue;
28822 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28823
28824
28825 var o = offset;
28826
28827 for (var j = 0; j < layer.eles.length; j++) {
28828 if (layer.eles[j] !== eles[o + j]) {
28829 // log('invalidate based on ordering', layer.id);
28830 this.invalidateLayer(layer);
28831 break;
28832 }
28833 }
28834 }
28835 };
28836
28837 LTCp.updateElementsInLayers = function (eles, update) {
28838 var self = this;
28839 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28840 // layer itself along the way
28841
28842 for (var i = 0; i < eles.length; i++) {
28843 var req = isEles ? null : eles[i];
28844 var ele = isEles ? eles[i] : eles[i].ele;
28845 var rs = ele._private.rscratch;
28846 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28847
28848 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28849 var layer = caches[l];
28850
28851 if (!layer) {
28852 continue;
28853 } // if update is a request from the ele cache, then it affects only
28854 // the matching level
28855
28856
28857 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28858 continue;
28859 }
28860
28861 update(layer, ele, req);
28862 }
28863 }
28864 };
28865
28866 LTCp.haveLayers = function () {
28867 var self = this;
28868 var haveLayers = false;
28869
28870 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28871 var layers = self.layersByLevel[l];
28872
28873 if (layers && layers.length > 0) {
28874 haveLayers = true;
28875 break;
28876 }
28877 }
28878
28879 return haveLayers;
28880 };
28881
28882 LTCp.invalidateElements = function (eles) {
28883 var self = this;
28884
28885 if (eles.length === 0) {
28886 return;
28887 }
28888
28889 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28890
28891 if (eles.length === 0 || !self.haveLayers()) {
28892 return;
28893 }
28894
28895 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28896 self.invalidateLayer(layer);
28897 });
28898 };
28899
28900 LTCp.invalidateLayer = function (layer) {
28901 // log('update invalidate layer time');
28902 this.lastInvalidationTime = performanceNow();
28903
28904 if (layer.invalid) {
28905 return;
28906 } // save cycles
28907
28908
28909 var lvl = layer.level;
28910 var eles = layer.eles;
28911 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28912
28913 removeFromArray(layers, layer); // layer.eles = [];
28914
28915 layer.elesQueue = [];
28916 layer.invalid = true;
28917
28918 if (layer.replacement) {
28919 layer.replacement.invalid = true;
28920 }
28921
28922 for (var i = 0; i < eles.length; i++) {
28923 var caches = eles[i]._private.rscratch.imgLayerCaches;
28924
28925 if (caches) {
28926 caches[lvl] = null;
28927 }
28928 }
28929 };
28930
28931 LTCp.refineElementTextures = function (eles) {
28932 var self = this; // log('refine', eles.length);
28933
28934 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28935 var rLyr = layer.replacement;
28936
28937 if (!rLyr) {
28938 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28939 rLyr.replaces = layer;
28940 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28941 }
28942
28943 if (!rLyr.reqs) {
28944 for (var i = 0; i < rLyr.eles.length; i++) {
28945 self.queueLayer(rLyr, rLyr.eles[i]);
28946 } // log('queue replacement layer refinement', rLyr.id);
28947
28948 }
28949 });
28950 };
28951
28952 LTCp.enqueueElementRefinement = function (ele) {
28953
28954 this.eleTxrDeqs.merge(ele);
28955 this.scheduleElementRefinement();
28956 };
28957
28958 LTCp.queueLayer = function (layer, ele) {
28959 var self = this;
28960 var q = self.layersQueue;
28961 var elesQ = layer.elesQueue;
28962 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28963
28964 if (layer.replacement) {
28965 return;
28966 }
28967
28968 if (ele) {
28969 if (hasId[ele.id()]) {
28970 return;
28971 }
28972
28973 elesQ.push(ele);
28974 hasId[ele.id()] = true;
28975 }
28976
28977 if (layer.reqs) {
28978 layer.reqs++;
28979 q.updateItem(layer);
28980 } else {
28981 layer.reqs = 1;
28982 q.push(layer);
28983 }
28984 };
28985
28986 LTCp.dequeue = function (pxRatio) {
28987 var self = this;
28988 var q = self.layersQueue;
28989 var deqd = [];
28990 var eleDeqs = 0;
28991
28992 while (eleDeqs < maxDeqSize$1) {
28993 if (q.size() === 0) {
28994 break;
28995 }
28996
28997 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28998
28999 if (layer.replacement) {
29000 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
29001 q.pop();
29002 continue;
29003 } // if this is a replacement layer that has been superceded, then forget it
29004
29005
29006 if (layer.replaces && layer !== layer.replaces.replacement) {
29007 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
29008 q.pop();
29009 continue;
29010 }
29011
29012 if (layer.invalid) {
29013 // log('replacement layer %s is invalid; dequeued', layer.id);
29014 q.pop();
29015 continue;
29016 }
29017
29018 var ele = layer.elesQueue.shift();
29019
29020 if (ele) {
29021 // log('dequeue layer %s', layer.id);
29022 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
29023 eleDeqs++;
29024 }
29025
29026 if (deqd.length === 0) {
29027 // we need only one entry in deqd to queue redrawing etc
29028 deqd.push(true);
29029 } // if the layer has all its eles done, then remove from the queue
29030
29031
29032 if (layer.elesQueue.length === 0) {
29033 q.pop();
29034 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
29035 // when a replacement layer is dequeued, it replaces the old layer in the level
29036
29037 if (layer.replaces) {
29038 self.applyLayerReplacement(layer);
29039 }
29040
29041 self.requestRedraw();
29042 }
29043 }
29044
29045 return deqd;
29046 };
29047
29048 LTCp.applyLayerReplacement = function (layer) {
29049 var self = this;
29050 var layersInLevel = self.layersByLevel[layer.level];
29051 var replaced = layer.replaces;
29052 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
29053 // refs would be a mistake (i.e. overwriting the true active layer)
29054
29055 if (index < 0 || replaced.invalid) {
29056 // log('replacement layer would have no effect', layer.id);
29057 return;
29058 }
29059
29060 layersInLevel[index] = layer; // replace level ref
29061 // replace refs in eles
29062
29063 for (var i = 0; i < layer.eles.length; i++) {
29064 var _p = layer.eles[i]._private;
29065 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
29066
29067 if (cache) {
29068 cache[layer.level] = layer;
29069 }
29070 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
29071
29072
29073 self.requestRedraw();
29074 };
29075
29076 LTCp.requestRedraw = lodash_debounce(function () {
29077 var r = this.renderer;
29078 r.redrawHint('eles', true);
29079 r.redrawHint('drag', true);
29080 r.redraw();
29081 }, 100);
29082 LTCp.setupDequeueing = defs.setupDequeueing({
29083 deqRedrawThreshold: deqRedrawThreshold$1,
29084 deqCost: deqCost$1,
29085 deqAvgCost: deqAvgCost$1,
29086 deqNoDrawCost: deqNoDrawCost$1,
29087 deqFastCost: deqFastCost$1,
29088 deq: function deq(self, pxRatio) {
29089 return self.dequeue(pxRatio);
29090 },
29091 onDeqd: noop,
29092 shouldRedraw: trueify,
29093 priority: function priority(self) {
29094 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
29095 }
29096 });
29097
29098 var CRp = {};
29099 var impl;
29100
29101 function polygon(context, points) {
29102 for (var i = 0; i < points.length; i++) {
29103 var pt = points[i];
29104 context.lineTo(pt.x, pt.y);
29105 }
29106 }
29107
29108 function triangleBackcurve(context, points, controlPoint) {
29109 var firstPt;
29110
29111 for (var i = 0; i < points.length; i++) {
29112 var pt = points[i];
29113
29114 if (i === 0) {
29115 firstPt = pt;
29116 }
29117
29118 context.lineTo(pt.x, pt.y);
29119 }
29120
29121 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
29122 }
29123
29124 function triangleTee(context, trianglePoints, teePoints) {
29125 if (context.beginPath) {
29126 context.beginPath();
29127 }
29128
29129 var triPts = trianglePoints;
29130
29131 for (var i = 0; i < triPts.length; i++) {
29132 var pt = triPts[i];
29133 context.lineTo(pt.x, pt.y);
29134 }
29135
29136 var teePts = teePoints;
29137 var firstTeePt = teePoints[0];
29138 context.moveTo(firstTeePt.x, firstTeePt.y);
29139
29140 for (var i = 1; i < teePts.length; i++) {
29141 var pt = teePts[i];
29142 context.lineTo(pt.x, pt.y);
29143 }
29144
29145 if (context.closePath) {
29146 context.closePath();
29147 }
29148 }
29149
29150 function circleTriangle(context, trianglePoints, rx, ry, r) {
29151 if (context.beginPath) {
29152 context.beginPath();
29153 }
29154
29155 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29156 var triPts = trianglePoints;
29157 var firstTrPt = triPts[0];
29158 context.moveTo(firstTrPt.x, firstTrPt.y);
29159
29160 for (var i = 0; i < triPts.length; i++) {
29161 var pt = triPts[i];
29162 context.lineTo(pt.x, pt.y);
29163 }
29164
29165 if (context.closePath) {
29166 context.closePath();
29167 }
29168 }
29169
29170 function circle(context, rx, ry, r) {
29171 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29172 }
29173
29174 CRp.arrowShapeImpl = function (name) {
29175 return (impl || (impl = {
29176 'polygon': polygon,
29177 'triangle-backcurve': triangleBackcurve,
29178 'triangle-tee': triangleTee,
29179 'circle-triangle': circleTriangle,
29180 'triangle-cross': triangleTee,
29181 'circle': circle
29182 }))[name];
29183 };
29184
29185 var CRp$1 = {};
29186
29187 CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
29188 var r = this;
29189
29190 if (ele.isNode()) {
29191 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29192 } else {
29193 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29194 }
29195 };
29196
29197 CRp$1.drawElementOverlay = function (context, ele) {
29198 var r = this;
29199
29200 if (ele.isNode()) {
29201 r.drawNodeOverlay(context, ele);
29202 } else {
29203 r.drawEdgeOverlay(context, ele);
29204 }
29205 };
29206
29207 CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
29208 var r = this;
29209 var bb = eleTxrCache.getBoundingBox(ele);
29210
29211 if (bb.w === 0 || bb.h === 0) {
29212 return;
29213 } // ignore zero size case
29214
29215
29216 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
29217
29218 if (eleCache != null) {
29219 var opacity = getOpacity(r, ele);
29220
29221 if (opacity === 0) {
29222 return;
29223 }
29224
29225 var theta = getRotation(r, ele);
29226 var x1 = bb.x1,
29227 y1 = bb.y1,
29228 w = bb.w,
29229 h = bb.h;
29230 var x, y, sx, sy, smooth;
29231
29232 if (theta !== 0) {
29233 var rotPt = eleTxrCache.getRotationPoint(ele);
29234 sx = rotPt.x;
29235 sy = rotPt.y;
29236 context.translate(sx, sy);
29237 context.rotate(theta);
29238 smooth = r.getImgSmoothing(context);
29239
29240 if (!smooth) {
29241 r.setImgSmoothing(context, true);
29242 }
29243
29244 var off = eleTxrCache.getRotationOffset(ele);
29245 x = off.x;
29246 y = off.y;
29247 } else {
29248 x = x1;
29249 y = y1;
29250 }
29251
29252 var oldGlobalAlpha;
29253
29254 if (opacity !== 1) {
29255 oldGlobalAlpha = context.globalAlpha;
29256 context.globalAlpha = oldGlobalAlpha * opacity;
29257 }
29258
29259 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
29260
29261 if (opacity !== 1) {
29262 context.globalAlpha = oldGlobalAlpha;
29263 }
29264
29265 if (theta !== 0) {
29266 context.rotate(-theta);
29267 context.translate(-sx, -sy);
29268
29269 if (!smooth) {
29270 r.setImgSmoothing(context, false);
29271 }
29272 }
29273 } else {
29274 eleTxrCache.drawElement(context, ele); // direct draw fallback
29275 }
29276 };
29277
29278 var getZeroRotation = function getZeroRotation() {
29279 return 0;
29280 };
29281
29282 var getLabelRotation = function getLabelRotation(r, ele) {
29283 return r.getTextAngle(ele, null);
29284 };
29285
29286 var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
29287 return r.getTextAngle(ele, 'source');
29288 };
29289
29290 var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
29291 return r.getTextAngle(ele, 'target');
29292 };
29293
29294 var getOpacity = function getOpacity(r, ele) {
29295 return ele.effectiveOpacity();
29296 };
29297
29298 var getTextOpacity = function getTextOpacity(e, ele) {
29299 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
29300 };
29301
29302 CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
29303 var r = this;
29304 var _r$data = r.data,
29305 eleTxrCache = _r$data.eleTxrCache,
29306 lblTxrCache = _r$data.lblTxrCache,
29307 slbTxrCache = _r$data.slbTxrCache,
29308 tlbTxrCache = _r$data.tlbTxrCache;
29309 var bb = ele.boundingBox();
29310 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
29311
29312 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
29313 return;
29314 }
29315
29316 if (!extent || boundingBoxesIntersect(bb, extent)) {
29317 var isEdge = ele.isEdge();
29318
29319 var badLine = ele.element()._private.rscratch.badLine;
29320
29321 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
29322
29323 if (!isEdge || !badLine) {
29324 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
29325 }
29326
29327 if (isEdge && !badLine) {
29328 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
29329 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
29330 }
29331
29332 r.drawElementOverlay(context, ele);
29333 }
29334 };
29335
29336 CRp$1.drawElements = function (context, eles) {
29337 var r = this;
29338
29339 for (var i = 0; i < eles.length; i++) {
29340 var ele = eles[i];
29341 r.drawElement(context, ele);
29342 }
29343 };
29344
29345 CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
29346 var r = this;
29347
29348 for (var i = 0; i < eles.length; i++) {
29349 var ele = eles[i];
29350 r.drawCachedElement(context, ele, pxRatio, extent);
29351 }
29352 };
29353
29354 CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
29355 var r = this;
29356
29357 for (var i = 0; i < eles.length; i++) {
29358 var ele = eles[i];
29359
29360 if (!ele.isNode()) {
29361 continue;
29362 }
29363
29364 r.drawCachedElement(context, ele, pxRatio, extent);
29365 }
29366 };
29367
29368 CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
29369 var r = this;
29370 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
29371
29372 if (layers) {
29373 for (var i = 0; i < layers.length; i++) {
29374 var layer = layers[i];
29375 var bb = layer.bb;
29376
29377 if (bb.w === 0 || bb.h === 0) {
29378 continue;
29379 }
29380
29381 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
29382 }
29383 } else {
29384 // fall back on plain caching if no layers
29385 r.drawCachedElements(context, eles, pxRatio, extent);
29386 }
29387 };
29388
29389 /* global Path2D */
29390 var CRp$2 = {};
29391
29392 CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
29393 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29394 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29395 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29396 var r = this;
29397 var rs = edge._private.rscratch;
29398
29399 if (shouldDrawOpacity && !edge.visible()) {
29400 return;
29401 } // if bezier ctrl pts can not be calculated, then die
29402
29403
29404 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
29405 // isNaN in case edge is impossible and browser bugs (e.g. safari)
29406 return;
29407 }
29408
29409 var bb;
29410
29411 if (shiftToOriginWithBb) {
29412 bb = shiftToOriginWithBb;
29413 context.translate(-bb.x1, -bb.y1);
29414 }
29415
29416 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
29417 var lineStyle = edge.pstyle('line-style').value;
29418 var edgeWidth = edge.pstyle('width').pfValue;
29419 var lineCap = edge.pstyle('line-cap').value;
29420
29421 var drawLine = function drawLine() {
29422 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
29423 context.lineWidth = edgeWidth;
29424 context.lineCap = lineCap;
29425 r.eleStrokeStyle(context, edge, strokeOpacity);
29426 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
29427 context.lineCap = 'butt'; // reset for other drawing functions
29428 };
29429
29430 var drawOverlay = function drawOverlay() {
29431 if (!shouldDrawOverlay) {
29432 return;
29433 }
29434
29435 r.drawEdgeOverlay(context, edge);
29436 };
29437
29438 var drawArrows = function drawArrows() {
29439 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
29440 r.drawArrowheads(context, edge, arrowOpacity);
29441 };
29442
29443 var drawText = function drawText() {
29444 r.drawElementText(context, edge, null, drawLabel);
29445 };
29446
29447 context.lineJoin = 'round';
29448 var ghost = edge.pstyle('ghost').value === 'yes';
29449
29450 if (ghost) {
29451 var gx = edge.pstyle('ghost-offset-x').pfValue;
29452 var gy = edge.pstyle('ghost-offset-y').pfValue;
29453 var ghostOpacity = edge.pstyle('ghost-opacity').value;
29454 var effectiveGhostOpacity = opacity * ghostOpacity;
29455 context.translate(gx, gy);
29456 drawLine(effectiveGhostOpacity);
29457 drawArrows(effectiveGhostOpacity);
29458 context.translate(-gx, -gy);
29459 }
29460
29461 drawLine();
29462 drawArrows();
29463 drawOverlay();
29464 drawText();
29465
29466 if (shiftToOriginWithBb) {
29467 context.translate(bb.x1, bb.y1);
29468 }
29469 };
29470
29471 CRp$2.drawEdgeOverlay = function (context, edge) {
29472 if (!edge.visible()) {
29473 return;
29474 }
29475
29476 var overlayOpacity = edge.pstyle('overlay-opacity').value;
29477
29478 if (overlayOpacity === 0) {
29479 return;
29480 }
29481
29482 var r = this;
29483 var usePaths = r.usePaths();
29484 var rs = edge._private.rscratch;
29485 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
29486 var overlayWidth = 2 * overlayPadding;
29487 var overlayColor = edge.pstyle('overlay-color').value;
29488 context.lineWidth = overlayWidth;
29489
29490 if (rs.edgeType === 'self' && !usePaths) {
29491 context.lineCap = 'butt';
29492 } else {
29493 context.lineCap = 'round';
29494 }
29495
29496 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29497 r.drawEdgePath(edge, context, rs.allpts, 'solid');
29498 };
29499
29500 CRp$2.drawEdgePath = function (edge, context, pts, type) {
29501 var rs = edge._private.rscratch;
29502 var canvasCxt = context;
29503 var path;
29504 var pathCacheHit = false;
29505 var usePaths = this.usePaths();
29506 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
29507 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
29508
29509 if (usePaths) {
29510 var pathCacheKey = pts.join('$');
29511 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
29512
29513 if (keyMatches) {
29514 path = context = rs.pathCache;
29515 pathCacheHit = true;
29516 } else {
29517 path = context = new Path2D();
29518 rs.pathCacheKey = pathCacheKey;
29519 rs.pathCache = path;
29520 }
29521 }
29522
29523 if (canvasCxt.setLineDash) {
29524 // for very outofdate browsers
29525 switch (type) {
29526 case 'dotted':
29527 canvasCxt.setLineDash([1, 1]);
29528 break;
29529
29530 case 'dashed':
29531 canvasCxt.setLineDash(lineDashPattern);
29532 canvasCxt.lineDashOffset = lineDashOffset;
29533 break;
29534
29535 case 'solid':
29536 canvasCxt.setLineDash([]);
29537 break;
29538 }
29539 }
29540
29541 if (!pathCacheHit && !rs.badLine) {
29542 if (context.beginPath) {
29543 context.beginPath();
29544 }
29545
29546 context.moveTo(pts[0], pts[1]);
29547
29548 switch (rs.edgeType) {
29549 case 'bezier':
29550 case 'self':
29551 case 'compound':
29552 case 'multibezier':
29553 for (var i = 2; i + 3 < pts.length; i += 4) {
29554 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29555 }
29556
29557 break;
29558
29559 case 'straight':
29560 case 'segments':
29561 case 'haystack':
29562 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29563 context.lineTo(pts[_i], pts[_i + 1]);
29564 }
29565
29566 break;
29567 }
29568 }
29569
29570 context = canvasCxt;
29571
29572 if (usePaths) {
29573 context.stroke(path);
29574 } else {
29575 context.stroke();
29576 } // reset any line dashes
29577
29578
29579 if (context.setLineDash) {
29580 // for very outofdate browsers
29581 context.setLineDash([]);
29582 }
29583 };
29584
29585 CRp$2.drawArrowheads = function (context, edge, opacity) {
29586 var rs = edge._private.rscratch;
29587 var isHaystack = rs.edgeType === 'haystack';
29588
29589 if (!isHaystack) {
29590 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29591 }
29592
29593 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29594 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29595
29596 if (!isHaystack) {
29597 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29598 }
29599 };
29600
29601 CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29602 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29603 return;
29604 }
29605
29606 var self = this;
29607 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29608
29609 if (arrowShape === 'none') {
29610 return;
29611 }
29612
29613 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29614 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29615 var edgeWidth = edge.pstyle('width').pfValue;
29616 var edgeOpacity = edge.pstyle('opacity').value;
29617
29618 if (opacity === undefined) {
29619 opacity = edgeOpacity;
29620 }
29621
29622 var gco = context.globalCompositeOperation;
29623
29624 if (opacity !== 1 || arrowFill === 'hollow') {
29625 // then extra clear is needed
29626 context.globalCompositeOperation = 'destination-out';
29627 self.colorFillStyle(context, 255, 255, 255, 1);
29628 self.colorStrokeStyle(context, 255, 255, 255, 1);
29629 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29630 context.globalCompositeOperation = gco;
29631 } // otherwise, the opaque arrow clears it for free :)
29632
29633
29634 var color = edge.pstyle(prefix + '-arrow-color').value;
29635 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29636 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29637 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29638 };
29639
29640 CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29641 var r = this;
29642 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29643 var pathCacheHit = false;
29644 var path;
29645 var canvasContext = context;
29646 var translation = {
29647 x: x,
29648 y: y
29649 };
29650 var scale = edge.pstyle('arrow-scale').value;
29651 var size = this.getArrowWidth(edgeWidth, scale);
29652 var shapeImpl = r.arrowShapes[shape];
29653
29654 if (usePaths) {
29655 var cache = r.arrowPathCache = r.arrowPathCache || [];
29656 var key = hashString(shape);
29657 var cachedPath = cache[key];
29658
29659 if (cachedPath != null) {
29660 path = context = cachedPath;
29661 pathCacheHit = true;
29662 } else {
29663 path = context = new Path2D();
29664 cache[key] = path;
29665 }
29666 }
29667
29668 if (!pathCacheHit) {
29669 if (context.beginPath) {
29670 context.beginPath();
29671 }
29672
29673 if (usePaths) {
29674 // store in the path cache with values easily manipulated later
29675 shapeImpl.draw(context, 1, 0, {
29676 x: 0,
29677 y: 0
29678 }, 1);
29679 } else {
29680 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29681 }
29682
29683 if (context.closePath) {
29684 context.closePath();
29685 }
29686 }
29687
29688 context = canvasContext;
29689
29690 if (usePaths) {
29691 // set transform to arrow position/orientation
29692 context.translate(x, y);
29693 context.rotate(angle);
29694 context.scale(size, size);
29695 }
29696
29697 if (fill === 'filled' || fill === 'both') {
29698 if (usePaths) {
29699 context.fill(path);
29700 } else {
29701 context.fill();
29702 }
29703 }
29704
29705 if (fill === 'hollow' || fill === 'both') {
29706 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29707 context.lineJoin = 'miter';
29708
29709 if (usePaths) {
29710 context.stroke(path);
29711 } else {
29712 context.stroke();
29713 }
29714 }
29715
29716 if (usePaths) {
29717 // reset transform by applying inverse
29718 context.scale(1 / size, 1 / size);
29719 context.rotate(-angle);
29720 context.translate(-x, -y);
29721 }
29722 };
29723
29724 var CRp$3 = {};
29725
29726 CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29727 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29728 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29729 return;
29730 }
29731
29732 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29733 };
29734
29735 CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29736 var r = this;
29737 var pos = node.position();
29738 var nodeX = pos.x;
29739 var nodeY = pos.y;
29740 var styleObj = node.cy().style();
29741 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29742 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29743 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29744 var nodeW = node.width();
29745 var nodeH = node.height();
29746 var paddingX2 = node.padding() * 2;
29747 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29748 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29749 var rs = node._private.rscratch;
29750 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29751 var shouldClip = clip === 'node';
29752 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29753 var imgW = img.width || img.cachedW;
29754 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29755
29756 if (null == imgW || null == imgH) {
29757 document.body.appendChild(img); // eslint-disable-line no-undef
29758
29759 imgW = img.cachedW = img.width || img.offsetWidth;
29760 imgH = img.cachedH = img.height || img.offsetHeight;
29761 document.body.removeChild(img); // eslint-disable-line no-undef
29762 }
29763
29764 var w = imgW;
29765 var h = imgH;
29766
29767 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29768 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29769 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29770 } else {
29771 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29772 }
29773 }
29774
29775 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29776 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29777 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29778 } else {
29779 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29780 }
29781 }
29782
29783 if (w === 0 || h === 0) {
29784 return; // no point in drawing empty image (and chrome is broken in this case)
29785 }
29786
29787 if (fit === 'contain') {
29788 var scale = Math.min(nodeTW / w, nodeTH / h);
29789 w *= scale;
29790 h *= scale;
29791 } else if (fit === 'cover') {
29792 var scale = Math.max(nodeTW / w, nodeTH / h);
29793 w *= scale;
29794 h *= scale;
29795 }
29796
29797 var x = nodeX - nodeTW / 2; // left
29798
29799 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29800 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29801
29802 if (posXUnits === '%') {
29803 x += (nodeTW - w) * posXPfVal;
29804 } else {
29805 x += posXPfVal;
29806 }
29807
29808 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29809 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29810
29811 if (offXUnits === '%') {
29812 x += (nodeTW - w) * offXPfVal;
29813 } else {
29814 x += offXPfVal;
29815 }
29816
29817 var y = nodeY - nodeTH / 2; // top
29818
29819 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29820 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29821
29822 if (posYUnits === '%') {
29823 y += (nodeTH - h) * posYPfVal;
29824 } else {
29825 y += posYPfVal;
29826 }
29827
29828 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29829 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29830
29831 if (offYUnits === '%') {
29832 y += (nodeTH - h) * offYPfVal;
29833 } else {
29834 y += offYPfVal;
29835 }
29836
29837 if (rs.pathCache) {
29838 x -= nodeX;
29839 y -= nodeY;
29840 nodeX = 0;
29841 nodeY = 0;
29842 }
29843
29844 var gAlpha = context.globalAlpha;
29845 context.globalAlpha = imgOpacity;
29846
29847 if (repeat === 'no-repeat') {
29848 if (shouldClip) {
29849 context.save();
29850
29851 if (rs.pathCache) {
29852 context.clip(rs.pathCache);
29853 } else {
29854 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29855 context.clip();
29856 }
29857 }
29858
29859 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29860
29861 if (shouldClip) {
29862 context.restore();
29863 }
29864 } else {
29865 var pattern = context.createPattern(img, repeat);
29866 context.fillStyle = pattern;
29867 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29868 context.translate(x, y);
29869 context.fill();
29870 context.translate(-x, -y);
29871 }
29872
29873 context.globalAlpha = gAlpha;
29874 };
29875
29876 var CRp$4 = {};
29877
29878 CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29879 if (!scale) {
29880 var zoom = ele.cy().zoom();
29881 var pxRatio = this.getPixelRatio();
29882 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29883
29884 scale = Math.pow(2, lvl);
29885 }
29886
29887 var computedSize = ele.pstyle('font-size').pfValue * scale;
29888 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29889
29890 if (computedSize < minSize) {
29891 return false;
29892 }
29893
29894 return true;
29895 };
29896
29897 CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29898 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29899 var r = this;
29900
29901 if (force == null) {
29902 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29903 return;
29904 }
29905 } else if (force === false) {
29906 return;
29907 }
29908
29909 if (ele.isNode()) {
29910 var label = ele.pstyle('label');
29911
29912 if (!label || !label.value) {
29913 return;
29914 }
29915
29916 var justification = r.getLabelJustification(ele);
29917 context.textAlign = justification;
29918 context.textBaseline = 'bottom';
29919 } else {
29920 var badLine = ele.element()._private.rscratch.badLine;
29921
29922 var _label = ele.pstyle('label');
29923
29924 var srcLabel = ele.pstyle('source-label');
29925 var tgtLabel = ele.pstyle('target-label');
29926
29927 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29928 return;
29929 }
29930
29931 context.textAlign = 'center';
29932 context.textBaseline = 'bottom';
29933 }
29934
29935 var applyRotation = !shiftToOriginWithBb;
29936 var bb;
29937
29938 if (shiftToOriginWithBb) {
29939 bb = shiftToOriginWithBb;
29940 context.translate(-bb.x1, -bb.y1);
29941 }
29942
29943 if (prefix == null) {
29944 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29945
29946 if (ele.isEdge()) {
29947 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29948 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29949 }
29950 } else {
29951 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29952 }
29953
29954 if (shiftToOriginWithBb) {
29955 context.translate(bb.x1, bb.y1);
29956 }
29957 };
29958
29959 CRp$4.getFontCache = function (context) {
29960 var cache;
29961 this.fontCaches = this.fontCaches || [];
29962
29963 for (var i = 0; i < this.fontCaches.length; i++) {
29964 cache = this.fontCaches[i];
29965
29966 if (cache.context === context) {
29967 return cache;
29968 }
29969 }
29970
29971 cache = {
29972 context: context
29973 };
29974 this.fontCaches.push(cache);
29975 return cache;
29976 }; // set up canvas context with font
29977 // returns transformed text string
29978
29979
29980 CRp$4.setupTextStyle = function (context, ele) {
29981 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29982 // Font style
29983 var labelStyle = ele.pstyle('font-style').strValue;
29984 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29985 var labelFamily = ele.pstyle('font-family').strValue;
29986 var labelWeight = ele.pstyle('font-weight').strValue;
29987 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29988 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29989 var color = ele.pstyle('color').value;
29990 var outlineColor = ele.pstyle('text-outline-color').value;
29991 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29992 context.lineJoin = 'round'; // so text outlines aren't jagged
29993
29994 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29995 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29996 }; // TODO ensure re-used
29997
29998
29999 function roundRect(ctx, x, y, width, height) {
30000 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
30001 ctx.beginPath();
30002 ctx.moveTo(x + radius, y);
30003 ctx.lineTo(x + width - radius, y);
30004 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
30005 ctx.lineTo(x + width, y + height - radius);
30006 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
30007 ctx.lineTo(x + radius, y + height);
30008 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
30009 ctx.lineTo(x, y + radius);
30010 ctx.quadraticCurveTo(x, y, x + radius, y);
30011 ctx.closePath();
30012 ctx.fill();
30013 }
30014
30015 CRp$4.getTextAngle = function (ele, prefix) {
30016 var theta;
30017 var _p = ele._private;
30018 var rscratch = _p.rscratch;
30019 var pdash = prefix ? prefix + '-' : '';
30020 var rotation = ele.pstyle(pdash + 'text-rotation');
30021 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
30022
30023 if (rotation.strValue === 'autorotate') {
30024 theta = ele.isEdge() ? textAngle : 0;
30025 } else if (rotation.strValue === 'none') {
30026 theta = 0;
30027 } else {
30028 theta = rotation.pfValue;
30029 }
30030
30031 return theta;
30032 };
30033
30034 CRp$4.drawText = function (context, ele, prefix) {
30035 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30036 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30037 var _p = ele._private;
30038 var rscratch = _p.rscratch;
30039 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
30040
30041 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
30042 return;
30043 } // use 'main' as an alias for the main label (i.e. null prefix)
30044
30045
30046 if (prefix === 'main') {
30047 prefix = null;
30048 }
30049
30050 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
30051 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
30052 var orgTextX, orgTextY; // used for rotation
30053
30054 var text = this.getLabelText(ele, prefix);
30055
30056 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
30057 this.setupTextStyle(context, ele, useEleOpacity);
30058 var pdash = prefix ? prefix + '-' : '';
30059 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
30060 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
30061 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
30062 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
30063 var isEdge = ele.isEdge();
30064 var halign = ele.pstyle('text-halign').value;
30065 var valign = ele.pstyle('text-valign').value;
30066
30067 if (isEdge) {
30068 halign = 'center';
30069 valign = 'center';
30070 }
30071
30072 textX += marginX;
30073 textY += marginY;
30074 var theta;
30075
30076 if (!applyRotation) {
30077 theta = 0;
30078 } else {
30079 theta = this.getTextAngle(ele, prefix);
30080 }
30081
30082 if (theta !== 0) {
30083 orgTextX = textX;
30084 orgTextY = textY;
30085 context.translate(orgTextX, orgTextY);
30086 context.rotate(theta);
30087 textX = 0;
30088 textY = 0;
30089 }
30090
30091 switch (valign) {
30092 case 'top':
30093 break;
30094
30095 case 'center':
30096 textY += textH / 2;
30097 break;
30098
30099 case 'bottom':
30100 textY += textH;
30101 break;
30102 }
30103
30104 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
30105 var borderOpacity = ele.pstyle('text-border-opacity').value;
30106 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
30107 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
30108
30109 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
30110 var bgX = textX - backgroundPadding;
30111
30112 switch (halign) {
30113 case 'left':
30114 bgX -= textW;
30115 break;
30116
30117 case 'center':
30118 bgX -= textW / 2;
30119 break;
30120 }
30121
30122 var bgY = textY - textH - backgroundPadding;
30123 var bgW = textW + 2 * backgroundPadding;
30124 var bgH = textH + 2 * backgroundPadding;
30125
30126 if (backgroundOpacity > 0) {
30127 var textFill = context.fillStyle;
30128 var textBackgroundColor = ele.pstyle('text-background-color').value;
30129 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
30130 var styleShape = ele.pstyle('text-background-shape').strValue;
30131
30132 if (styleShape.indexOf('round') === 0) {
30133 roundRect(context, bgX, bgY, bgW, bgH, 2);
30134 } else {
30135 context.fillRect(bgX, bgY, bgW, bgH);
30136 }
30137
30138 context.fillStyle = textFill;
30139 }
30140
30141 if (textBorderWidth > 0 && borderOpacity > 0) {
30142 var textStroke = context.strokeStyle;
30143 var textLineWidth = context.lineWidth;
30144 var textBorderColor = ele.pstyle('text-border-color').value;
30145 var textBorderStyle = ele.pstyle('text-border-style').value;
30146 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
30147 context.lineWidth = textBorderWidth;
30148
30149 if (context.setLineDash) {
30150 // for very outofdate browsers
30151 switch (textBorderStyle) {
30152 case 'dotted':
30153 context.setLineDash([1, 1]);
30154 break;
30155
30156 case 'dashed':
30157 context.setLineDash([4, 2]);
30158 break;
30159
30160 case 'double':
30161 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
30162
30163 context.setLineDash([]);
30164 break;
30165
30166 case 'solid':
30167 context.setLineDash([]);
30168 break;
30169 }
30170 }
30171
30172 context.strokeRect(bgX, bgY, bgW, bgH);
30173
30174 if (textBorderStyle === 'double') {
30175 var whiteWidth = textBorderWidth / 2;
30176 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
30177 }
30178
30179 if (context.setLineDash) {
30180 // for very outofdate browsers
30181 context.setLineDash([]);
30182 }
30183
30184 context.lineWidth = textLineWidth;
30185 context.strokeStyle = textStroke;
30186 }
30187 }
30188
30189 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
30190
30191 if (lineWidth > 0) {
30192 context.lineWidth = lineWidth;
30193 }
30194
30195 if (ele.pstyle('text-wrap').value === 'wrap') {
30196 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
30197 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
30198 var halfTextW = textW / 2;
30199 var justification = this.getLabelJustification(ele);
30200
30201 if (justification === 'auto') ; else if (halign === 'left') {
30202 // auto justification : right
30203 if (justification === 'left') {
30204 textX += -textW;
30205 } else if (justification === 'center') {
30206 textX += -halfTextW;
30207 } // else same as auto
30208
30209 } else if (halign === 'center') {
30210 // auto justfication : center
30211 if (justification === 'left') {
30212 textX += -halfTextW;
30213 } else if (justification === 'right') {
30214 textX += halfTextW;
30215 } // else same as auto
30216
30217 } else if (halign === 'right') {
30218 // auto justification : left
30219 if (justification === 'center') {
30220 textX += halfTextW;
30221 } else if (justification === 'right') {
30222 textX += textW;
30223 } // else same as auto
30224
30225 }
30226
30227 switch (valign) {
30228 case 'top':
30229 textY -= (lines.length - 1) * lineHeight;
30230 break;
30231
30232 case 'center':
30233 case 'bottom':
30234 textY -= (lines.length - 1) * lineHeight;
30235 break;
30236 }
30237
30238 for (var l = 0; l < lines.length; l++) {
30239 if (lineWidth > 0) {
30240 context.strokeText(lines[l], textX, textY);
30241 }
30242
30243 context.fillText(lines[l], textX, textY);
30244 textY += lineHeight;
30245 }
30246 } else {
30247 if (lineWidth > 0) {
30248 context.strokeText(text, textX, textY);
30249 }
30250
30251 context.fillText(text, textX, textY);
30252 }
30253
30254 if (theta !== 0) {
30255 context.rotate(-theta);
30256 context.translate(-orgTextX, -orgTextY);
30257 }
30258 }
30259 };
30260
30261 /* global Path2D */
30262 var CRp$5 = {};
30263
30264 CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
30265 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30266 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30267 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
30268 var r = this;
30269 var nodeWidth, nodeHeight;
30270 var _p = node._private;
30271 var rs = _p.rscratch;
30272 var pos = node.position();
30273
30274 if (!number(pos.x) || !number(pos.y)) {
30275 return; // can't draw node with undefined position
30276 }
30277
30278 if (shouldDrawOpacity && !node.visible()) {
30279 return;
30280 }
30281
30282 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
30283 var usePaths = r.usePaths();
30284 var path;
30285 var pathCacheHit = false;
30286 var padding = node.padding();
30287 nodeWidth = node.width() + 2 * padding;
30288 nodeHeight = node.height() + 2 * padding; //
30289 // setup shift
30290
30291 var bb;
30292
30293 if (shiftToOriginWithBb) {
30294 bb = shiftToOriginWithBb;
30295 context.translate(-bb.x1, -bb.y1);
30296 } //
30297 // load bg image
30298
30299
30300 var bgImgProp = node.pstyle('background-image');
30301 var urls = bgImgProp.value;
30302 var urlDefined = new Array(urls.length);
30303 var image = new Array(urls.length);
30304 var numImages = 0;
30305
30306 for (var i = 0; i < urls.length; i++) {
30307 var url = urls[i];
30308 var defd = urlDefined[i] = url != null && url !== 'none';
30309
30310 if (defd) {
30311 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
30312 numImages++; // get image, and if not loaded then ask to redraw when later loaded
30313
30314 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
30315 _p.backgroundTimestamp = Date.now();
30316 node.emitAndNotify('background');
30317 });
30318 }
30319 } //
30320 // setup styles
30321
30322
30323 var darkness = node.pstyle('background-blacken').value;
30324 var borderWidth = node.pstyle('border-width').pfValue;
30325 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
30326 var borderColor = node.pstyle('border-color').value;
30327 var borderStyle = node.pstyle('border-style').value;
30328 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
30329 context.lineJoin = 'miter'; // so borders are square with the node shape
30330
30331 var setupShapeColor = function setupShapeColor() {
30332 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
30333 r.eleFillStyle(context, node, bgOpy);
30334 };
30335
30336 var setupBorderColor = function setupBorderColor() {
30337 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
30338 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
30339 }; //
30340 // setup shape
30341
30342
30343 var styleShape = node.pstyle('shape').strValue;
30344 var shapePts = node.pstyle('shape-polygon-points').pfValue;
30345
30346 if (usePaths) {
30347 context.translate(pos.x, pos.y);
30348 var pathCache = r.nodePathCache = r.nodePathCache || [];
30349 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
30350 var cachedPath = pathCache[key];
30351
30352 if (cachedPath != null) {
30353 path = cachedPath;
30354 pathCacheHit = true;
30355 rs.pathCache = path;
30356 } else {
30357 path = new Path2D();
30358 pathCache[key] = rs.pathCache = path;
30359 }
30360 }
30361
30362 var drawShape = function drawShape() {
30363 if (!pathCacheHit) {
30364 var npos = pos;
30365
30366 if (usePaths) {
30367 npos = {
30368 x: 0,
30369 y: 0
30370 };
30371 }
30372
30373 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
30374 }
30375
30376 if (usePaths) {
30377 context.fill(path);
30378 } else {
30379 context.fill();
30380 }
30381 };
30382
30383 var drawImages = function drawImages() {
30384 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30385 var prevBging = _p.backgrounding;
30386 var totalCompleted = 0;
30387
30388 for (var _i = 0; _i < image.length; _i++) {
30389 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
30390 totalCompleted++;
30391 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
30392 }
30393 }
30394
30395 _p.backgrounding = !(totalCompleted === numImages);
30396
30397 if (prevBging !== _p.backgrounding) {
30398 // update style b/c :backgrounding state changed
30399 node.updateStyle(false);
30400 }
30401 };
30402
30403 var drawPie = function drawPie() {
30404 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
30405 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
30406
30407 if (r.hasPie(node)) {
30408 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
30409
30410 if (redrawShape) {
30411 if (!usePaths) {
30412 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
30413 }
30414 }
30415 }
30416 };
30417
30418 var darken = function darken() {
30419 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30420 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
30421 var c = darkness > 0 ? 0 : 255;
30422
30423 if (darkness !== 0) {
30424 r.colorFillStyle(context, c, c, c, opacity);
30425
30426 if (usePaths) {
30427 context.fill(path);
30428 } else {
30429 context.fill();
30430 }
30431 }
30432 };
30433
30434 var drawBorder = function drawBorder() {
30435 if (borderWidth > 0) {
30436 context.lineWidth = borderWidth;
30437 context.lineCap = 'butt';
30438
30439 if (context.setLineDash) {
30440 // for very outofdate browsers
30441 switch (borderStyle) {
30442 case 'dotted':
30443 context.setLineDash([1, 1]);
30444 break;
30445
30446 case 'dashed':
30447 context.setLineDash([4, 2]);
30448 break;
30449
30450 case 'solid':
30451 case 'double':
30452 context.setLineDash([]);
30453 break;
30454 }
30455 }
30456
30457 if (usePaths) {
30458 context.stroke(path);
30459 } else {
30460 context.stroke();
30461 }
30462
30463 if (borderStyle === 'double') {
30464 context.lineWidth = borderWidth / 3;
30465 var gco = context.globalCompositeOperation;
30466 context.globalCompositeOperation = 'destination-out';
30467
30468 if (usePaths) {
30469 context.stroke(path);
30470 } else {
30471 context.stroke();
30472 }
30473
30474 context.globalCompositeOperation = gco;
30475 } // reset in case we changed the border style
30476
30477
30478 if (context.setLineDash) {
30479 // for very outofdate browsers
30480 context.setLineDash([]);
30481 }
30482 }
30483 };
30484
30485 var drawOverlay = function drawOverlay() {
30486 if (shouldDrawOverlay) {
30487 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30488 }
30489 };
30490
30491 var drawText = function drawText() {
30492 r.drawElementText(context, node, null, drawLabel);
30493 };
30494
30495 var ghost = node.pstyle('ghost').value === 'yes';
30496
30497 if (ghost) {
30498 var gx = node.pstyle('ghost-offset-x').pfValue;
30499 var gy = node.pstyle('ghost-offset-y').pfValue;
30500 var ghostOpacity = node.pstyle('ghost-opacity').value;
30501 var effGhostOpacity = ghostOpacity * eleOpacity;
30502 context.translate(gx, gy);
30503 setupShapeColor(ghostOpacity * bgOpacity);
30504 drawShape();
30505 drawImages(effGhostOpacity);
30506 drawPie(darkness !== 0 || borderWidth !== 0);
30507 darken(effGhostOpacity);
30508 setupBorderColor(ghostOpacity * borderOpacity);
30509 drawBorder();
30510 context.translate(-gx, -gy);
30511 }
30512
30513 setupShapeColor();
30514 drawShape();
30515 drawImages();
30516 drawPie(darkness !== 0 || borderWidth !== 0);
30517 darken();
30518 setupBorderColor();
30519 drawBorder();
30520
30521 if (usePaths) {
30522 context.translate(-pos.x, -pos.y);
30523 }
30524
30525 drawText();
30526 drawOverlay(); //
30527 // clean up shift
30528
30529 if (shiftToOriginWithBb) {
30530 context.translate(bb.x1, bb.y1);
30531 }
30532 };
30533
30534 CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
30535 var r = this;
30536
30537 if (!node.visible()) {
30538 return;
30539 }
30540
30541 var overlayPadding = node.pstyle('overlay-padding').pfValue;
30542 var overlayOpacity = node.pstyle('overlay-opacity').value;
30543 var overlayColor = node.pstyle('overlay-color').value;
30544
30545 if (overlayOpacity > 0) {
30546 pos = pos || node.position();
30547
30548 if (nodeWidth == null || nodeHeight == null) {
30549 var padding = node.padding();
30550 nodeWidth = node.width() + 2 * padding;
30551 nodeHeight = node.height() + 2 * padding;
30552 }
30553
30554 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
30555 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
30556 context.fill();
30557 }
30558 }; // does the node have at least one pie piece?
30559
30560
30561 CRp$5.hasPie = function (node) {
30562 node = node[0]; // ensure ele ref
30563
30564 return node._private.hasPie;
30565 };
30566
30567 CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30568 node = node[0]; // ensure ele ref
30569
30570 pos = pos || node.position();
30571 var cyStyle = node.cy().style();
30572 var pieSize = node.pstyle('pie-size');
30573 var x = pos.x;
30574 var y = pos.y;
30575 var nodeW = node.width();
30576 var nodeH = node.height();
30577 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30578
30579 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30580
30581 var usePaths = this.usePaths();
30582
30583 if (usePaths) {
30584 x = 0;
30585 y = 0;
30586 }
30587
30588 if (pieSize.units === '%') {
30589 radius = radius * pieSize.pfValue;
30590 } else if (pieSize.pfValue !== undefined) {
30591 radius = pieSize.pfValue / 2;
30592 }
30593
30594 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30595 // 1..N
30596 var size = node.pstyle('pie-' + i + '-background-size').value;
30597 var color = node.pstyle('pie-' + i + '-background-color').value;
30598 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30599 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30600 // percent can't push beyond 1
30601
30602 if (percent + lastPercent > 1) {
30603 percent = 1 - lastPercent;
30604 }
30605
30606 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30607
30608 var angleDelta = 2 * Math.PI * percent;
30609 var angleEnd = angleStart + angleDelta; // ignore if
30610 // - zero size
30611 // - we're already beyond the full circle
30612 // - adding the current slice would go beyond the full circle
30613
30614 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30615 continue;
30616 }
30617
30618 context.beginPath();
30619 context.moveTo(x, y);
30620 context.arc(x, y, radius, angleStart, angleEnd);
30621 context.closePath();
30622 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30623 context.fill();
30624 lastPercent += percent;
30625 }
30626 };
30627
30628 var CRp$6 = {};
30629 var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30630
30631 CRp$6.getPixelRatio = function () {
30632 var context = this.data.contexts[0];
30633
30634 if (this.forcedPixelRatio != null) {
30635 return this.forcedPixelRatio;
30636 }
30637
30638 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30639 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30640 };
30641
30642 CRp$6.paintCache = function (context) {
30643 var caches = this.paintCaches = this.paintCaches || [];
30644 var needToCreateCache = true;
30645 var cache;
30646
30647 for (var i = 0; i < caches.length; i++) {
30648 cache = caches[i];
30649
30650 if (cache.context === context) {
30651 needToCreateCache = false;
30652 break;
30653 }
30654 }
30655
30656 if (needToCreateCache) {
30657 cache = {
30658 context: context
30659 };
30660 caches.push(cache);
30661 }
30662
30663 return cache;
30664 };
30665
30666 CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30667 var gradientStyle;
30668 var usePaths = this.usePaths();
30669 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30670 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30671
30672 if (fill === 'radial-gradient') {
30673 if (ele.isEdge()) {
30674 var start = ele.sourceEndpoint(),
30675 end = ele.targetEndpoint(),
30676 mid = ele.midpoint();
30677 var d1 = dist(start, mid);
30678 var d2 = dist(end, mid);
30679 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30680 } else {
30681 var pos = usePaths ? {
30682 x: 0,
30683 y: 0
30684 } : ele.position(),
30685 width = ele.paddedWidth(),
30686 height = ele.paddedHeight();
30687 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30688 }
30689 } else {
30690 if (ele.isEdge()) {
30691 var _start = ele.sourceEndpoint(),
30692 _end = ele.targetEndpoint();
30693
30694 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30695 } else {
30696 var _pos = usePaths ? {
30697 x: 0,
30698 y: 0
30699 } : ele.position(),
30700 _width = ele.paddedWidth(),
30701 _height = ele.paddedHeight(),
30702 halfWidth = _width / 2,
30703 halfHeight = _height / 2;
30704
30705 var direction = ele.pstyle('background-gradient-direction').value;
30706
30707 switch (direction) {
30708 case 'to-bottom':
30709 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30710 break;
30711
30712 case 'to-top':
30713 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30714 break;
30715
30716 case 'to-left':
30717 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30718 break;
30719
30720 case 'to-right':
30721 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30722 break;
30723
30724 case 'to-bottom-right':
30725 case 'to-right-bottom':
30726 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30727 break;
30728
30729 case 'to-top-right':
30730 case 'to-right-top':
30731 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30732 break;
30733
30734 case 'to-bottom-left':
30735 case 'to-left-bottom':
30736 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30737 break;
30738
30739 case 'to-top-left':
30740 case 'to-left-top':
30741 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30742 break;
30743 }
30744 }
30745 }
30746
30747 if (!gradientStyle) return null; // invalid gradient style
30748
30749 var hasPositions = positions.length === colors.length;
30750 var length = colors.length;
30751
30752 for (var i = 0; i < length; i++) {
30753 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30754 }
30755
30756 return gradientStyle;
30757 };
30758
30759 CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30760 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30761 if (!gradientStyle) return null; // error
30762
30763 context.fillStyle = gradientStyle;
30764 };
30765
30766 CRp$6.colorFillStyle = function (context, r, g, b, a) {
30767 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30768 // var cache = this.paintCache(context);
30769 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30770 // if( cache.fillStyle !== fillStyle ){
30771 // context.fillStyle = cache.fillStyle = fillStyle;
30772 // }
30773 };
30774
30775 CRp$6.eleFillStyle = function (context, ele, opacity) {
30776 var backgroundFill = ele.pstyle('background-fill').value;
30777
30778 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30779 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30780 } else {
30781 var backgroundColor = ele.pstyle('background-color').value;
30782 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30783 }
30784 };
30785
30786 CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30787 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30788 if (!gradientStyle) return null; // error
30789
30790 context.strokeStyle = gradientStyle;
30791 };
30792
30793 CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30794 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30795 // var cache = this.paintCache(context);
30796 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30797 // if( cache.strokeStyle !== strokeStyle ){
30798 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30799 // }
30800 };
30801
30802 CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30803 var lineFill = ele.pstyle('line-fill').value;
30804
30805 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30806 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30807 } else {
30808 var lineColor = ele.pstyle('line-color').value;
30809 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30810 }
30811 }; // Resize canvas
30812
30813
30814 CRp$6.matchCanvasSize = function (container) {
30815 var r = this;
30816 var data = r.data;
30817 var bb = r.findContainerClientCoords();
30818 var width = bb[2];
30819 var height = bb[3];
30820 var pixelRatio = r.getPixelRatio();
30821 var mbPxRatio = r.motionBlurPxRatio;
30822
30823 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30824 pixelRatio = mbPxRatio;
30825 }
30826
30827 var canvasWidth = width * pixelRatio;
30828 var canvasHeight = height * pixelRatio;
30829 var canvas;
30830
30831 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30832 return; // save cycles if same
30833 }
30834
30835 r.fontCaches = null; // resizing resets the style
30836
30837 var canvasContainer = data.canvasContainer;
30838 canvasContainer.style.width = width + 'px';
30839 canvasContainer.style.height = height + 'px';
30840
30841 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30842 canvas = data.canvases[i];
30843 canvas.width = canvasWidth;
30844 canvas.height = canvasHeight;
30845 canvas.style.width = width + 'px';
30846 canvas.style.height = height + 'px';
30847 }
30848
30849 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30850 canvas = data.bufferCanvases[i];
30851 canvas.width = canvasWidth;
30852 canvas.height = canvasHeight;
30853 canvas.style.width = width + 'px';
30854 canvas.style.height = height + 'px';
30855 }
30856
30857 r.textureMult = 1;
30858
30859 if (pixelRatio <= 1) {
30860 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30861 r.textureMult = 2;
30862 canvas.width = canvasWidth * r.textureMult;
30863 canvas.height = canvasHeight * r.textureMult;
30864 }
30865
30866 r.canvasWidth = canvasWidth;
30867 r.canvasHeight = canvasHeight;
30868 };
30869
30870 CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30871 this.render({
30872 forcedContext: cxt,
30873 forcedZoom: zoom,
30874 forcedPan: pan,
30875 drawAllLayers: true,
30876 forcedPxRatio: pxRatio
30877 });
30878 };
30879
30880 CRp$6.render = function (options) {
30881 options = options || staticEmptyObject();
30882 var forcedContext = options.forcedContext;
30883 var drawAllLayers = options.drawAllLayers;
30884 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30885 var forcedZoom = options.forcedZoom;
30886 var forcedPan = options.forcedPan;
30887 var r = this;
30888 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30889 var cy = r.cy;
30890 var data = r.data;
30891 var needDraw = data.canvasNeedsRedraw;
30892 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30893 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30894 var mbPxRatio = r.motionBlurPxRatio;
30895 var hasCompoundNodes = cy.hasCompoundNodes();
30896 var inNodeDragGesture = r.hoverData.draggingEles;
30897 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30898 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30899 var motionBlurFadeEffect = motionBlur;
30900
30901 if (!forcedContext) {
30902 if (r.prevPxRatio !== pixelRatio) {
30903 r.invalidateContainerClientCoordsCache();
30904 r.matchCanvasSize(r.container);
30905 r.redrawHint('eles', true);
30906 r.redrawHint('drag', true);
30907 }
30908
30909 r.prevPxRatio = pixelRatio;
30910 }
30911
30912 if (!forcedContext && r.motionBlurTimeout) {
30913 clearTimeout(r.motionBlurTimeout);
30914 }
30915
30916 if (motionBlur) {
30917 if (r.mbFrames == null) {
30918 r.mbFrames = 0;
30919 }
30920
30921 r.mbFrames++;
30922
30923 if (r.mbFrames < 3) {
30924 // need several frames before even high quality motionblur
30925 motionBlurFadeEffect = false;
30926 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30927
30928
30929 if (r.mbFrames > r.minMbLowQualFrames) {
30930 //r.fullQualityMb = false;
30931 r.motionBlurPxRatio = r.mbPxRBlurry;
30932 }
30933 }
30934
30935 if (r.clearingMotionBlur) {
30936 r.motionBlurPxRatio = 1;
30937 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30938 // because a rogue async texture frame would clear needDraw
30939
30940
30941 if (r.textureDrawLastFrame && !textureDraw) {
30942 needDraw[r.NODE] = true;
30943 needDraw[r.SELECT_BOX] = true;
30944 }
30945
30946 var style = cy.style();
30947 var zoom = cy.zoom();
30948 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30949 var pan = cy.pan();
30950 var effectivePan = {
30951 x: pan.x,
30952 y: pan.y
30953 };
30954 var vp = {
30955 zoom: zoom,
30956 pan: {
30957 x: pan.x,
30958 y: pan.y
30959 }
30960 };
30961 var prevVp = r.prevViewport;
30962 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)
30963
30964 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30965 r.motionBlurPxRatio = 1;
30966 }
30967
30968 if (forcedPan) {
30969 effectivePan = forcedPan;
30970 } // apply pixel ratio
30971
30972
30973 effectiveZoom *= pixelRatio;
30974 effectivePan.x *= pixelRatio;
30975 effectivePan.y *= pixelRatio;
30976 var eles = r.getCachedZSortedEles();
30977
30978 function mbclear(context, x, y, w, h) {
30979 var gco = context.globalCompositeOperation;
30980 context.globalCompositeOperation = 'destination-out';
30981 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30982 context.fillRect(x, y, w, h);
30983 context.globalCompositeOperation = gco;
30984 }
30985
30986 function setContextTransform(context, clear) {
30987 var ePan, eZoom, w, h;
30988
30989 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30990 ePan = {
30991 x: pan.x * mbPxRatio,
30992 y: pan.y * mbPxRatio
30993 };
30994 eZoom = zoom * mbPxRatio;
30995 w = r.canvasWidth * mbPxRatio;
30996 h = r.canvasHeight * mbPxRatio;
30997 } else {
30998 ePan = effectivePan;
30999 eZoom = effectiveZoom;
31000 w = r.canvasWidth;
31001 h = r.canvasHeight;
31002 }
31003
31004 context.setTransform(1, 0, 0, 1, 0, 0);
31005
31006 if (clear === 'motionBlur') {
31007 mbclear(context, 0, 0, w, h);
31008 } else if (!forcedContext && (clear === undefined || clear)) {
31009 context.clearRect(0, 0, w, h);
31010 }
31011
31012 if (!drawAllLayers) {
31013 context.translate(ePan.x, ePan.y);
31014 context.scale(eZoom, eZoom);
31015 }
31016
31017 if (forcedPan) {
31018 context.translate(forcedPan.x, forcedPan.y);
31019 }
31020
31021 if (forcedZoom) {
31022 context.scale(forcedZoom, forcedZoom);
31023 }
31024 }
31025
31026 if (!textureDraw) {
31027 r.textureDrawLastFrame = false;
31028 }
31029
31030 if (textureDraw) {
31031 r.textureDrawLastFrame = true;
31032
31033 if (!r.textureCache) {
31034 r.textureCache = {};
31035 r.textureCache.bb = cy.mutableElements().boundingBox();
31036 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
31037 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
31038 cxt.setTransform(1, 0, 0, 1, 0, 0);
31039 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
31040 r.render({
31041 forcedContext: cxt,
31042 drawOnlyNodeLayer: true,
31043 forcedPxRatio: pixelRatio * r.textureMult
31044 });
31045 var vp = r.textureCache.viewport = {
31046 zoom: cy.zoom(),
31047 pan: cy.pan(),
31048 width: r.canvasWidth,
31049 height: r.canvasHeight
31050 };
31051 vp.mpan = {
31052 x: (0 - vp.pan.x) / vp.zoom,
31053 y: (0 - vp.pan.y) / vp.zoom
31054 };
31055 }
31056
31057 needDraw[r.DRAG] = false;
31058 needDraw[r.NODE] = false;
31059 var context = data.contexts[r.NODE];
31060 var texture = r.textureCache.texture;
31061 var vp = r.textureCache.viewport;
31062 context.setTransform(1, 0, 0, 1, 0, 0);
31063
31064 if (motionBlur) {
31065 mbclear(context, 0, 0, vp.width, vp.height);
31066 } else {
31067 context.clearRect(0, 0, vp.width, vp.height);
31068 }
31069
31070 var outsideBgColor = style.core('outside-texture-bg-color').value;
31071 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
31072 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
31073 context.fillRect(0, 0, vp.width, vp.height);
31074 var zoom = cy.zoom();
31075 setContextTransform(context, false);
31076 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
31077 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
31078 } else if (r.textureOnViewport && !forcedContext) {
31079 // clear the cache since we don't need it
31080 r.textureCache = null;
31081 }
31082
31083 var extent = cy.extent();
31084 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
31085 var hideEdges = r.hideEdgesOnViewport && vpManip;
31086 var needMbClear = [];
31087 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
31088
31089 if (needMbClear[r.NODE]) {
31090 r.clearedForMotionBlur[r.NODE] = true;
31091 }
31092
31093 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
31094
31095 if (needMbClear[r.DRAG]) {
31096 r.clearedForMotionBlur[r.DRAG] = true;
31097 }
31098
31099 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
31100 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
31101 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
31102 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
31103 setContextTransform(context, clear);
31104
31105 if (hideEdges) {
31106 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
31107 } else {
31108 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
31109 }
31110
31111 if (r.debug) {
31112 r.drawDebugPoints(context, eles.nondrag);
31113 }
31114
31115 if (!drawAllLayers && !motionBlur) {
31116 needDraw[r.NODE] = false;
31117 }
31118 }
31119
31120 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
31121 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
31122 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
31123 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
31124
31125 if (hideEdges) {
31126 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
31127 } else {
31128 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
31129 }
31130
31131 if (r.debug) {
31132 r.drawDebugPoints(context, eles.drag);
31133 }
31134
31135 if (!drawAllLayers && !motionBlur) {
31136 needDraw[r.DRAG] = false;
31137 }
31138 }
31139
31140 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
31141 var context = forcedContext || data.contexts[r.SELECT_BOX];
31142 setContextTransform(context);
31143
31144 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
31145 var zoom = r.cy.zoom();
31146 var borderWidth = style.core('selection-box-border-width').value / zoom;
31147 context.lineWidth = borderWidth;
31148 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 + ')';
31149 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31150
31151 if (borderWidth > 0) {
31152 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 + ')';
31153 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31154 }
31155 }
31156
31157 if (data.bgActivePosistion && !r.hoverData.selecting) {
31158 var zoom = r.cy.zoom();
31159 var pos = data.bgActivePosistion;
31160 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 + ')';
31161 context.beginPath();
31162 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
31163 context.fill();
31164 }
31165
31166 var timeToRender = r.lastRedrawTime;
31167
31168 if (r.showFps && timeToRender) {
31169 timeToRender = Math.round(timeToRender);
31170 var fps = Math.round(1000 / timeToRender);
31171 context.setTransform(1, 0, 0, 1, 0, 0);
31172 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
31173 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
31174 context.lineWidth = 1;
31175 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
31176 var maxFps = 60;
31177 context.strokeRect(0, 30, 250, 20);
31178 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
31179 }
31180
31181 if (!drawAllLayers) {
31182 needDraw[r.SELECT_BOX] = false;
31183 }
31184 } // motionblur: blit rendered blurry frames
31185
31186
31187 if (motionBlur && mbPxRatio !== 1) {
31188 var cxtNode = data.contexts[r.NODE];
31189 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
31190 var cxtDrag = data.contexts[r.DRAG];
31191 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
31192
31193 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
31194 cxt.setTransform(1, 0, 0, 1, 0, 0);
31195
31196 if (needClear || !motionBlurFadeEffect) {
31197 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
31198 } else {
31199 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
31200 }
31201
31202 var pxr = mbPxRatio;
31203 cxt.drawImage(txt, // img
31204 0, 0, // sx, sy
31205 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
31206 0, 0, // x, y
31207 r.canvasWidth, r.canvasHeight // w, h
31208 );
31209 };
31210
31211 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
31212 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
31213 needDraw[r.NODE] = false;
31214 }
31215
31216 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
31217 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
31218 needDraw[r.DRAG] = false;
31219 }
31220 }
31221
31222 r.prevViewport = vp;
31223
31224 if (r.clearingMotionBlur) {
31225 r.clearingMotionBlur = false;
31226 r.motionBlurCleared = true;
31227 r.motionBlur = true;
31228 }
31229
31230 if (motionBlur) {
31231 r.motionBlurTimeout = setTimeout(function () {
31232 r.motionBlurTimeout = null;
31233 r.clearedForMotionBlur[r.NODE] = false;
31234 r.clearedForMotionBlur[r.DRAG] = false;
31235 r.motionBlur = false;
31236 r.clearingMotionBlur = !textureDraw;
31237 r.mbFrames = 0;
31238 needDraw[r.NODE] = true;
31239 needDraw[r.DRAG] = true;
31240 r.redraw();
31241 }, motionBlurDelay);
31242 }
31243
31244 if (!forcedContext) {
31245 cy.emit('render');
31246 }
31247 };
31248
31249 var CRp$7 = {}; // @O Polygon drawing
31250
31251 CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
31252 var halfW = width / 2;
31253 var halfH = height / 2;
31254
31255 if (context.beginPath) {
31256 context.beginPath();
31257 }
31258
31259 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
31260
31261 for (var i = 1; i < points.length / 2; i++) {
31262 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
31263 }
31264
31265 context.closePath();
31266 };
31267
31268 CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
31269 var halfW = width / 2;
31270 var halfH = height / 2;
31271 var cornerRadius = getRoundPolygonRadius(width, height);
31272
31273 if (context.beginPath) {
31274 context.beginPath();
31275 }
31276
31277 for (var _i = 0; _i < points.length / 4; _i++) {
31278 var sourceUv = void 0,
31279 destUv = void 0;
31280
31281 if (_i === 0) {
31282 sourceUv = points.length - 2;
31283 } else {
31284 sourceUv = _i * 4 - 2;
31285 }
31286
31287 destUv = _i * 4 + 2;
31288 var px = x + halfW * points[_i * 4];
31289 var py = y + halfH * points[_i * 4 + 1];
31290 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
31291 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
31292 var cp0x = px - offset * points[sourceUv];
31293 var cp0y = py - offset * points[sourceUv + 1];
31294 var cp1x = px + offset * points[destUv];
31295 var cp1y = py + offset * points[destUv + 1];
31296
31297 if (_i === 0) {
31298 context.moveTo(cp0x, cp0y);
31299 } else {
31300 context.lineTo(cp0x, cp0y);
31301 }
31302
31303 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
31304 }
31305
31306 context.closePath();
31307 }; // Round rectangle drawing
31308
31309
31310 CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
31311 var halfWidth = width / 2;
31312 var halfHeight = height / 2;
31313 var cornerRadius = getRoundRectangleRadius(width, height);
31314
31315 if (context.beginPath) {
31316 context.beginPath();
31317 } // Start at top middle
31318
31319
31320 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
31321
31322 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
31323
31324 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
31325
31326 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
31327
31328 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
31329
31330 context.lineTo(x, y - halfHeight);
31331 context.closePath();
31332 };
31333
31334 CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
31335 var halfWidth = width / 2;
31336 var halfHeight = height / 2;
31337 var cornerRadius = getRoundRectangleRadius(width, height);
31338
31339 if (context.beginPath) {
31340 context.beginPath();
31341 } // Start at top middle
31342
31343
31344 context.moveTo(x, y - halfHeight);
31345 context.lineTo(x + halfWidth, y - halfHeight);
31346 context.lineTo(x + halfWidth, y);
31347 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
31348 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
31349 context.lineTo(x - halfWidth, y - halfHeight);
31350 context.lineTo(x, y - halfHeight);
31351 context.closePath();
31352 };
31353
31354 CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
31355 var halfWidth = width / 2;
31356 var halfHeight = height / 2;
31357 var cornerLength = getCutRectangleCornerLength();
31358
31359 if (context.beginPath) {
31360 context.beginPath();
31361 }
31362
31363 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
31364 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
31365 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
31366 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
31367 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
31368 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
31369 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
31370 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
31371 context.closePath();
31372 };
31373
31374 CRp$7.drawBarrelPath = function (context, x, y, width, height) {
31375 var halfWidth = width / 2;
31376 var halfHeight = height / 2;
31377 var xBegin = x - halfWidth;
31378 var xEnd = x + halfWidth;
31379 var yBegin = y - halfHeight;
31380 var yEnd = y + halfHeight;
31381 var barrelCurveConstants = getBarrelCurveConstants(width, height);
31382 var wOffset = barrelCurveConstants.widthOffset;
31383 var hOffset = barrelCurveConstants.heightOffset;
31384 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
31385
31386 if (context.beginPath) {
31387 context.beginPath();
31388 }
31389
31390 context.moveTo(xBegin, yBegin + hOffset);
31391 context.lineTo(xBegin, yEnd - hOffset);
31392 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
31393 context.lineTo(xEnd - wOffset, yEnd);
31394 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
31395 context.lineTo(xEnd, yBegin + hOffset);
31396 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
31397 context.lineTo(xBegin + wOffset, yBegin);
31398 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
31399 context.closePath();
31400 };
31401
31402 var sin0 = Math.sin(0);
31403 var cos0 = Math.cos(0);
31404 var sin = {};
31405 var cos = {};
31406 var ellipseStepSize = Math.PI / 40;
31407
31408 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31409 sin[i] = Math.sin(i);
31410 cos[i] = Math.cos(i);
31411 }
31412
31413 CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
31414 if (context.beginPath) {
31415 context.beginPath();
31416 }
31417
31418 if (context.ellipse) {
31419 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
31420 } else {
31421 var xPos, yPos;
31422 var rw = width / 2;
31423 var rh = height / 2;
31424
31425 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31426 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
31427 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
31428
31429 if (i === 0) {
31430 context.moveTo(xPos, yPos);
31431 } else {
31432 context.lineTo(xPos, yPos);
31433 }
31434 }
31435 }
31436
31437 context.closePath();
31438 };
31439
31440 /* global atob, ArrayBuffer, Uint8Array, Blob */
31441 var CRp$8 = {};
31442
31443 CRp$8.createBuffer = function (w, h) {
31444 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
31445
31446 buffer.width = w;
31447 buffer.height = h;
31448 return [buffer, buffer.getContext('2d')];
31449 };
31450
31451 CRp$8.bufferCanvasImage = function (options) {
31452 var cy = this.cy;
31453 var eles = cy.mutableElements();
31454 var bb = eles.boundingBox();
31455 var ctrRect = this.findContainerClientCoords();
31456 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31457 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31458 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
31459 var pxRatio = this.getPixelRatio();
31460 var scale = 1;
31461
31462 if (options.scale !== undefined) {
31463 width *= options.scale;
31464 height *= options.scale;
31465 scale = options.scale;
31466 } else if (specdMaxDims) {
31467 var maxScaleW = Infinity;
31468 var maxScaleH = Infinity;
31469
31470 if (number(options.maxWidth)) {
31471 maxScaleW = scale * options.maxWidth / width;
31472 }
31473
31474 if (number(options.maxHeight)) {
31475 maxScaleH = scale * options.maxHeight / height;
31476 }
31477
31478 scale = Math.min(maxScaleW, maxScaleH);
31479 width *= scale;
31480 height *= scale;
31481 }
31482
31483 if (!specdMaxDims) {
31484 width *= pxRatio;
31485 height *= pxRatio;
31486 scale *= pxRatio;
31487 }
31488
31489 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31490
31491 buffCanvas.width = width;
31492 buffCanvas.height = height;
31493 buffCanvas.style.width = width + 'px';
31494 buffCanvas.style.height = height + 'px';
31495 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31496
31497 if (width > 0 && height > 0) {
31498 buffCxt.clearRect(0, 0, width, height);
31499 buffCxt.globalCompositeOperation = 'source-over';
31500 var zsortedEles = this.getCachedZSortedEles();
31501
31502 if (options.full) {
31503 // draw the full bounds of the graph
31504 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31505 buffCxt.scale(scale, scale);
31506 this.drawElements(buffCxt, zsortedEles);
31507 buffCxt.scale(1 / scale, 1 / scale);
31508 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31509 } else {
31510 // draw the current view
31511 var pan = cy.pan();
31512 var translation = {
31513 x: pan.x * scale,
31514 y: pan.y * scale
31515 };
31516 scale *= cy.zoom();
31517 buffCxt.translate(translation.x, translation.y);
31518 buffCxt.scale(scale, scale);
31519 this.drawElements(buffCxt, zsortedEles);
31520 buffCxt.scale(1 / scale, 1 / scale);
31521 buffCxt.translate(-translation.x, -translation.y);
31522 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31523
31524
31525 if (options.bg) {
31526 buffCxt.globalCompositeOperation = 'destination-over';
31527 buffCxt.fillStyle = options.bg;
31528 buffCxt.rect(0, 0, width, height);
31529 buffCxt.fill();
31530 }
31531 }
31532
31533 return buffCanvas;
31534 };
31535
31536 function b64ToBlob(b64, mimeType) {
31537 var bytes = atob(b64);
31538 var buff = new ArrayBuffer(bytes.length);
31539 var buffUint8 = new Uint8Array(buff);
31540
31541 for (var i = 0; i < bytes.length; i++) {
31542 buffUint8[i] = bytes.charCodeAt(i);
31543 }
31544
31545 return new Blob([buff], {
31546 type: mimeType
31547 });
31548 }
31549
31550 function b64UriToB64(b64uri) {
31551 var i = b64uri.indexOf(',');
31552 return b64uri.substr(i + 1);
31553 }
31554
31555 function output(options, canvas, mimeType) {
31556 var getB64Uri = function getB64Uri() {
31557 return canvas.toDataURL(mimeType, options.quality);
31558 };
31559
31560 switch (options.output) {
31561 case 'blob-promise':
31562 return new Promise$1(function (resolve, reject) {
31563 try {
31564 canvas.toBlob(function (blob) {
31565 if (blob != null) {
31566 resolve(blob);
31567 } else {
31568 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31569 }
31570 }, mimeType, options.quality);
31571 } catch (err) {
31572 reject(err);
31573 }
31574 });
31575
31576 case 'blob':
31577 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31578
31579 case 'base64':
31580 return b64UriToB64(getB64Uri());
31581
31582 case 'base64uri':
31583 default:
31584 return getB64Uri();
31585 }
31586 }
31587
31588 CRp$8.png = function (options) {
31589 return output(options, this.bufferCanvasImage(options), 'image/png');
31590 };
31591
31592 CRp$8.jpg = function (options) {
31593 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31594 };
31595
31596 var CRp$9 = {};
31597
31598 CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31599 switch (name) {
31600 case 'ellipse':
31601 return this.drawEllipsePath(context, centerX, centerY, width, height);
31602
31603 case 'polygon':
31604 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31605
31606 case 'round-polygon':
31607 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31608
31609 case 'roundrectangle':
31610 case 'round-rectangle':
31611 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31612
31613 case 'cutrectangle':
31614 case 'cut-rectangle':
31615 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31616
31617 case 'bottomroundrectangle':
31618 case 'bottom-round-rectangle':
31619 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31620
31621 case 'barrel':
31622 return this.drawBarrelPath(context, centerX, centerY, width, height);
31623 }
31624 };
31625
31626 var CR = CanvasRenderer;
31627 var CRp$a = CanvasRenderer.prototype;
31628 CRp$a.CANVAS_LAYERS = 3; //
31629
31630 CRp$a.SELECT_BOX = 0;
31631 CRp$a.DRAG = 1;
31632 CRp$a.NODE = 2;
31633 CRp$a.BUFFER_COUNT = 3; //
31634
31635 CRp$a.TEXTURE_BUFFER = 0;
31636 CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
31637 CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
31638
31639 function CanvasRenderer(options) {
31640 var r = this;
31641 r.data = {
31642 canvases: new Array(CRp$a.CANVAS_LAYERS),
31643 contexts: new Array(CRp$a.CANVAS_LAYERS),
31644 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
31645 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
31646 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
31647 };
31648 var tapHlOffAttr = '-webkit-tap-highlight-color';
31649 var tapHlOffStyle = 'rgba(0,0,0,0)';
31650 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31651
31652 var containerStyle = r.data.canvasContainer.style;
31653 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31654 containerStyle.position = 'relative';
31655 containerStyle.zIndex = '0';
31656 containerStyle.overflow = 'hidden';
31657 var container = options.cy.container();
31658 container.appendChild(r.data.canvasContainer);
31659 container.style[tapHlOffAttr] = tapHlOffStyle;
31660 var styleMap = {
31661 '-webkit-user-select': 'none',
31662 '-moz-user-select': '-moz-none',
31663 'user-select': 'none',
31664 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31665 'outline-style': 'none'
31666 };
31667
31668 if (ms()) {
31669 styleMap['-ms-touch-action'] = 'none';
31670 styleMap['touch-action'] = 'none';
31671 }
31672
31673 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
31674 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31675
31676 r.data.contexts[i] = canvas.getContext('2d');
31677 Object.keys(styleMap).forEach(function (k) {
31678 canvas.style[k] = styleMap[k];
31679 });
31680 canvas.style.position = 'absolute';
31681 canvas.setAttribute('data-id', 'layer' + i);
31682 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
31683 r.data.canvasContainer.appendChild(canvas);
31684 r.data.canvasNeedsRedraw[i] = false;
31685 }
31686
31687 r.data.topCanvas = r.data.canvases[0];
31688 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
31689 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
31690 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
31691
31692 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
31693 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31694
31695 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31696 r.data.bufferCanvases[i].style.position = 'absolute';
31697 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31698 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31699 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31700 }
31701
31702 r.pathsEnabled = true;
31703 var emptyBb = makeBoundingBox();
31704
31705 var getBoxCenter = function getBoxCenter(bb) {
31706 return {
31707 x: (bb.x1 + bb.x2) / 2,
31708 y: (bb.y1 + bb.y2) / 2
31709 };
31710 };
31711
31712 var getCenterOffset = function getCenterOffset(bb) {
31713 return {
31714 x: -bb.w / 2,
31715 y: -bb.h / 2
31716 };
31717 };
31718
31719 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31720 var _p = ele[0]._private;
31721 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31722 return !same;
31723 };
31724
31725 var getStyleKey = function getStyleKey(ele) {
31726 return ele[0]._private.nodeKey;
31727 };
31728
31729 var getLabelKey = function getLabelKey(ele) {
31730 return ele[0]._private.labelStyleKey;
31731 };
31732
31733 var getSourceLabelKey = function getSourceLabelKey(ele) {
31734 return ele[0]._private.sourceLabelStyleKey;
31735 };
31736
31737 var getTargetLabelKey = function getTargetLabelKey(ele) {
31738 return ele[0]._private.targetLabelStyleKey;
31739 };
31740
31741 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31742 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31743 };
31744
31745 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31746 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31747 };
31748
31749 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31750 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31751 };
31752
31753 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31754 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31755 };
31756
31757 var getElementBox = function getElementBox(ele) {
31758 ele.boundingBox();
31759 return ele[0]._private.bodyBounds;
31760 };
31761
31762 var getLabelBox = function getLabelBox(ele) {
31763 ele.boundingBox();
31764 return ele[0]._private.labelBounds.main || emptyBb;
31765 };
31766
31767 var getSourceLabelBox = function getSourceLabelBox(ele) {
31768 ele.boundingBox();
31769 return ele[0]._private.labelBounds.source || emptyBb;
31770 };
31771
31772 var getTargetLabelBox = function getTargetLabelBox(ele) {
31773 ele.boundingBox();
31774 return ele[0]._private.labelBounds.target || emptyBb;
31775 };
31776
31777 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31778 return scaledLabelShown;
31779 };
31780
31781 var getElementRotationPoint = function getElementRotationPoint(ele) {
31782 return getBoxCenter(getElementBox(ele));
31783 };
31784
31785 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31786 var pre = prefix ? prefix + '-' : '';
31787 return {
31788 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31789 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31790 };
31791 };
31792
31793 var getRsPt = function getRsPt(ele, x, y) {
31794 var rs = ele[0]._private.rscratch;
31795 return {
31796 x: rs[x],
31797 y: rs[y]
31798 };
31799 };
31800
31801 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31802 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31803 };
31804
31805 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31806 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31807 };
31808
31809 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31810 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31811 };
31812
31813 var getElementRotationOffset = function getElementRotationOffset(ele) {
31814 return getCenterOffset(getElementBox(ele));
31815 };
31816
31817 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31818 return getCenterOffset(getSourceLabelBox(ele));
31819 };
31820
31821 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31822 return getCenterOffset(getTargetLabelBox(ele));
31823 };
31824
31825 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31826 var bb = getLabelBox(ele);
31827 var p = getCenterOffset(getLabelBox(ele));
31828
31829 if (ele.isNode()) {
31830 switch (ele.pstyle('text-halign').value) {
31831 case 'left':
31832 p.x = -bb.w;
31833 break;
31834
31835 case 'right':
31836 p.x = 0;
31837 break;
31838 }
31839
31840 switch (ele.pstyle('text-valign').value) {
31841 case 'top':
31842 p.y = -bb.h;
31843 break;
31844
31845 case 'bottom':
31846 p.y = 0;
31847 break;
31848 }
31849 }
31850
31851 return p;
31852 };
31853
31854 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31855 getKey: getStyleKey,
31856 doesEleInvalidateKey: backgroundTimestampHasChanged,
31857 drawElement: drawElement,
31858 getBoundingBox: getElementBox,
31859 getRotationPoint: getElementRotationPoint,
31860 getRotationOffset: getElementRotationOffset,
31861 allowEdgeTxrCaching: false,
31862 allowParentTxrCaching: false
31863 });
31864 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31865 getKey: getLabelKey,
31866 drawElement: drawLabel,
31867 getBoundingBox: getLabelBox,
31868 getRotationPoint: getLabelRotationPoint,
31869 getRotationOffset: getLabelRotationOffset,
31870 isVisible: isLabelVisibleAtScale
31871 });
31872 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31873 getKey: getSourceLabelKey,
31874 drawElement: drawSourceLabel,
31875 getBoundingBox: getSourceLabelBox,
31876 getRotationPoint: getSourceLabelRotationPoint,
31877 getRotationOffset: getSourceLabelRotationOffset,
31878 isVisible: isLabelVisibleAtScale
31879 });
31880 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31881 getKey: getTargetLabelKey,
31882 drawElement: drawTargetLabel,
31883 getBoundingBox: getTargetLabelBox,
31884 getRotationPoint: getTargetLabelRotationPoint,
31885 getRotationOffset: getTargetLabelRotationOffset,
31886 isVisible: isLabelVisibleAtScale
31887 });
31888 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31889 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31890 // each cache should check for sub-key diff to see that the update affects that cache particularly
31891 eleTxrCache.invalidateElements(eles);
31892 lblTxrCache.invalidateElements(eles);
31893 slbTxrCache.invalidateElements(eles);
31894 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31895
31896 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31897
31898 for (var _i = 0; _i < eles.length; _i++) {
31899 var _p = eles[_i]._private;
31900 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31901 }
31902 });
31903
31904 var refineInLayers = function refineInLayers(reqs) {
31905 for (var i = 0; i < reqs.length; i++) {
31906 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31907 }
31908 };
31909
31910 eleTxrCache.onDequeue(refineInLayers);
31911 lblTxrCache.onDequeue(refineInLayers);
31912 slbTxrCache.onDequeue(refineInLayers);
31913 tlbTxrCache.onDequeue(refineInLayers);
31914 }
31915
31916 CRp$a.redrawHint = function (group, bool) {
31917 var r = this;
31918
31919 switch (group) {
31920 case 'eles':
31921 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31922 break;
31923
31924 case 'drag':
31925 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31926 break;
31927
31928 case 'select':
31929 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31930 break;
31931 }
31932 }; // whether to use Path2D caching for drawing
31933
31934
31935 var pathsImpld = typeof Path2D !== 'undefined';
31936
31937 CRp$a.path2dEnabled = function (on) {
31938 if (on === undefined) {
31939 return this.pathsEnabled;
31940 }
31941
31942 this.pathsEnabled = on ? true : false;
31943 };
31944
31945 CRp$a.usePaths = function () {
31946 return pathsImpld && this.pathsEnabled;
31947 };
31948
31949 CRp$a.setImgSmoothing = function (context, bool) {
31950 if (context.imageSmoothingEnabled != null) {
31951 context.imageSmoothingEnabled = bool;
31952 } else {
31953 context.webkitImageSmoothingEnabled = bool;
31954 context.mozImageSmoothingEnabled = bool;
31955 context.msImageSmoothingEnabled = bool;
31956 }
31957 };
31958
31959 CRp$a.getImgSmoothing = function (context) {
31960 if (context.imageSmoothingEnabled != null) {
31961 return context.imageSmoothingEnabled;
31962 } else {
31963 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31964 }
31965 };
31966
31967 CRp$a.makeOffscreenCanvas = function (width, height) {
31968 var canvas;
31969
31970 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31971 canvas = new OffscreenCanvas(width, height);
31972 } else {
31973 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31974
31975 canvas.width = width;
31976 canvas.height = height;
31977 }
31978
31979 return canvas;
31980 };
31981
31982 [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31983 extend(CRp$a, props);
31984 });
31985
31986 var renderer = [{
31987 name: 'null',
31988 impl: NullRenderer
31989 }, {
31990 name: 'base',
31991 impl: BR
31992 }, {
31993 name: 'canvas',
31994 impl: CR
31995 }];
31996
31997 var incExts = [{
31998 type: 'layout',
31999 extensions: layout
32000 }, {
32001 type: 'renderer',
32002 extensions: renderer
32003 }];
32004
32005 var extensions = {}; // registered modules for extensions, indexed by name
32006
32007 var modules = {};
32008
32009 function setExtension(type, name, registrant) {
32010 var ext = registrant;
32011
32012 var overrideErr = function overrideErr(field) {
32013 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
32014 };
32015
32016 if (type === 'core') {
32017 if (Core.prototype[name]) {
32018 return overrideErr(name);
32019 } else {
32020 Core.prototype[name] = registrant;
32021 }
32022 } else if (type === 'collection') {
32023 if (Collection.prototype[name]) {
32024 return overrideErr(name);
32025 } else {
32026 Collection.prototype[name] = registrant;
32027 }
32028 } else if (type === 'layout') {
32029 // fill in missing layout functions in the prototype
32030 var Layout = function Layout(options) {
32031 this.options = options;
32032 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
32033
32034 if (!plainObject(this._private)) {
32035 this._private = {};
32036 }
32037
32038 this._private.cy = options.cy;
32039 this._private.listeners = [];
32040 this.createEmitter();
32041 };
32042
32043 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
32044 var optLayoutFns = [];
32045
32046 for (var i = 0; i < optLayoutFns.length; i++) {
32047 var fnName = optLayoutFns[i];
32048
32049 layoutProto[fnName] = layoutProto[fnName] || function () {
32050 return this;
32051 };
32052 } // either .start() or .run() is defined, so autogen the other
32053
32054
32055 if (layoutProto.start && !layoutProto.run) {
32056 layoutProto.run = function () {
32057 this.start();
32058 return this;
32059 };
32060 } else if (!layoutProto.start && layoutProto.run) {
32061 layoutProto.start = function () {
32062 this.run();
32063 return this;
32064 };
32065 }
32066
32067 var regStop = registrant.prototype.stop;
32068
32069 layoutProto.stop = function () {
32070 var opts = this.options;
32071
32072 if (opts && opts.animate) {
32073 var anis = this.animations;
32074
32075 if (anis) {
32076 for (var _i = 0; _i < anis.length; _i++) {
32077 anis[_i].stop();
32078 }
32079 }
32080 }
32081
32082 if (regStop) {
32083 regStop.call(this);
32084 } else {
32085 this.emit('layoutstop');
32086 }
32087
32088 return this;
32089 };
32090
32091 if (!layoutProto.destroy) {
32092 layoutProto.destroy = function () {
32093 return this;
32094 };
32095 }
32096
32097 layoutProto.cy = function () {
32098 return this._private.cy;
32099 };
32100
32101 var getCy = function getCy(layout) {
32102 return layout._private.cy;
32103 };
32104
32105 var emitterOpts = {
32106 addEventFields: function addEventFields(layout, evt) {
32107 evt.layout = layout;
32108 evt.cy = getCy(layout);
32109 evt.target = layout;
32110 },
32111 bubble: function bubble() {
32112 return true;
32113 },
32114 parent: function parent(layout) {
32115 return getCy(layout);
32116 }
32117 };
32118 extend(layoutProto, {
32119 createEmitter: function createEmitter() {
32120 this._private.emitter = new Emitter(emitterOpts, this);
32121 return this;
32122 },
32123 emitter: function emitter() {
32124 return this._private.emitter;
32125 },
32126 on: function on(evt, cb) {
32127 this.emitter().on(evt, cb);
32128 return this;
32129 },
32130 one: function one(evt, cb) {
32131 this.emitter().one(evt, cb);
32132 return this;
32133 },
32134 once: function once(evt, cb) {
32135 this.emitter().one(evt, cb);
32136 return this;
32137 },
32138 removeListener: function removeListener(evt, cb) {
32139 this.emitter().removeListener(evt, cb);
32140 return this;
32141 },
32142 removeAllListeners: function removeAllListeners() {
32143 this.emitter().removeAllListeners();
32144 return this;
32145 },
32146 emit: function emit(evt, params) {
32147 this.emitter().emit(evt, params);
32148 return this;
32149 }
32150 });
32151 define$3.eventAliasesOn(layoutProto);
32152 ext = Layout; // replace with our wrapped layout
32153 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
32154 // user registered renderers inherit from base
32155 var BaseRenderer = getExtension('renderer', 'base');
32156 var bProto = BaseRenderer.prototype;
32157 var RegistrantRenderer = registrant;
32158 var rProto = registrant.prototype;
32159
32160 var Renderer = function Renderer() {
32161 BaseRenderer.apply(this, arguments);
32162 RegistrantRenderer.apply(this, arguments);
32163 };
32164
32165 var proto = Renderer.prototype;
32166
32167 for (var pName in bProto) {
32168 var pVal = bProto[pName];
32169 var existsInR = rProto[pName] != null;
32170
32171 if (existsInR) {
32172 return overrideErr(pName);
32173 }
32174
32175 proto[pName] = pVal; // take impl from base
32176 }
32177
32178 for (var _pName in rProto) {
32179 proto[_pName] = rProto[_pName]; // take impl from registrant
32180 }
32181
32182 bProto.clientFunctions.forEach(function (name) {
32183 proto[name] = proto[name] || function () {
32184 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
32185 };
32186 });
32187 ext = Renderer;
32188 }
32189
32190 return setMap({
32191 map: extensions,
32192 keys: [type, name],
32193 value: ext
32194 });
32195 }
32196
32197 function getExtension(type, name) {
32198 return getMap({
32199 map: extensions,
32200 keys: [type, name]
32201 });
32202 }
32203
32204 function setModule(type, name, moduleType, moduleName, registrant) {
32205 return setMap({
32206 map: modules,
32207 keys: [type, name, moduleType, moduleName],
32208 value: registrant
32209 });
32210 }
32211
32212 function getModule(type, name, moduleType, moduleName) {
32213 return getMap({
32214 map: modules,
32215 keys: [type, name, moduleType, moduleName]
32216 });
32217 }
32218
32219 var extension = function extension() {
32220 // e.g. extension('renderer', 'svg')
32221 if (arguments.length === 2) {
32222 return getExtension.apply(null, arguments);
32223 } // e.g. extension('renderer', 'svg', { ... })
32224 else if (arguments.length === 3) {
32225 return setExtension.apply(null, arguments);
32226 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
32227 else if (arguments.length === 4) {
32228 return getModule.apply(null, arguments);
32229 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
32230 else if (arguments.length === 5) {
32231 return setModule.apply(null, arguments);
32232 } else {
32233 error('Invalid extension access syntax');
32234 }
32235 }; // allows a core instance to access extensions internally
32236
32237
32238 Core.prototype.extension = extension; // included extensions
32239
32240 incExts.forEach(function (group) {
32241 group.extensions.forEach(function (ext) {
32242 setExtension(group.type, ext.name, ext.impl);
32243 });
32244 });
32245
32246 // (useful for init)
32247
32248 var Stylesheet = function Stylesheet() {
32249 if (!(this instanceof Stylesheet)) {
32250 return new Stylesheet();
32251 }
32252
32253 this.length = 0;
32254 };
32255
32256 var sheetfn = Stylesheet.prototype;
32257
32258 sheetfn.instanceString = function () {
32259 return 'stylesheet';
32260 }; // just store the selector to be parsed later
32261
32262
32263 sheetfn.selector = function (selector) {
32264 var i = this.length++;
32265 this[i] = {
32266 selector: selector,
32267 properties: []
32268 };
32269 return this; // chaining
32270 }; // just store the property to be parsed later
32271
32272
32273 sheetfn.css = function (name, value) {
32274 var i = this.length - 1;
32275
32276 if (string(name)) {
32277 this[i].properties.push({
32278 name: name,
32279 value: value
32280 });
32281 } else if (plainObject(name)) {
32282 var map = name;
32283 var propNames = Object.keys(map);
32284
32285 for (var j = 0; j < propNames.length; j++) {
32286 var key = propNames[j];
32287 var mapVal = map[key];
32288
32289 if (mapVal == null) {
32290 continue;
32291 }
32292
32293 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
32294
32295 if (prop == null) {
32296 continue;
32297 }
32298
32299 var _name = prop.name;
32300 var _value = mapVal;
32301 this[i].properties.push({
32302 name: _name,
32303 value: _value
32304 });
32305 }
32306 }
32307
32308 return this; // chaining
32309 };
32310
32311 sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
32312
32313 sheetfn.generateStyle = function (cy) {
32314 var style = new Style(cy);
32315 return this.appendToStyle(style);
32316 }; // append a dummy stylesheet object on a real style object
32317
32318
32319 sheetfn.appendToStyle = function (style) {
32320 for (var i = 0; i < this.length; i++) {
32321 var context = this[i];
32322 var selector = context.selector;
32323 var props = context.properties;
32324 style.selector(selector); // apply selector
32325
32326 for (var j = 0; j < props.length; j++) {
32327 var prop = props[j];
32328 style.css(prop.name, prop.value); // apply property
32329 }
32330 }
32331
32332 return style;
32333 };
32334
32335 var version = "3.16.0";
32336
32337 var cytoscape = function cytoscape(options) {
32338 // if no options specified, use default
32339 if (options === undefined) {
32340 options = {};
32341 } // create instance
32342
32343
32344 if (plainObject(options)) {
32345 return new Core(options);
32346 } // allow for registration of extensions
32347 else if (string(options)) {
32348 return extension.apply(extension, arguments);
32349 }
32350 }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
32351
32352
32353 cytoscape.use = function (ext) {
32354 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
32355
32356 args.unshift(cytoscape); // cytoscape is first arg to ext
32357
32358 ext.apply(null, args);
32359 return this;
32360 };
32361
32362 cytoscape.warnings = function (bool) {
32363 return warnings(bool);
32364 }; // replaced by build system
32365
32366
32367 cytoscape.version = version; // expose public apis (mostly for extensions)
32368
32369 cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
32370
32371 return cytoscape;
32372
32373})));