UNPKG

936 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2021, 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, oneCopy) {
1295 for (var i = arr.length - 1; i >= 0; i--) {
1296 if (arr[i] === ele) {
1297 arr.splice(i, 1);
1298
1299 if (oneCopy) {
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 should be done only when absolutely necessary. Try to use the stylesheet instead.');
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 cameFromEdge[wid] = e;
2496 }
2497 } // End of neighbors update
2498
2499 } // End of main loop
2500 // If we've reached here, then we've not reached our goal
2501
2502
2503 return {
2504 found: false,
2505 distance: undefined,
2506 path: undefined,
2507 steps: steps
2508 };
2509 }
2510 }; // elesfn
2511
2512 var floydWarshallDefaults = defaults({
2513 weight: function weight(edge) {
2514 return 1;
2515 },
2516 directed: false
2517 });
2518 var elesfn$4 = {
2519 // Implemented from pseudocode from wikipedia
2520 floydWarshall: function floydWarshall(options) {
2521 var cy = this.cy();
2522
2523 var _floydWarshallDefault = floydWarshallDefaults(options),
2524 weight = _floydWarshallDefault.weight,
2525 directed = _floydWarshallDefault.directed;
2526
2527 var weightFn = weight;
2528
2529 var _this$byGroup = this.byGroup(),
2530 nodes = _this$byGroup.nodes,
2531 edges = _this$byGroup.edges;
2532
2533 var N = nodes.length;
2534 var Nsq = N * N;
2535
2536 var indexOf = function indexOf(node) {
2537 return nodes.indexOf(node);
2538 };
2539
2540 var atIndex = function atIndex(i) {
2541 return nodes[i];
2542 }; // Initialize distance matrix
2543
2544
2545 var dist = new Array(Nsq);
2546
2547 for (var n = 0; n < Nsq; n++) {
2548 var j = n % N;
2549 var i = (n - j) / N;
2550
2551 if (i === j) {
2552 dist[n] = 0;
2553 } else {
2554 dist[n] = Infinity;
2555 }
2556 } // Initialize matrix used for path reconstruction
2557 // Initialize distance matrix
2558
2559
2560 var next = new Array(Nsq);
2561 var edgeNext = new Array(Nsq); // Process edges
2562
2563 for (var _i = 0; _i < edges.length; _i++) {
2564 var edge = edges[_i];
2565 var src = edge.source()[0];
2566 var tgt = edge.target()[0];
2567
2568 if (src === tgt) {
2569 continue;
2570 } // exclude loops
2571
2572
2573 var s = indexOf(src);
2574 var t = indexOf(tgt);
2575 var st = s * N + t; // source to target index
2576
2577 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
2578
2579
2580 if (dist[st] > _weight) {
2581 dist[st] = _weight;
2582 next[st] = t;
2583 edgeNext[st] = edge;
2584 } // If undirected graph, process 'reversed' edge
2585
2586
2587 if (!directed) {
2588 var ts = t * N + s; // target to source index
2589
2590 if (!directed && dist[ts] > _weight) {
2591 dist[ts] = _weight;
2592 next[ts] = s;
2593 edgeNext[ts] = edge;
2594 }
2595 }
2596 } // Main loop
2597
2598
2599 for (var k = 0; k < N; k++) {
2600 for (var _i2 = 0; _i2 < N; _i2++) {
2601 var ik = _i2 * N + k;
2602
2603 for (var _j = 0; _j < N; _j++) {
2604 var ij = _i2 * N + _j;
2605 var kj = k * N + _j;
2606
2607 if (dist[ik] + dist[kj] < dist[ij]) {
2608 dist[ij] = dist[ik] + dist[kj];
2609 next[ij] = next[ik];
2610 }
2611 }
2612 }
2613 }
2614
2615 var getArgEle = function getArgEle(ele) {
2616 return (string(ele) ? cy.filter(ele) : ele)[0];
2617 };
2618
2619 var indexOfArgEle = function indexOfArgEle(ele) {
2620 return indexOf(getArgEle(ele));
2621 };
2622
2623 var res = {
2624 distance: function distance(from, to) {
2625 var i = indexOfArgEle(from);
2626 var j = indexOfArgEle(to);
2627 return dist[i * N + j];
2628 },
2629 path: function path(from, to) {
2630 var i = indexOfArgEle(from);
2631 var j = indexOfArgEle(to);
2632 var fromNode = atIndex(i);
2633
2634 if (i === j) {
2635 return fromNode.collection();
2636 }
2637
2638 if (next[i * N + j] == null) {
2639 return cy.collection();
2640 }
2641
2642 var path = cy.collection();
2643 var prev = i;
2644 var edge;
2645 path.merge(fromNode);
2646
2647 while (i !== j) {
2648 prev = i;
2649 i = next[i * N + j];
2650 edge = edgeNext[prev * N + i];
2651 path.merge(edge);
2652 path.merge(atIndex(i));
2653 }
2654
2655 return path;
2656 }
2657 };
2658 return res;
2659 } // floydWarshall
2660
2661 }; // elesfn
2662
2663 var bellmanFordDefaults = defaults({
2664 weight: function weight(edge) {
2665 return 1;
2666 },
2667 directed: false,
2668 root: null
2669 });
2670 var elesfn$5 = {
2671 // Implemented from pseudocode from wikipedia
2672 bellmanFord: function bellmanFord(options) {
2673 var _this = this;
2674
2675 var _bellmanFordDefaults = bellmanFordDefaults(options),
2676 weight = _bellmanFordDefaults.weight,
2677 directed = _bellmanFordDefaults.directed,
2678 root = _bellmanFordDefaults.root;
2679
2680 var weightFn = weight;
2681 var eles = this;
2682 var cy = this.cy();
2683
2684 var _this$byGroup = this.byGroup(),
2685 edges = _this$byGroup.edges,
2686 nodes = _this$byGroup.nodes;
2687
2688 var numNodes = nodes.length;
2689 var infoMap = new Map$1();
2690 var hasNegativeWeightCycle = false;
2691 var negativeWeightCycles = [];
2692 root = cy.collection(root)[0]; // in case selector passed
2693
2694 edges.unmergeBy(function (edge) {
2695 return edge.isLoop();
2696 });
2697 var numEdges = edges.length;
2698
2699 var getInfo = function getInfo(node) {
2700 var obj = infoMap.get(node.id());
2701
2702 if (!obj) {
2703 obj = {};
2704 infoMap.set(node.id(), obj);
2705 }
2706
2707 return obj;
2708 };
2709
2710 var getNodeFromTo = function getNodeFromTo(to) {
2711 return (string(to) ? cy.$(to) : to)[0];
2712 };
2713
2714 var distanceTo = function distanceTo(to) {
2715 return getInfo(getNodeFromTo(to)).dist;
2716 };
2717
2718 var pathTo = function pathTo(to) {
2719 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2720 var end = getNodeFromTo(to);
2721 var path = [];
2722 var node = end;
2723
2724 for (;;) {
2725 if (node == null) {
2726 return _this.spawn();
2727 }
2728
2729 var _getInfo = getInfo(node),
2730 edge = _getInfo.edge,
2731 pred = _getInfo.pred;
2732
2733 path.unshift(node[0]);
2734
2735 if (node.same(thisStart) && path.length > 0) {
2736 break;
2737 }
2738
2739 if (edge != null) {
2740 path.unshift(edge);
2741 }
2742
2743 node = pred;
2744 }
2745
2746 return eles.spawn(path);
2747 }; // Initializations { dist, pred, edge }
2748
2749
2750 for (var i = 0; i < numNodes; i++) {
2751 var node = nodes[i];
2752 var info = getInfo(node);
2753
2754 if (node.same(root)) {
2755 info.dist = 0;
2756 } else {
2757 info.dist = Infinity;
2758 }
2759
2760 info.pred = null;
2761 info.edge = null;
2762 } // Edges relaxation
2763
2764
2765 var replacedEdge = false;
2766
2767 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2768 var dist = info1.dist + weight;
2769
2770 if (dist < info2.dist && !edge.same(info1.edge)) {
2771 info2.dist = dist;
2772 info2.pred = node1;
2773 info2.edge = edge;
2774 replacedEdge = true;
2775 }
2776 };
2777
2778 for (var _i = 1; _i < numNodes; _i++) {
2779 replacedEdge = false;
2780
2781 for (var e = 0; e < numEdges; e++) {
2782 var edge = edges[e];
2783 var src = edge.source();
2784 var tgt = edge.target();
2785
2786 var _weight = weightFn(edge);
2787
2788 var srcInfo = getInfo(src);
2789 var tgtInfo = getInfo(tgt);
2790 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2791
2792 if (!directed) {
2793 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2794 }
2795 }
2796
2797 if (!replacedEdge) {
2798 break;
2799 }
2800 }
2801
2802 if (replacedEdge) {
2803 // Check for negative weight cycles
2804 for (var _e = 0; _e < numEdges; _e++) {
2805 var _edge = edges[_e];
2806
2807 var _src = _edge.source();
2808
2809 var _tgt = _edge.target();
2810
2811 var _weight2 = weightFn(_edge);
2812
2813 var srcDist = getInfo(_src).dist;
2814 var tgtDist = getInfo(_tgt).dist;
2815
2816 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2817 warn('Graph contains a negative weight cycle for Bellman-Ford');
2818 hasNegativeWeightCycle = true;
2819 break;
2820 }
2821 }
2822 }
2823
2824 return {
2825 distanceTo: distanceTo,
2826 pathTo: pathTo,
2827 hasNegativeWeightCycle: hasNegativeWeightCycle,
2828 negativeWeightCycles: negativeWeightCycles
2829 };
2830 } // bellmanFord
2831
2832 }; // elesfn
2833
2834 var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2835 // Updates the remaining edge lists
2836 // Receives as a paramater the edge which causes the collapse
2837
2838 var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2839 if (remainingEdges.length === 0) {
2840 error("Karger-Stein must be run on a connected (sub)graph");
2841 }
2842
2843 var edgeInfo = remainingEdges[edgeIndex];
2844 var sourceIn = edgeInfo[1];
2845 var targetIn = edgeInfo[2];
2846 var partition1 = nodeMap[sourceIn];
2847 var partition2 = nodeMap[targetIn];
2848 var newEdges = remainingEdges; // re-use array
2849 // Delete all edges between partition1 and partition2
2850
2851 for (var i = newEdges.length - 1; i >= 0; i--) {
2852 var edge = newEdges[i];
2853 var src = edge[1];
2854 var tgt = edge[2];
2855
2856 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2857 newEdges.splice(i, 1);
2858 }
2859 } // All edges pointing to partition2 should now point to partition1
2860
2861
2862 for (var _i = 0; _i < newEdges.length; _i++) {
2863 var _edge = newEdges[_i];
2864
2865 if (_edge[1] === partition2) {
2866 // Check source
2867 newEdges[_i] = _edge.slice(); // copy
2868
2869 newEdges[_i][1] = partition1;
2870 } else if (_edge[2] === partition2) {
2871 // Check target
2872 newEdges[_i] = _edge.slice(); // copy
2873
2874 newEdges[_i][2] = partition1;
2875 }
2876 } // Move all nodes from partition2 to partition1
2877
2878
2879 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2880 if (nodeMap[_i2] === partition2) {
2881 nodeMap[_i2] = partition1;
2882 }
2883 }
2884
2885 return newEdges;
2886 }; // Contracts a graph until we reach a certain number of meta nodes
2887
2888
2889 var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2890 while (size > sizeLimit) {
2891 // Choose an edge randomly
2892 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2893
2894 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2895 size--;
2896 }
2897
2898 return remainingEdges;
2899 };
2900
2901 var elesfn$6 = {
2902 // Computes the minimum cut of an undirected graph
2903 // Returns the correct answer with high probability
2904 kargerStein: function kargerStein() {
2905 var _this = this;
2906
2907 var _this$byGroup = this.byGroup(),
2908 nodes = _this$byGroup.nodes,
2909 edges = _this$byGroup.edges;
2910
2911 edges.unmergeBy(function (edge) {
2912 return edge.isLoop();
2913 });
2914 var numNodes = nodes.length;
2915 var numEdges = edges.length;
2916 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2917 var stopSize = Math.floor(numNodes / sqrt2);
2918
2919 if (numNodes < 2) {
2920 error('At least 2 nodes are required for Karger-Stein algorithm');
2921 return undefined;
2922 } // Now store edge destination as indexes
2923 // Format for each edge (edge index, source node index, target node index)
2924
2925
2926 var edgeIndexes = [];
2927
2928 for (var i = 0; i < numEdges; i++) {
2929 var e = edges[i];
2930 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2931 } // We will store the best cut found here
2932
2933
2934 var minCutSize = Infinity;
2935 var minCutEdgeIndexes = [];
2936 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2937
2938 var metaNodeMap = new Array(numNodes);
2939 var metaNodeMap2 = new Array(numNodes);
2940
2941 var copyNodesMap = function copyNodesMap(from, to) {
2942 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2943 to[_i3] = from[_i3];
2944 }
2945 }; // Main loop
2946
2947
2948 for (var iter = 0; iter <= numIter; iter++) {
2949 // Reset meta node partition
2950 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2951 metaNodeMap[_i4] = _i4;
2952 } // Contract until stop point (stopSize nodes)
2953
2954
2955 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2956 var edgesState2 = edgesState.slice(); // copy
2957 // Create a copy of the colapsed nodes state
2958
2959 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2960
2961 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2962 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2963
2964 if (res1.length <= res2.length && res1.length < minCutSize) {
2965 minCutSize = res1.length;
2966 minCutEdgeIndexes = res1;
2967 copyNodesMap(metaNodeMap, minCutNodeMap);
2968 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2969 minCutSize = res2.length;
2970 minCutEdgeIndexes = res2;
2971 copyNodesMap(metaNodeMap2, minCutNodeMap);
2972 }
2973 } // end of main loop
2974 // Construct result
2975
2976
2977 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2978 return edges[e[0]];
2979 }));
2980 var partition1 = this.spawn();
2981 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2982
2983 var witnessNodePartition = minCutNodeMap[0];
2984
2985 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2986 var partitionId = minCutNodeMap[_i5];
2987 var node = nodes[_i5];
2988
2989 if (partitionId === witnessNodePartition) {
2990 partition1.merge(node);
2991 } else {
2992 partition2.merge(node);
2993 }
2994 } // construct components corresponding to each disjoint subset of nodes
2995
2996
2997 var constructComponent = function constructComponent(subset) {
2998 var component = _this.spawn();
2999
3000 subset.forEach(function (node) {
3001 component.merge(node);
3002 node.connectedEdges().forEach(function (edge) {
3003 // ensure edge is within calling collection and edge is not in cut
3004 if (_this.contains(edge) && !cut.contains(edge)) {
3005 component.merge(edge);
3006 }
3007 });
3008 });
3009 return component;
3010 };
3011
3012 var components = [constructComponent(partition1), constructComponent(partition2)];
3013 var ret = {
3014 cut: cut,
3015 components: components,
3016 // n.b. partitions are included to be compatible with the old api spec
3017 // (could be removed in a future major version)
3018 partition1: partition1,
3019 partition2: partition2
3020 };
3021 return ret;
3022 }
3023 }; // elesfn
3024
3025 var copyPosition = function copyPosition(p) {
3026 return {
3027 x: p.x,
3028 y: p.y
3029 };
3030 };
3031 var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3032 return {
3033 x: p.x * zoom + pan.x,
3034 y: p.y * zoom + pan.y
3035 };
3036 };
3037 var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3038 return {
3039 x: (p.x - pan.x) / zoom,
3040 y: (p.y - pan.y) / zoom
3041 };
3042 };
3043 var array2point = function array2point(arr) {
3044 return {
3045 x: arr[0],
3046 y: arr[1]
3047 };
3048 };
3049 var min = function min(arr) {
3050 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3051 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3052 var min = Infinity;
3053
3054 for (var i = begin; i < end; i++) {
3055 var val = arr[i];
3056
3057 if (isFinite(val)) {
3058 min = Math.min(val, min);
3059 }
3060 }
3061
3062 return min;
3063 };
3064 var max = function max(arr) {
3065 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3066 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3067 var max = -Infinity;
3068
3069 for (var i = begin; i < end; i++) {
3070 var val = arr[i];
3071
3072 if (isFinite(val)) {
3073 max = Math.max(val, max);
3074 }
3075 }
3076
3077 return max;
3078 };
3079 var mean = function mean(arr) {
3080 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3081 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3082 var total = 0;
3083 var n = 0;
3084
3085 for (var i = begin; i < end; i++) {
3086 var val = arr[i];
3087
3088 if (isFinite(val)) {
3089 total += val;
3090 n++;
3091 }
3092 }
3093
3094 return total / n;
3095 };
3096 var median = function median(arr) {
3097 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3098 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3099 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3100 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3101 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3102
3103 if (copy) {
3104 arr = arr.slice(begin, end);
3105 } else {
3106 if (end < arr.length) {
3107 arr.splice(end, arr.length - end);
3108 }
3109
3110 if (begin > 0) {
3111 arr.splice(0, begin);
3112 }
3113 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3114
3115
3116 var off = 0; // offset from non-finite values
3117
3118 for (var i = arr.length - 1; i >= 0; i--) {
3119 var v = arr[i];
3120
3121 if (includeHoles) {
3122 if (!isFinite(v)) {
3123 arr[i] = -Infinity;
3124 off++;
3125 }
3126 } else {
3127 // just remove it if we don't want to consider holes
3128 arr.splice(i, 1);
3129 }
3130 }
3131
3132 if (sort) {
3133 arr.sort(function (a, b) {
3134 return a - b;
3135 }); // requires copy = true if you don't want to change the orig
3136 }
3137
3138 var len = arr.length;
3139 var mid = Math.floor(len / 2);
3140
3141 if (len % 2 !== 0) {
3142 return arr[mid + 1 + off];
3143 } else {
3144 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3145 }
3146 };
3147 var deg2rad = function deg2rad(deg) {
3148 return Math.PI * deg / 180;
3149 };
3150 var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3151 return Math.atan2(dispY, dispX) - Math.PI / 2;
3152 };
3153 var log2 = Math.log2 || function (n) {
3154 return Math.log(n) / Math.log(2);
3155 };
3156 var signum = function signum(x) {
3157 if (x > 0) {
3158 return 1;
3159 } else if (x < 0) {
3160 return -1;
3161 } else {
3162 return 0;
3163 }
3164 };
3165 var dist = function dist(p1, p2) {
3166 return Math.sqrt(sqdist(p1, p2));
3167 };
3168 var sqdist = function sqdist(p1, p2) {
3169 var dx = p2.x - p1.x;
3170 var dy = p2.y - p1.y;
3171 return dx * dx + dy * dy;
3172 };
3173 var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3174 var length = v.length; // First, get sum of all elements
3175
3176 var total = 0;
3177
3178 for (var i = 0; i < length; i++) {
3179 total += v[i];
3180 } // Now, divide each by the sum of all elements
3181
3182
3183 for (var _i = 0; _i < length; _i++) {
3184 v[_i] = v[_i] / total;
3185 }
3186
3187 return v;
3188 };
3189
3190 var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3191 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3192 };
3193 var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3194 return {
3195 x: qbezierAt(p0.x, p1.x, p2.x, t),
3196 y: qbezierAt(p0.y, p1.y, p2.y, t)
3197 };
3198 };
3199 var lineAt = function lineAt(p0, p1, t, d) {
3200 var vec = {
3201 x: p1.x - p0.x,
3202 y: p1.y - p0.y
3203 };
3204 var vecDist = dist(p0, p1);
3205 var normVec = {
3206 x: vec.x / vecDist,
3207 y: vec.y / vecDist
3208 };
3209 t = t == null ? 0 : t;
3210 d = d != null ? d : t * vecDist;
3211 return {
3212 x: p0.x + normVec.x * d,
3213 y: p0.y + normVec.y * d
3214 };
3215 };
3216 var bound = function bound(min, val, max) {
3217 return Math.max(min, Math.min(max, val));
3218 }; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3219
3220 var makeBoundingBox = function makeBoundingBox(bb) {
3221 if (bb == null) {
3222 return {
3223 x1: Infinity,
3224 y1: Infinity,
3225 x2: -Infinity,
3226 y2: -Infinity,
3227 w: 0,
3228 h: 0
3229 };
3230 } else if (bb.x1 != null && bb.y1 != null) {
3231 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3232 return {
3233 x1: bb.x1,
3234 y1: bb.y1,
3235 x2: bb.x2,
3236 y2: bb.y2,
3237 w: bb.x2 - bb.x1,
3238 h: bb.y2 - bb.y1
3239 };
3240 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3241 return {
3242 x1: bb.x1,
3243 y1: bb.y1,
3244 x2: bb.x1 + bb.w,
3245 y2: bb.y1 + bb.h,
3246 w: bb.w,
3247 h: bb.h
3248 };
3249 }
3250 }
3251 };
3252 var copyBoundingBox = function copyBoundingBox(bb) {
3253 return {
3254 x1: bb.x1,
3255 x2: bb.x2,
3256 w: bb.w,
3257 y1: bb.y1,
3258 y2: bb.y2,
3259 h: bb.h
3260 };
3261 };
3262 var clearBoundingBox = function clearBoundingBox(bb) {
3263 bb.x1 = Infinity;
3264 bb.y1 = Infinity;
3265 bb.x2 = -Infinity;
3266 bb.y2 = -Infinity;
3267 bb.w = 0;
3268 bb.h = 0;
3269 };
3270 var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3271 // update bb1 with bb2 bounds
3272 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3273 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3274 bb1.w = bb1.x2 - bb1.x1;
3275 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3276 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3277 bb1.h = bb1.y2 - bb1.y1;
3278 };
3279 var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3280 bb.x1 = Math.min(bb.x1, x);
3281 bb.x2 = Math.max(bb.x2, x);
3282 bb.w = bb.x2 - bb.x1;
3283 bb.y1 = Math.min(bb.y1, y);
3284 bb.y2 = Math.max(bb.y2, y);
3285 bb.h = bb.y2 - bb.y1;
3286 };
3287 var expandBoundingBox = function expandBoundingBox(bb) {
3288 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3289 bb.x1 -= padding;
3290 bb.x2 += padding;
3291 bb.y1 -= padding;
3292 bb.y2 += padding;
3293 bb.w = bb.x2 - bb.x1;
3294 bb.h = bb.y2 - bb.y1;
3295 return bb;
3296 };
3297 var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3298 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3299 var top, right, bottom, left;
3300
3301 if (padding.length === 1) {
3302 top = right = bottom = left = padding[0];
3303 } else if (padding.length === 2) {
3304 top = bottom = padding[0];
3305 left = right = padding[1];
3306 } else if (padding.length === 4) {
3307 var _padding = _slicedToArray(padding, 4);
3308
3309 top = _padding[0];
3310 right = _padding[1];
3311 bottom = _padding[2];
3312 left = _padding[3];
3313 }
3314
3315 bb.x1 -= left;
3316 bb.x2 += right;
3317 bb.y1 -= top;
3318 bb.y2 += bottom;
3319 bb.w = bb.x2 - bb.x1;
3320 bb.h = bb.y2 - bb.y1;
3321 return bb;
3322 };
3323
3324 var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3325 bb1.x1 = bb2.x1;
3326 bb1.y1 = bb2.y1;
3327 bb1.x2 = bb2.x2;
3328 bb1.y2 = bb2.y2;
3329 bb1.w = bb1.x2 - bb1.x1;
3330 bb1.h = bb1.y2 - bb1.y1;
3331 };
3332 var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3333 // case: one bb to right of other
3334 if (bb1.x1 > bb2.x2) {
3335 return false;
3336 }
3337
3338 if (bb2.x1 > bb1.x2) {
3339 return false;
3340 } // case: one bb to left of other
3341
3342
3343 if (bb1.x2 < bb2.x1) {
3344 return false;
3345 }
3346
3347 if (bb2.x2 < bb1.x1) {
3348 return false;
3349 } // case: one bb above other
3350
3351
3352 if (bb1.y2 < bb2.y1) {
3353 return false;
3354 }
3355
3356 if (bb2.y2 < bb1.y1) {
3357 return false;
3358 } // case: one bb below other
3359
3360
3361 if (bb1.y1 > bb2.y2) {
3362 return false;
3363 }
3364
3365 if (bb2.y1 > bb1.y2) {
3366 return false;
3367 } // otherwise, must have some overlap
3368
3369
3370 return true;
3371 };
3372 var inBoundingBox = function inBoundingBox(bb, x, y) {
3373 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3374 };
3375 var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3376 return inBoundingBox(bb, pt.x, pt.y);
3377 };
3378 var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3379 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3380 };
3381 var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3382 var cornerRadius = getRoundRectangleRadius(width, height);
3383 var halfWidth = width / 2;
3384 var halfHeight = height / 2; // Check intersections with straight line segments
3385
3386 var straightLineIntersections; // Top segment, left to right
3387
3388 {
3389 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3390 var topStartY = nodeY - halfHeight - padding;
3391 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3392 var topEndY = topStartY;
3393 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3394
3395 if (straightLineIntersections.length > 0) {
3396 return straightLineIntersections;
3397 }
3398 } // Right segment, top to bottom
3399
3400 {
3401 var rightStartX = nodeX + halfWidth + padding;
3402 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3403 var rightEndX = rightStartX;
3404 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3405 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3406
3407 if (straightLineIntersections.length > 0) {
3408 return straightLineIntersections;
3409 }
3410 } // Bottom segment, left to right
3411
3412 {
3413 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3414 var bottomStartY = nodeY + halfHeight + padding;
3415 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3416 var bottomEndY = bottomStartY;
3417 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3418
3419 if (straightLineIntersections.length > 0) {
3420 return straightLineIntersections;
3421 }
3422 } // Left segment, top to bottom
3423
3424 {
3425 var leftStartX = nodeX - halfWidth - padding;
3426 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3427 var leftEndX = leftStartX;
3428 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3429 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3430
3431 if (straightLineIntersections.length > 0) {
3432 return straightLineIntersections;
3433 }
3434 } // Check intersections with arc segments
3435
3436 var arcIntersections; // Top Left
3437
3438 {
3439 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3440 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3441 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3442
3443 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3444 return [arcIntersections[0], arcIntersections[1]];
3445 }
3446 } // Top Right
3447
3448 {
3449 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3450 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3451 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3452
3453 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3454 return [arcIntersections[0], arcIntersections[1]];
3455 }
3456 } // Bottom Right
3457
3458 {
3459 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3460 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3461 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3462
3463 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3464 return [arcIntersections[0], arcIntersections[1]];
3465 }
3466 } // Bottom Left
3467
3468 {
3469 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3470 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3471 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
3472
3473 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3474 return [arcIntersections[0], arcIntersections[1]];
3475 }
3476 }
3477 return []; // if nothing
3478 };
3479 var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3480 var t = tolerance;
3481 var x1 = Math.min(lx1, lx2);
3482 var x2 = Math.max(lx1, lx2);
3483 var y1 = Math.min(ly1, ly2);
3484 var y2 = Math.max(ly1, ly2);
3485 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3486 };
3487 var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3488 var bb = {
3489 x1: Math.min(x1, x3, x2) - tolerance,
3490 x2: Math.max(x1, x3, x2) + tolerance,
3491 y1: Math.min(y1, y3, y2) - tolerance,
3492 y2: Math.max(y1, y3, y2) + tolerance
3493 }; // if outside the rough bounding box for the bezier, then it can't be a hit
3494
3495 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3496 // console.log('bezier out of rough bb')
3497 return false;
3498 } else {
3499 // console.log('do more expensive check');
3500 return true;
3501 }
3502 };
3503 var solveQuadratic = function solveQuadratic(a, b, c, val) {
3504 c -= val;
3505 var r = b * b - 4 * a * c;
3506
3507 if (r < 0) {
3508 return [];
3509 }
3510
3511 var sqrtR = Math.sqrt(r);
3512 var denom = 2 * a;
3513 var root1 = (-b + sqrtR) / denom;
3514 var root2 = (-b - sqrtR) / denom;
3515 return [root1, root2];
3516 };
3517 var solveCubic = function solveCubic(a, b, c, d, result) {
3518 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3519 // r is the real component, i is the imaginary component
3520 // An implementation of the Cardano method from the year 1545
3521 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3522 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
3523
3524 if (a === 0) {
3525 a = epsilon;
3526 }
3527
3528 b /= a;
3529 c /= a;
3530 d /= a;
3531 var discriminant, q, r, dum1, s, t, term1, r13;
3532 q = (3.0 * c - b * b) / 9.0;
3533 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3534 r /= 54.0;
3535 discriminant = q * q * q + r * r;
3536 result[1] = 0;
3537 term1 = b / 3.0;
3538
3539 if (discriminant > 0) {
3540 s = r + Math.sqrt(discriminant);
3541 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3542 t = r - Math.sqrt(discriminant);
3543 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3544 result[0] = -term1 + s + t;
3545 term1 += (s + t) / 2.0;
3546 result[4] = result[2] = -term1;
3547 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3548 result[3] = term1;
3549 result[5] = -term1;
3550 return;
3551 }
3552
3553 result[5] = result[3] = 0;
3554
3555 if (discriminant === 0) {
3556 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3557 result[0] = -term1 + 2.0 * r13;
3558 result[4] = result[2] = -(r13 + term1);
3559 return;
3560 }
3561
3562 q = -q;
3563 dum1 = q * q * q;
3564 dum1 = Math.acos(r / Math.sqrt(dum1));
3565 r13 = 2.0 * Math.sqrt(q);
3566 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3567 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3568 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3569 return;
3570 };
3571 var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3572 // Find minimum distance by using the minimum of the distance
3573 // function between the given point and the curve
3574 // This gives the coefficients of the resulting cubic equation
3575 // whose roots tell us where a possible minimum is
3576 // (Coefficients are divided by 4)
3577 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;
3578 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;
3579 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;
3580 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);
3581
3582 var roots = []; // Use the cubic solving algorithm
3583
3584 solveCubic(a, b, c, d, roots);
3585 var zeroThreshold = 0.0000001;
3586 var params = [];
3587
3588 for (var index = 0; index < 6; index += 2) {
3589 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3590 params.push(roots[index]);
3591 }
3592 }
3593
3594 params.push(1.0);
3595 params.push(0.0);
3596 var minDistanceSquared = -1;
3597 var curX, curY, distSquared;
3598
3599 for (var i = 0; i < params.length; i++) {
3600 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3601 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3602 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3603
3604 if (minDistanceSquared >= 0) {
3605 if (distSquared < minDistanceSquared) {
3606 minDistanceSquared = distSquared;
3607 }
3608 } else {
3609 minDistanceSquared = distSquared;
3610 }
3611 }
3612
3613 return minDistanceSquared;
3614 };
3615 var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3616 var offset = [x - x1, y - y1];
3617 var line = [x2 - x1, y2 - y1];
3618 var lineSq = line[0] * line[0] + line[1] * line[1];
3619 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3620 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3621 var adjSq = dotProduct * dotProduct / lineSq;
3622
3623 if (dotProduct < 0) {
3624 return hypSq;
3625 }
3626
3627 if (adjSq > lineSq) {
3628 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3629 }
3630
3631 return hypSq - adjSq;
3632 };
3633 var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3634 var x1, y1, x2, y2;
3635 var y3; // Intersect with vertical line through (x, y)
3636
3637 var up = 0; // let down = 0;
3638
3639 for (var i = 0; i < points.length / 2; i++) {
3640 x1 = points[i * 2];
3641 y1 = points[i * 2 + 1];
3642
3643 if (i + 1 < points.length / 2) {
3644 x2 = points[(i + 1) * 2];
3645 y2 = points[(i + 1) * 2 + 1];
3646 } else {
3647 x2 = points[(i + 1 - points.length / 2) * 2];
3648 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3649 }
3650
3651 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3652 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3653
3654 if (y3 > y) {
3655 up++;
3656 } // if( y3 < y ){
3657 // down++;
3658 // }
3659
3660 } else {
3661 continue;
3662 }
3663 }
3664
3665 if (up % 2 === 0) {
3666 return false;
3667 } else {
3668 return true;
3669 }
3670 };
3671 var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3672 var transformedPoints = new Array(basePoints.length); // Gives negative angle
3673
3674 var angle;
3675
3676 if (direction[0] != null) {
3677 angle = Math.atan(direction[1] / direction[0]);
3678
3679 if (direction[0] < 0) {
3680 angle = angle + Math.PI / 2;
3681 } else {
3682 angle = -angle - Math.PI / 2;
3683 }
3684 } else {
3685 angle = direction;
3686 }
3687
3688 var cos = Math.cos(-angle);
3689 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
3690
3691 for (var i = 0; i < transformedPoints.length / 2; i++) {
3692 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3693 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3694 transformedPoints[i * 2] += centerX;
3695 transformedPoints[i * 2 + 1] += centerY;
3696 }
3697
3698 var points;
3699
3700 if (padding > 0) {
3701 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3702 points = joinLines(expandedLineSet);
3703 } else {
3704 points = transformedPoints;
3705 }
3706
3707 return pointInsidePolygonPoints(x, y, points);
3708 };
3709 var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
3710 var cutPolygonPoints = new Array(basePoints.length);
3711 var halfW = width / 2;
3712 var halfH = height / 2;
3713 var cornerRadius = getRoundPolygonRadius(width, height);
3714 var squaredCornerRadius = cornerRadius * cornerRadius;
3715
3716 for (var i = 0; i < basePoints.length / 4; i++) {
3717 var sourceUv = void 0,
3718 destUv = void 0;
3719
3720 if (i === 0) {
3721 sourceUv = basePoints.length - 2;
3722 } else {
3723 sourceUv = i * 4 - 2;
3724 }
3725
3726 destUv = i * 4 + 2;
3727 var px = centerX + halfW * basePoints[i * 4];
3728 var py = centerY + halfH * basePoints[i * 4 + 1];
3729 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3730 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3731 var cp0x = px - offset * basePoints[sourceUv];
3732 var cp0y = py - offset * basePoints[sourceUv + 1];
3733 var cp1x = px + offset * basePoints[destUv];
3734 var cp1y = py + offset * basePoints[destUv + 1];
3735 cutPolygonPoints[i * 4] = cp0x;
3736 cutPolygonPoints[i * 4 + 1] = cp0y;
3737 cutPolygonPoints[i * 4 + 2] = cp1x;
3738 cutPolygonPoints[i * 4 + 3] = cp1y;
3739 var orthx = basePoints[sourceUv + 1];
3740 var orthy = -basePoints[sourceUv];
3741 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3742
3743 if (cosAlpha < 0) {
3744 orthx *= -1;
3745 orthy *= -1;
3746 }
3747
3748 var cx = cp0x + orthx * cornerRadius;
3749 var cy = cp0y + orthy * cornerRadius;
3750 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
3751
3752 if (squaredDistance <= squaredCornerRadius) {
3753 return true;
3754 }
3755 }
3756
3757 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3758 };
3759 var joinLines = function joinLines(lineSet) {
3760 var vertices = new Array(lineSet.length / 2);
3761 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3762 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3763
3764 for (var i = 0; i < lineSet.length / 4; i++) {
3765 currentLineStartX = lineSet[i * 4];
3766 currentLineStartY = lineSet[i * 4 + 1];
3767 currentLineEndX = lineSet[i * 4 + 2];
3768 currentLineEndY = lineSet[i * 4 + 3];
3769
3770 if (i < lineSet.length / 4 - 1) {
3771 nextLineStartX = lineSet[(i + 1) * 4];
3772 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3773 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3774 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3775 } else {
3776 nextLineStartX = lineSet[0];
3777 nextLineStartY = lineSet[1];
3778 nextLineEndX = lineSet[2];
3779 nextLineEndY = lineSet[3];
3780 }
3781
3782 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3783 vertices[i * 2] = intersection[0];
3784 vertices[i * 2 + 1] = intersection[1];
3785 }
3786
3787 return vertices;
3788 };
3789 var expandPolygon = function expandPolygon(points, pad) {
3790 var expandedLineSet = new Array(points.length * 2);
3791 var currentPointX, currentPointY, nextPointX, nextPointY;
3792
3793 for (var i = 0; i < points.length / 2; i++) {
3794 currentPointX = points[i * 2];
3795 currentPointY = points[i * 2 + 1];
3796
3797 if (i < points.length / 2 - 1) {
3798 nextPointX = points[(i + 1) * 2];
3799 nextPointY = points[(i + 1) * 2 + 1];
3800 } else {
3801 nextPointX = points[0];
3802 nextPointY = points[1];
3803 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3804 // Assume CCW polygon winding
3805
3806
3807 var offsetX = nextPointY - currentPointY;
3808 var offsetY = -(nextPointX - currentPointX); // Normalize
3809
3810 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3811 var normalizedOffsetX = offsetX / offsetLength;
3812 var normalizedOffsetY = offsetY / offsetLength;
3813 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3814 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3815 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3816 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3817 }
3818
3819 return expandedLineSet;
3820 };
3821 var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3822 var dispX = centerX - x;
3823 var dispY = centerY - y;
3824 dispX /= ellipseWradius;
3825 dispY /= ellipseHradius;
3826 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3827 var newLength = len - 1;
3828
3829 if (newLength < 0) {
3830 return [];
3831 }
3832
3833 var lenProportion = newLength / len;
3834 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3835 };
3836 var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3837 x -= centerX;
3838 y -= centerY;
3839 x /= width / 2 + padding;
3840 y /= height / 2 + padding;
3841 return x * x + y * y <= 1;
3842 }; // Returns intersections of increasing distance from line's start point
3843
3844 var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3845 // Calculate d, direction vector of line
3846 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3847
3848 var f = [x1 - centerX, y1 - centerY];
3849 var a = d[0] * d[0] + d[1] * d[1];
3850 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3851 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3852 var discriminant = b * b - 4 * a * c;
3853
3854 if (discriminant < 0) {
3855 return [];
3856 }
3857
3858 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3859 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3860 var tMin = Math.min(t1, t2);
3861 var tMax = Math.max(t1, t2);
3862 var inRangeParams = [];
3863
3864 if (tMin >= 0 && tMin <= 1) {
3865 inRangeParams.push(tMin);
3866 }
3867
3868 if (tMax >= 0 && tMax <= 1) {
3869 inRangeParams.push(tMax);
3870 }
3871
3872 if (inRangeParams.length === 0) {
3873 return [];
3874 }
3875
3876 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3877 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3878
3879 if (inRangeParams.length > 1) {
3880 if (inRangeParams[0] == inRangeParams[1]) {
3881 return [nearIntersectionX, nearIntersectionY];
3882 } else {
3883 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3884 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3885 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3886 }
3887 } else {
3888 return [nearIntersectionX, nearIntersectionY];
3889 }
3890 };
3891 var midOfThree = function midOfThree(a, b, c) {
3892 if (b <= a && a <= c || c <= a && a <= b) {
3893 return a;
3894 } else if (a <= b && b <= c || c <= b && b <= a) {
3895 return b;
3896 } else {
3897 return c;
3898 }
3899 }; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3900
3901 var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3902 var dx13 = x1 - x3;
3903 var dx21 = x2 - x1;
3904 var dx43 = x4 - x3;
3905 var dy13 = y1 - y3;
3906 var dy21 = y2 - y1;
3907 var dy43 = y4 - y3;
3908 var ua_t = dx43 * dy13 - dy43 * dx13;
3909 var ub_t = dx21 * dy13 - dy21 * dx13;
3910 var u_b = dy43 * dx21 - dx43 * dy21;
3911
3912 if (u_b !== 0) {
3913 var ua = ua_t / u_b;
3914 var ub = ub_t / u_b;
3915 var flptThreshold = 0.001;
3916
3917 var _min = 0 - flptThreshold;
3918
3919 var _max = 1 + flptThreshold;
3920
3921 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3922 return [x1 + ua * dx21, y1 + ua * dy21];
3923 } else {
3924 if (!infiniteLines) {
3925 return [];
3926 } else {
3927 return [x1 + ua * dx21, y1 + ua * dy21];
3928 }
3929 }
3930 } else {
3931 if (ua_t === 0 || ub_t === 0) {
3932 // Parallel, coincident lines. Check if overlap
3933 // Check endpoint of second line
3934 if (midOfThree(x1, x2, x4) === x4) {
3935 return [x4, y4];
3936 } // Check start point of second line
3937
3938
3939 if (midOfThree(x1, x2, x3) === x3) {
3940 return [x3, y3];
3941 } // Endpoint of first line
3942
3943
3944 if (midOfThree(x3, x4, x2) === x2) {
3945 return [x2, y2];
3946 }
3947
3948 return [];
3949 } else {
3950 // Parallel, non-coincident
3951 return [];
3952 }
3953 }
3954 }; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3955 // intersect a node polygon (pts transformed)
3956 //
3957 // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3958 // intersect the points (no transform)
3959
3960 var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3961 var intersections = [];
3962 var intersection;
3963 var transformedPoints = new Array(basePoints.length);
3964 var doTransform = true;
3965
3966 if (width == null) {
3967 doTransform = false;
3968 }
3969
3970 var points;
3971
3972 if (doTransform) {
3973 for (var i = 0; i < transformedPoints.length / 2; i++) {
3974 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3975 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3976 }
3977
3978 if (padding > 0) {
3979 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3980 points = joinLines(expandedLineSet);
3981 } else {
3982 points = transformedPoints;
3983 }
3984 } else {
3985 points = basePoints;
3986 }
3987
3988 var currentX, currentY, nextX, nextY;
3989
3990 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3991 currentX = points[_i2 * 2];
3992 currentY = points[_i2 * 2 + 1];
3993
3994 if (_i2 < points.length / 2 - 1) {
3995 nextX = points[(_i2 + 1) * 2];
3996 nextY = points[(_i2 + 1) * 2 + 1];
3997 } else {
3998 nextX = points[0];
3999 nextY = points[1];
4000 }
4001
4002 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
4003
4004 if (intersection.length !== 0) {
4005 intersections.push(intersection[0], intersection[1]);
4006 }
4007 }
4008
4009 return intersections;
4010 };
4011 var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
4012 var intersections = [];
4013 var intersection;
4014 var lines = new Array(basePoints.length);
4015 var halfW = width / 2;
4016 var halfH = height / 2;
4017 var cornerRadius = getRoundPolygonRadius(width, height);
4018
4019 for (var i = 0; i < basePoints.length / 4; i++) {
4020 var sourceUv = void 0,
4021 destUv = void 0;
4022
4023 if (i === 0) {
4024 sourceUv = basePoints.length - 2;
4025 } else {
4026 sourceUv = i * 4 - 2;
4027 }
4028
4029 destUv = i * 4 + 2;
4030 var px = centerX + halfW * basePoints[i * 4];
4031 var py = centerY + halfH * basePoints[i * 4 + 1];
4032 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
4033 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
4034 var cp0x = px - offset * basePoints[sourceUv];
4035 var cp0y = py - offset * basePoints[sourceUv + 1];
4036 var cp1x = px + offset * basePoints[destUv];
4037 var cp1y = py + offset * basePoints[destUv + 1];
4038
4039 if (i === 0) {
4040 lines[basePoints.length - 2] = cp0x;
4041 lines[basePoints.length - 1] = cp0y;
4042 } else {
4043 lines[i * 4 - 2] = cp0x;
4044 lines[i * 4 - 1] = cp0y;
4045 }
4046
4047 lines[i * 4] = cp1x;
4048 lines[i * 4 + 1] = cp1y;
4049 var orthx = basePoints[sourceUv + 1];
4050 var orthy = -basePoints[sourceUv];
4051 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
4052
4053 if (cosAlpha < 0) {
4054 orthx *= -1;
4055 orthy *= -1;
4056 }
4057
4058 var cx = cp0x + orthx * cornerRadius;
4059 var cy = cp0y + orthy * cornerRadius;
4060 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
4061
4062 if (intersection.length !== 0) {
4063 intersections.push(intersection[0], intersection[1]);
4064 }
4065 }
4066
4067 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
4068 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
4069
4070 if (intersection.length !== 0) {
4071 intersections.push(intersection[0], intersection[1]);
4072 }
4073 }
4074
4075 if (intersections.length > 2) {
4076 var lowestIntersection = [intersections[0], intersections[1]];
4077 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
4078
4079 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
4080 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
4081
4082 if (squaredDistance <= lowestSquaredDistance) {
4083 lowestIntersection[0] = intersections[_i4 * 2];
4084 lowestIntersection[1] = intersections[_i4 * 2 + 1];
4085 lowestSquaredDistance = squaredDistance;
4086 }
4087 }
4088
4089 return lowestIntersection;
4090 }
4091
4092 return intersections;
4093 };
4094 var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
4095 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
4096 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
4097 var lenRatio = (length - amount) / length;
4098
4099 if (lenRatio < 0) {
4100 lenRatio = 0.00001;
4101 }
4102
4103 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
4104 };
4105 var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
4106 var points = generateUnitNgonPoints(sides, rotationRadians);
4107 points = fitPolygonToSquare(points);
4108 return points;
4109 };
4110 var fitPolygonToSquare = function fitPolygonToSquare(points) {
4111 var x, y;
4112 var sides = points.length / 2;
4113 var minX = Infinity,
4114 minY = Infinity,
4115 maxX = -Infinity,
4116 maxY = -Infinity;
4117
4118 for (var i = 0; i < sides; i++) {
4119 x = points[2 * i];
4120 y = points[2 * i + 1];
4121 minX = Math.min(minX, x);
4122 maxX = Math.max(maxX, x);
4123 minY = Math.min(minY, y);
4124 maxY = Math.max(maxY, y);
4125 } // stretch factors
4126
4127
4128 var sx = 2 / (maxX - minX);
4129 var sy = 2 / (maxY - minY);
4130
4131 for (var _i5 = 0; _i5 < sides; _i5++) {
4132 x = points[2 * _i5] = points[2 * _i5] * sx;
4133 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
4134 minX = Math.min(minX, x);
4135 maxX = Math.max(maxX, x);
4136 minY = Math.min(minY, y);
4137 maxY = Math.max(maxY, y);
4138 }
4139
4140 if (minY < -1) {
4141 for (var _i6 = 0; _i6 < sides; _i6++) {
4142 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
4143 }
4144 }
4145
4146 return points;
4147 };
4148 var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4149 var increment = 1.0 / sides * 2 * Math.PI;
4150 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4151 startAngle += rotationRadians;
4152 var points = new Array(sides * 2);
4153 var currentAngle;
4154
4155 for (var i = 0; i < sides; i++) {
4156 currentAngle = i * increment + startAngle;
4157 points[2 * i] = Math.cos(currentAngle); // x
4158
4159 points[2 * i + 1] = Math.sin(-currentAngle); // y
4160 }
4161
4162 return points;
4163 }; // Set the default radius, unless half of width or height is smaller than default
4164
4165 var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4166 return Math.min(width / 4, height / 4, 8);
4167 }; // Set the default radius
4168
4169 var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4170 return Math.min(width / 10, height / 10, 8);
4171 };
4172 var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4173 return 8;
4174 };
4175 var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4176 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4177 }; // get curve width, height, and control point position offsets as a percentage of node height / width
4178
4179 var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4180 return {
4181 heightOffset: Math.min(15, 0.05 * height),
4182 widthOffset: Math.min(100, 0.25 * width),
4183 ctrlPtOffsetPct: 0.05
4184 };
4185 };
4186
4187 var pageRankDefaults = defaults({
4188 dampingFactor: 0.8,
4189 precision: 0.000001,
4190 iterations: 200,
4191 weight: function weight(edge) {
4192 return 1;
4193 }
4194 });
4195 var elesfn$7 = {
4196 pageRank: function pageRank(options) {
4197 var _pageRankDefaults = pageRankDefaults(options),
4198 dampingFactor = _pageRankDefaults.dampingFactor,
4199 precision = _pageRankDefaults.precision,
4200 iterations = _pageRankDefaults.iterations,
4201 weight = _pageRankDefaults.weight;
4202
4203 var cy = this._private.cy;
4204
4205 var _this$byGroup = this.byGroup(),
4206 nodes = _this$byGroup.nodes,
4207 edges = _this$byGroup.edges;
4208
4209 var numNodes = nodes.length;
4210 var numNodesSqd = numNodes * numNodes;
4211 var numEdges = edges.length; // Construct transposed adjacency matrix
4212 // First lets have a zeroed matrix of the right size
4213 // We'll also keep track of the sum of each column
4214
4215 var matrix = new Array(numNodesSqd);
4216 var columnSum = new Array(numNodes);
4217 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
4218
4219 for (var i = 0; i < numNodes; i++) {
4220 for (var j = 0; j < numNodes; j++) {
4221 var n = i * numNodes + j;
4222 matrix[n] = 0;
4223 }
4224
4225 columnSum[i] = 0;
4226 } // Now, process edges
4227
4228
4229 for (var _i = 0; _i < numEdges; _i++) {
4230 var edge = edges[_i];
4231 var srcId = edge.data('source');
4232 var tgtId = edge.data('target'); // Don't include loops in the matrix
4233
4234 if (srcId === tgtId) {
4235 continue;
4236 }
4237
4238 var s = nodes.indexOfId(srcId);
4239 var t = nodes.indexOfId(tgtId);
4240 var w = weight(edge);
4241
4242 var _n = t * numNodes + s; // Update matrix
4243
4244
4245 matrix[_n] += w; // Update column sum
4246
4247 columnSum[s] += w;
4248 } // Add additional probability based on damping factor
4249 // Also, take into account columns that have sum = 0
4250
4251
4252 var p = 1.0 / numNodes + additionalProb; // Shorthand
4253 // Traverse matrix, column by column
4254
4255 for (var _j = 0; _j < numNodes; _j++) {
4256 if (columnSum[_j] === 0) {
4257 // No 'links' out from node jth, assume equal probability for each possible node
4258 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4259 var _n2 = _i2 * numNodes + _j;
4260
4261 matrix[_n2] = p;
4262 }
4263 } else {
4264 // Node jth has outgoing link, compute normalized probabilities
4265 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4266 var _n3 = _i3 * numNodes + _j;
4267
4268 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4269 }
4270 }
4271 } // Compute dominant eigenvector using power method
4272
4273
4274 var eigenvector = new Array(numNodes);
4275 var temp = new Array(numNodes);
4276 var previous; // Start with a vector of all 1's
4277 // Also, initialize a null vector which will be used as shorthand
4278
4279 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4280 eigenvector[_i4] = 1;
4281 }
4282
4283 for (var iter = 0; iter < iterations; iter++) {
4284 // Temp array with all 0's
4285 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4286 temp[_i5] = 0;
4287 } // Multiply matrix with previous result
4288
4289
4290 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4291 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4292 var _n4 = _i6 * numNodes + _j2;
4293
4294 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4295 }
4296 }
4297
4298 inPlaceSumNormalize(temp);
4299 previous = eigenvector;
4300 eigenvector = temp;
4301 temp = previous;
4302 var diff = 0; // Compute difference (squared module) of both vectors
4303
4304 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4305 var delta = previous[_i7] - eigenvector[_i7];
4306 diff += delta * delta;
4307 } // If difference is less than the desired threshold, stop iterating
4308
4309
4310 if (diff < precision) {
4311 break;
4312 }
4313 } // Construct result
4314
4315
4316 var res = {
4317 rank: function rank(node) {
4318 node = cy.collection(node)[0];
4319 return eigenvector[nodes.indexOf(node)];
4320 }
4321 };
4322 return res;
4323 } // pageRank
4324
4325 }; // elesfn
4326
4327 var defaults$1 = defaults({
4328 root: null,
4329 weight: function weight(edge) {
4330 return 1;
4331 },
4332 directed: false,
4333 alpha: 0
4334 });
4335 var elesfn$8 = {
4336 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4337 options = defaults$1(options);
4338 var cy = this.cy();
4339 var nodes = this.nodes();
4340 var numNodes = nodes.length;
4341
4342 if (!options.directed) {
4343 var degrees = {};
4344 var maxDegree = 0;
4345
4346 for (var i = 0; i < numNodes; i++) {
4347 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
4348
4349 options.root = node;
4350 var currDegree = this.degreeCentrality(options);
4351
4352 if (maxDegree < currDegree.degree) {
4353 maxDegree = currDegree.degree;
4354 }
4355
4356 degrees[node.id()] = currDegree.degree;
4357 }
4358
4359 return {
4360 degree: function degree(node) {
4361 if (maxDegree === 0) {
4362 return 0;
4363 }
4364
4365 if (string(node)) {
4366 // from is a selector string
4367 node = cy.filter(node);
4368 }
4369
4370 return degrees[node.id()] / maxDegree;
4371 }
4372 };
4373 } else {
4374 var indegrees = {};
4375 var outdegrees = {};
4376 var maxIndegree = 0;
4377 var maxOutdegree = 0;
4378
4379 for (var _i = 0; _i < numNodes; _i++) {
4380 var _node = nodes[_i];
4381
4382 var id = _node.id(); // add current node to the current options object and call degreeCentrality
4383
4384
4385 options.root = _node;
4386
4387 var _currDegree = this.degreeCentrality(options);
4388
4389 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4390 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4391 indegrees[id] = _currDegree.indegree;
4392 outdegrees[id] = _currDegree.outdegree;
4393 }
4394
4395 return {
4396 indegree: function indegree(node) {
4397 if (maxIndegree == 0) {
4398 return 0;
4399 }
4400
4401 if (string(node)) {
4402 // from is a selector string
4403 node = cy.filter(node);
4404 }
4405
4406 return indegrees[node.id()] / maxIndegree;
4407 },
4408 outdegree: function outdegree(node) {
4409 if (maxOutdegree === 0) {
4410 return 0;
4411 }
4412
4413 if (string(node)) {
4414 // from is a selector string
4415 node = cy.filter(node);
4416 }
4417
4418 return outdegrees[node.id()] / maxOutdegree;
4419 }
4420 };
4421 }
4422 },
4423 // degreeCentralityNormalized
4424 // Implemented from the algorithm in Opsahl's paper
4425 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4426 // check the heading 2 "Degree"
4427 degreeCentrality: function degreeCentrality(options) {
4428 options = defaults$1(options);
4429 var cy = this.cy();
4430 var callingEles = this;
4431 var _options = options,
4432 root = _options.root,
4433 weight = _options.weight,
4434 directed = _options.directed,
4435 alpha = _options.alpha;
4436 root = cy.collection(root)[0];
4437
4438 if (!directed) {
4439 var connEdges = root.connectedEdges().intersection(callingEles);
4440 var k = connEdges.length;
4441 var s = 0; // Now, sum edge weights
4442
4443 for (var i = 0; i < connEdges.length; i++) {
4444 s += weight(connEdges[i]);
4445 }
4446
4447 return {
4448 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4449 };
4450 } else {
4451 var edges = root.connectedEdges();
4452 var incoming = edges.filter(function (edge) {
4453 return edge.target().same(root) && callingEles.has(edge);
4454 });
4455 var outgoing = edges.filter(function (edge) {
4456 return edge.source().same(root) && callingEles.has(edge);
4457 });
4458 var k_in = incoming.length;
4459 var k_out = outgoing.length;
4460 var s_in = 0;
4461 var s_out = 0; // Now, sum incoming edge weights
4462
4463 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4464 s_in += weight(incoming[_i2]);
4465 } // Now, sum outgoing edge weights
4466
4467
4468 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4469 s_out += weight(outgoing[_i3]);
4470 }
4471
4472 return {
4473 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4474 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4475 };
4476 }
4477 } // degreeCentrality
4478
4479 }; // elesfn
4480 // nice, short mathemathical alias
4481
4482 elesfn$8.dc = elesfn$8.degreeCentrality;
4483 elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
4484
4485 var defaults$2 = defaults({
4486 harmonic: true,
4487 weight: function weight() {
4488 return 1;
4489 },
4490 directed: false,
4491 root: null
4492 });
4493 var elesfn$9 = {
4494 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4495 var _defaults = defaults$2(options),
4496 harmonic = _defaults.harmonic,
4497 weight = _defaults.weight,
4498 directed = _defaults.directed;
4499
4500 var cy = this.cy();
4501 var closenesses = {};
4502 var maxCloseness = 0;
4503 var nodes = this.nodes();
4504 var fw = this.floydWarshall({
4505 weight: weight,
4506 directed: directed
4507 }); // Compute closeness for every node and find the maximum closeness
4508
4509 for (var i = 0; i < nodes.length; i++) {
4510 var currCloseness = 0;
4511 var node_i = nodes[i];
4512
4513 for (var j = 0; j < nodes.length; j++) {
4514 if (i !== j) {
4515 var d = fw.distance(node_i, nodes[j]);
4516
4517 if (harmonic) {
4518 currCloseness += 1 / d;
4519 } else {
4520 currCloseness += d;
4521 }
4522 }
4523 }
4524
4525 if (!harmonic) {
4526 currCloseness = 1 / currCloseness;
4527 }
4528
4529 if (maxCloseness < currCloseness) {
4530 maxCloseness = currCloseness;
4531 }
4532
4533 closenesses[node_i.id()] = currCloseness;
4534 }
4535
4536 return {
4537 closeness: function closeness(node) {
4538 if (maxCloseness == 0) {
4539 return 0;
4540 }
4541
4542 if (string(node)) {
4543 // from is a selector string
4544 node = cy.filter(node)[0].id();
4545 } else {
4546 // from is a node
4547 node = node.id();
4548 }
4549
4550 return closenesses[node] / maxCloseness;
4551 }
4552 };
4553 },
4554 // Implemented from pseudocode from wikipedia
4555 closenessCentrality: function closenessCentrality(options) {
4556 var _defaults2 = defaults$2(options),
4557 root = _defaults2.root,
4558 weight = _defaults2.weight,
4559 directed = _defaults2.directed,
4560 harmonic = _defaults2.harmonic;
4561
4562 root = this.filter(root)[0]; // we need distance from this node to every other node
4563
4564 var dijkstra = this.dijkstra({
4565 root: root,
4566 weight: weight,
4567 directed: directed
4568 });
4569 var totalDistance = 0;
4570 var nodes = this.nodes();
4571
4572 for (var i = 0; i < nodes.length; i++) {
4573 var n = nodes[i];
4574
4575 if (!n.same(root)) {
4576 var d = dijkstra.distanceTo(n);
4577
4578 if (harmonic) {
4579 totalDistance += 1 / d;
4580 } else {
4581 totalDistance += d;
4582 }
4583 }
4584 }
4585
4586 return harmonic ? totalDistance : 1 / totalDistance;
4587 } // closenessCentrality
4588
4589 }; // elesfn
4590 // nice, short mathemathical alias
4591
4592 elesfn$9.cc = elesfn$9.closenessCentrality;
4593 elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
4594
4595 var defaults$3 = defaults({
4596 weight: null,
4597 directed: false
4598 });
4599 var elesfn$a = {
4600 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4601 betweennessCentrality: function betweennessCentrality(options) {
4602 var _defaults = defaults$3(options),
4603 directed = _defaults.directed,
4604 weight = _defaults.weight;
4605
4606 var weighted = weight != null;
4607 var cy = this.cy(); // starting
4608
4609 var V = this.nodes();
4610 var A = {};
4611 var _C = {};
4612 var max = 0;
4613 var C = {
4614 set: function set(key, val) {
4615 _C[key] = val;
4616
4617 if (val > max) {
4618 max = val;
4619 }
4620 },
4621 get: function get(key) {
4622 return _C[key];
4623 }
4624 }; // A contains the neighborhoods of every node
4625
4626 for (var i = 0; i < V.length; i++) {
4627 var v = V[i];
4628 var vid = v.id();
4629
4630 if (directed) {
4631 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4632 } else {
4633 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4634 }
4635
4636 C.set(vid, 0);
4637 }
4638
4639 var _loop = function _loop(s) {
4640 var sid = V[s].id();
4641 var S = []; // stack
4642
4643 var P = {};
4644 var g = {};
4645 var d = {};
4646 var Q = new heap$1(function (a, b) {
4647 return d[a] - d[b];
4648 }); // queue
4649 // init dictionaries
4650
4651 for (var _i = 0; _i < V.length; _i++) {
4652 var _vid = V[_i].id();
4653
4654 P[_vid] = [];
4655 g[_vid] = 0;
4656 d[_vid] = Infinity;
4657 }
4658
4659 g[sid] = 1; // sigma
4660
4661 d[sid] = 0; // distance to s
4662
4663 Q.push(sid);
4664
4665 while (!Q.empty()) {
4666 var _v = Q.pop();
4667
4668 S.push(_v);
4669
4670 if (weighted) {
4671 for (var j = 0; j < A[_v].length; j++) {
4672 var w = A[_v][j];
4673 var vEle = cy.getElementById(_v);
4674 var edge = void 0;
4675
4676 if (vEle.edgesTo(w).length > 0) {
4677 edge = vEle.edgesTo(w)[0];
4678 } else {
4679 edge = w.edgesTo(vEle)[0];
4680 }
4681
4682 var edgeWeight = weight(edge);
4683 w = w.id();
4684
4685 if (d[w] > d[_v] + edgeWeight) {
4686 d[w] = d[_v] + edgeWeight;
4687
4688 if (Q.nodes.indexOf(w) < 0) {
4689 //if w is not in Q
4690 Q.push(w);
4691 } else {
4692 // update position if w is in Q
4693 Q.updateItem(w);
4694 }
4695
4696 g[w] = 0;
4697 P[w] = [];
4698 }
4699
4700 if (d[w] == d[_v] + edgeWeight) {
4701 g[w] = g[w] + g[_v];
4702 P[w].push(_v);
4703 }
4704 }
4705 } else {
4706 for (var _j = 0; _j < A[_v].length; _j++) {
4707 var _w = A[_v][_j].id();
4708
4709 if (d[_w] == Infinity) {
4710 Q.push(_w);
4711 d[_w] = d[_v] + 1;
4712 }
4713
4714 if (d[_w] == d[_v] + 1) {
4715 g[_w] = g[_w] + g[_v];
4716
4717 P[_w].push(_v);
4718 }
4719 }
4720 }
4721 }
4722
4723 var e = {};
4724
4725 for (var _i2 = 0; _i2 < V.length; _i2++) {
4726 e[V[_i2].id()] = 0;
4727 }
4728
4729 while (S.length > 0) {
4730 var _w2 = S.pop();
4731
4732 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4733 var _v2 = P[_w2][_j2];
4734 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4735 }
4736
4737 if (_w2 != V[s].id()) {
4738 C.set(_w2, C.get(_w2) + e[_w2]);
4739 }
4740 }
4741 };
4742
4743 for (var s = 0; s < V.length; s++) {
4744 _loop(s);
4745 }
4746
4747 var ret = {
4748 betweenness: function betweenness(node) {
4749 var id = cy.collection(node).id();
4750 return C.get(id);
4751 },
4752 betweennessNormalized: function betweennessNormalized(node) {
4753 if (max == 0) {
4754 return 0;
4755 }
4756
4757 var id = cy.collection(node).id();
4758 return C.get(id) / max;
4759 }
4760 }; // alias
4761
4762 ret.betweennessNormalised = ret.betweennessNormalized;
4763 return ret;
4764 } // betweennessCentrality
4765
4766 }; // elesfn
4767 // nice, short mathemathical alias
4768
4769 elesfn$a.bc = elesfn$a.betweennessCentrality;
4770
4771 // Implemented by Zoe Xi @zoexi for GSOC 2016
4772 /* eslint-disable no-unused-vars */
4773
4774 var defaults$4 = defaults({
4775 expandFactor: 2,
4776 // affects time of computation and cluster granularity to some extent: M * M
4777 inflateFactor: 2,
4778 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4779 multFactor: 1,
4780 // optional self loops for each node. Use a neutral value to improve cluster computations.
4781 maxIterations: 20,
4782 // maximum number of iterations of the MCL algorithm in a single run
4783 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4784 function (edge) {
4785 return 1;
4786 }]
4787 });
4788 /* eslint-enable */
4789
4790 var setOptions = function setOptions(options) {
4791 return defaults$4(options);
4792 };
4793 /* eslint-enable */
4794
4795
4796 var getSimilarity = function getSimilarity(edge, attributes) {
4797 var total = 0;
4798
4799 for (var i = 0; i < attributes.length; i++) {
4800 total += attributes[i](edge);
4801 }
4802
4803 return total;
4804 };
4805
4806 var addLoops = function addLoops(M, n, val) {
4807 for (var i = 0; i < n; i++) {
4808 M[i * n + i] = val;
4809 }
4810 };
4811
4812 var normalize = function normalize(M, n) {
4813 var sum;
4814
4815 for (var col = 0; col < n; col++) {
4816 sum = 0;
4817
4818 for (var row = 0; row < n; row++) {
4819 sum += M[row * n + col];
4820 }
4821
4822 for (var _row = 0; _row < n; _row++) {
4823 M[_row * n + col] = M[_row * n + col] / sum;
4824 }
4825 }
4826 }; // TODO: blocked matrix multiplication?
4827
4828
4829 var mmult = function mmult(A, B, n) {
4830 var C = new Array(n * n);
4831
4832 for (var i = 0; i < n; i++) {
4833 for (var j = 0; j < n; j++) {
4834 C[i * n + j] = 0;
4835 }
4836
4837 for (var k = 0; k < n; k++) {
4838 for (var _j = 0; _j < n; _j++) {
4839 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4840 }
4841 }
4842 }
4843
4844 return C;
4845 };
4846
4847 var expand = function expand(M, n, expandFactor
4848 /** power **/
4849 ) {
4850 var _M = M.slice(0);
4851
4852 for (var p = 1; p < expandFactor; p++) {
4853 M = mmult(M, _M, n);
4854 }
4855
4856 return M;
4857 };
4858
4859 var inflate = function inflate(M, n, inflateFactor
4860 /** r **/
4861 ) {
4862 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4863
4864
4865 for (var i = 0; i < n * n; i++) {
4866 _M[i] = Math.pow(M[i], inflateFactor);
4867 }
4868
4869 normalize(_M, n);
4870 return _M;
4871 };
4872
4873 var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4874 // Check that both matrices have the same elements (i,j)
4875 for (var i = 0; i < n2; i++) {
4876 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4877
4878 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4879
4880 if (v1 !== v2) {
4881 return false;
4882 }
4883 }
4884
4885 return true;
4886 };
4887
4888 var assign = function assign(M, n, nodes, cy) {
4889 var clusters = [];
4890
4891 for (var i = 0; i < n; i++) {
4892 var cluster = [];
4893
4894 for (var j = 0; j < n; j++) {
4895 // Row-wise attractors and elements that they attract belong in same cluster
4896 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4897 cluster.push(nodes[j]);
4898 }
4899 }
4900
4901 if (cluster.length !== 0) {
4902 clusters.push(cy.collection(cluster));
4903 }
4904 }
4905
4906 return clusters;
4907 };
4908
4909 var isDuplicate = function isDuplicate(c1, c2) {
4910 for (var i = 0; i < c1.length; i++) {
4911 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4912 return false;
4913 }
4914 }
4915
4916 return true;
4917 };
4918
4919 var removeDuplicates = function removeDuplicates(clusters) {
4920 for (var i = 0; i < clusters.length; i++) {
4921 for (var j = 0; j < clusters.length; j++) {
4922 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4923 clusters.splice(j, 1);
4924 }
4925 }
4926 }
4927
4928 return clusters;
4929 };
4930
4931 var markovClustering = function markovClustering(options) {
4932 var nodes = this.nodes();
4933 var edges = this.edges();
4934 var cy = this.cy(); // Set parameters of algorithm:
4935
4936 var opts = setOptions(options); // Map each node to its position in node array
4937
4938 var id2position = {};
4939
4940 for (var i = 0; i < nodes.length; i++) {
4941 id2position[nodes[i].id()] = i;
4942 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4943
4944
4945 var n = nodes.length,
4946 n2 = n * n;
4947
4948 var M = new Array(n2),
4949 _M;
4950
4951 for (var _i = 0; _i < n2; _i++) {
4952 M[_i] = 0;
4953 }
4954
4955 for (var e = 0; e < edges.length; e++) {
4956 var edge = edges[e];
4957 var _i2 = id2position[edge.source().id()];
4958 var j = id2position[edge.target().id()];
4959 var sim = getSimilarity(edge, opts.attributes);
4960 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4961
4962 M[j * n + _i2] += sim;
4963 } // Begin Markov cluster algorithm
4964 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4965
4966
4967 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4968
4969 normalize(M, n);
4970 var isStillMoving = true;
4971 var iterations = 0;
4972
4973 while (isStillMoving && iterations < opts.maxIterations) {
4974 isStillMoving = false; // Step 3:
4975
4976 _M = expand(M, n, opts.expandFactor); // Step 4:
4977
4978 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4979
4980 if (!hasConverged(M, _M, n2, 4)) {
4981 isStillMoving = true;
4982 }
4983
4984 iterations++;
4985 } // Build clusters from matrix
4986
4987
4988 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4989
4990 clusters = removeDuplicates(clusters);
4991 return clusters;
4992 };
4993
4994 var markovClustering$1 = {
4995 markovClustering: markovClustering,
4996 mcl: markovClustering
4997 };
4998
4999 // Common distance metrics for clustering algorithms
5000
5001 var identity = function identity(x) {
5002 return x;
5003 };
5004
5005 var absDiff = function absDiff(p, q) {
5006 return Math.abs(q - p);
5007 };
5008
5009 var addAbsDiff = function addAbsDiff(total, p, q) {
5010 return total + absDiff(p, q);
5011 };
5012
5013 var addSquaredDiff = function addSquaredDiff(total, p, q) {
5014 return total + Math.pow(q - p, 2);
5015 };
5016
5017 var sqrt = function sqrt(x) {
5018 return Math.sqrt(x);
5019 };
5020
5021 var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
5022 return Math.max(currentMax, absDiff(p, q));
5023 };
5024
5025 var getDistance = function getDistance(length, getP, getQ, init, visit) {
5026 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
5027 var ret = init;
5028 var p, q;
5029
5030 for (var dim = 0; dim < length; dim++) {
5031 p = getP(dim);
5032 q = getQ(dim);
5033 ret = visit(ret, p, q);
5034 }
5035
5036 return post(ret);
5037 };
5038
5039 var distances = {
5040 euclidean: function euclidean(length, getP, getQ) {
5041 if (length >= 2) {
5042 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
5043 } else {
5044 // for single attr case, more efficient to avoid sqrt
5045 return getDistance(length, getP, getQ, 0, addAbsDiff);
5046 }
5047 },
5048 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
5049 return getDistance(length, getP, getQ, 0, addSquaredDiff);
5050 },
5051 manhattan: function manhattan(length, getP, getQ) {
5052 return getDistance(length, getP, getQ, 0, addAbsDiff);
5053 },
5054 max: function max(length, getP, getQ) {
5055 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
5056 }
5057 }; // in case the user accidentally doesn't use camel case
5058
5059 distances['squared-euclidean'] = distances['squaredEuclidean'];
5060 distances['squaredeuclidean'] = distances['squaredEuclidean'];
5061 function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
5062 var impl;
5063
5064 if (fn(method)) {
5065 impl = method;
5066 } else {
5067 impl = distances[method] || distances.euclidean;
5068 }
5069
5070 if (length === 0 && fn(method)) {
5071 return impl(nodeP, nodeQ);
5072 } else {
5073 return impl(length, getP, getQ, nodeP, nodeQ);
5074 }
5075 }
5076
5077 var defaults$5 = defaults({
5078 k: 2,
5079 m: 2,
5080 sensitivityThreshold: 0.0001,
5081 distance: 'euclidean',
5082 maxIterations: 10,
5083 attributes: [],
5084 testMode: false,
5085 testCentroids: null
5086 });
5087
5088 var setOptions$1 = function setOptions(options) {
5089 return defaults$5(options);
5090 };
5091 /* eslint-enable */
5092
5093
5094 var getDist = function getDist(type, node, centroid, attributes, mode) {
5095 var noNodeP = mode !== 'kMedoids';
5096 var getP = noNodeP ? function (i) {
5097 return centroid[i];
5098 } : function (i) {
5099 return attributes[i](centroid);
5100 };
5101
5102 var getQ = function getQ(i) {
5103 return attributes[i](node);
5104 };
5105
5106 var nodeP = centroid;
5107 var nodeQ = node;
5108 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
5109 };
5110
5111 var randomCentroids = function randomCentroids(nodes, k, attributes) {
5112 var ndim = attributes.length;
5113 var min = new Array(ndim);
5114 var max = new Array(ndim);
5115 var centroids = new Array(k);
5116 var centroid = null; // Find min, max values for each attribute dimension
5117
5118 for (var i = 0; i < ndim; i++) {
5119 min[i] = nodes.min(attributes[i]).value;
5120 max[i] = nodes.max(attributes[i]).value;
5121 } // Build k centroids, each represented as an n-dim feature vector
5122
5123
5124 for (var c = 0; c < k; c++) {
5125 centroid = [];
5126
5127 for (var _i = 0; _i < ndim; _i++) {
5128 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
5129 }
5130
5131 centroids[c] = centroid;
5132 }
5133
5134 return centroids;
5135 };
5136
5137 var classify = function classify(node, centroids, distance, attributes, type) {
5138 var min = Infinity;
5139 var index = 0;
5140
5141 for (var i = 0; i < centroids.length; i++) {
5142 var dist = getDist(distance, node, centroids[i], attributes, type);
5143
5144 if (dist < min) {
5145 min = dist;
5146 index = i;
5147 }
5148 }
5149
5150 return index;
5151 };
5152
5153 var buildCluster = function buildCluster(centroid, nodes, assignment) {
5154 var cluster = [];
5155 var node = null;
5156
5157 for (var n = 0; n < nodes.length; n++) {
5158 node = nodes[n];
5159
5160 if (assignment[node.id()] === centroid) {
5161 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
5162 cluster.push(node);
5163 }
5164 }
5165
5166 return cluster;
5167 };
5168
5169 var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
5170 return Math.abs(v2 - v1) <= sensitivityThreshold;
5171 };
5172
5173 var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
5174 for (var i = 0; i < v1.length; i++) {
5175 for (var j = 0; j < v1[i].length; j++) {
5176 var diff = Math.abs(v1[i][j] - v2[i][j]);
5177
5178 if (diff > sensitivityThreshold) {
5179 return false;
5180 }
5181 }
5182 }
5183
5184 return true;
5185 };
5186
5187 var seenBefore = function seenBefore(node, medoids, n) {
5188 for (var i = 0; i < n; i++) {
5189 if (node === medoids[i]) return true;
5190 }
5191
5192 return false;
5193 };
5194
5195 var randomMedoids = function randomMedoids(nodes, k) {
5196 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
5197 // so we need to check to see if we've already seen or chose this node before.
5198
5199 if (nodes.length < 50) {
5200 // Randomly select k medoids from the n nodes
5201 for (var i = 0; i < k; i++) {
5202 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).
5203 // Instead choose a different random node.
5204
5205 while (seenBefore(node, medoids, i)) {
5206 node = nodes[Math.floor(Math.random() * nodes.length)];
5207 }
5208
5209 medoids[i] = node;
5210 }
5211 } else {
5212 // Relatively large data set, so pretty safe to not check and just select random nodes
5213 for (var _i2 = 0; _i2 < k; _i2++) {
5214 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
5215 }
5216 }
5217
5218 return medoids;
5219 };
5220
5221 var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
5222 var cost = 0;
5223
5224 for (var n = 0; n < cluster.length; n++) {
5225 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
5226 }
5227
5228 return cost;
5229 };
5230
5231 var kMeans = function kMeans(options) {
5232 var cy = this.cy();
5233 var nodes = this.nodes();
5234 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
5235
5236 var opts = setOptions$1(options); // Begin k-means algorithm
5237
5238 var clusters = new Array(opts.k);
5239 var assignment = {};
5240 var centroids; // Step 1: Initialize centroid positions
5241
5242 if (opts.testMode) {
5243 if (typeof opts.testCentroids === 'number') {
5244 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5245 } else if (_typeof(opts.testCentroids) === 'object') {
5246 centroids = opts.testCentroids;
5247 } else {
5248 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5249 }
5250 } else {
5251 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5252 }
5253
5254 var isStillMoving = true;
5255 var iterations = 0;
5256
5257 while (isStillMoving && iterations < opts.maxIterations) {
5258 // Step 2: Assign nodes to the nearest centroid
5259 for (var n = 0; n < nodes.length; n++) {
5260 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5261
5262 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5263 } // Step 3: For each of the k clusters, update its centroid
5264
5265
5266 isStillMoving = false;
5267
5268 for (var c = 0; c < opts.k; c++) {
5269 // Get all nodes that belong to this cluster
5270 var cluster = buildCluster(c, nodes, assignment);
5271
5272 if (cluster.length === 0) {
5273 // If cluster is empty, break out early & move to next cluster
5274 continue;
5275 } // Update centroids by calculating avg of all nodes within the cluster.
5276
5277
5278 var ndim = opts.attributes.length;
5279 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5280
5281 var newCentroid = new Array(ndim);
5282 var sum = new Array(ndim);
5283
5284 for (var d = 0; d < ndim; d++) {
5285 sum[d] = 0.0;
5286
5287 for (var i = 0; i < cluster.length; i++) {
5288 node = cluster[i];
5289 sum[d] += opts.attributes[d](node);
5290 }
5291
5292 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
5293
5294 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5295 isStillMoving = true;
5296 }
5297 }
5298
5299 centroids[c] = newCentroid;
5300 clusters[c] = cy.collection(cluster);
5301 }
5302
5303 iterations++;
5304 }
5305
5306 return clusters;
5307 };
5308
5309 var kMedoids = function kMedoids(options) {
5310 var cy = this.cy();
5311 var nodes = this.nodes();
5312 var node = null;
5313 var opts = setOptions$1(options); // Begin k-medoids algorithm
5314
5315 var clusters = new Array(opts.k);
5316 var medoids;
5317 var assignment = {};
5318 var curCost;
5319 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5320 // Step 1: Initialize k medoids
5321
5322 if (opts.testMode) {
5323 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5324 medoids = opts.testCentroids;
5325 } else {
5326 medoids = randomMedoids(nodes, opts.k);
5327 }
5328 } else {
5329 medoids = randomMedoids(nodes, opts.k);
5330 }
5331
5332 var isStillMoving = true;
5333 var iterations = 0;
5334
5335 while (isStillMoving && iterations < opts.maxIterations) {
5336 // Step 2: Assign nodes to the nearest medoid
5337 for (var n = 0; n < nodes.length; n++) {
5338 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
5339
5340 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5341 }
5342
5343 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
5344 // select the node with the lowest configuration cost as new medoid.
5345
5346 for (var m = 0; m < medoids.length; m++) {
5347 // Get all nodes that belong to this medoid
5348 var cluster = buildCluster(m, nodes, assignment);
5349
5350 if (cluster.length === 0) {
5351 // If cluster is empty, break out early & move to next cluster
5352 continue;
5353 }
5354
5355 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5356 // Select different medoid if its configuration has the lowest cost
5357
5358 for (var _n = 0; _n < cluster.length; _n++) {
5359 curCost = findCost(cluster[_n], cluster, opts.attributes);
5360
5361 if (curCost < minCosts[m]) {
5362 minCosts[m] = curCost;
5363 medoids[m] = cluster[_n];
5364 isStillMoving = true;
5365 }
5366 }
5367
5368 clusters[m] = cy.collection(cluster);
5369 }
5370
5371 iterations++;
5372 }
5373
5374 return clusters;
5375 };
5376
5377 var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5378 var numerator, denominator;
5379
5380 for (var n = 0; n < nodes.length; n++) {
5381 for (var c = 0; c < centroids.length; c++) {
5382 weight[n][c] = Math.pow(U[n][c], opts.m);
5383 }
5384 }
5385
5386 for (var _c = 0; _c < centroids.length; _c++) {
5387 for (var dim = 0; dim < opts.attributes.length; dim++) {
5388 numerator = 0;
5389 denominator = 0;
5390
5391 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5392 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5393 denominator += weight[_n2][_c];
5394 }
5395
5396 centroids[_c][dim] = numerator / denominator;
5397 }
5398 }
5399 };
5400
5401 var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5402 // Save previous step
5403 for (var i = 0; i < U.length; i++) {
5404 _U[i] = U[i].slice();
5405 }
5406
5407 var sum, numerator, denominator;
5408 var pow = 2 / (opts.m - 1);
5409
5410 for (var c = 0; c < centroids.length; c++) {
5411 for (var n = 0; n < nodes.length; n++) {
5412 sum = 0;
5413
5414 for (var k = 0; k < centroids.length; k++) {
5415 // against all other centroids
5416 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5417 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5418 sum += Math.pow(numerator / denominator, pow);
5419 }
5420
5421 U[n][c] = 1 / sum;
5422 }
5423 }
5424 };
5425
5426 var assign$1 = function assign(nodes, U, opts, cy) {
5427 var clusters = new Array(opts.k);
5428
5429 for (var c = 0; c < clusters.length; c++) {
5430 clusters[c] = [];
5431 }
5432
5433 var max;
5434 var index;
5435
5436 for (var n = 0; n < U.length; n++) {
5437 // for each node (U is N x C matrix)
5438 max = -Infinity;
5439 index = -1; // Determine which cluster the node is most likely to belong in
5440
5441 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5442 if (U[n][_c2] > max) {
5443 max = U[n][_c2];
5444 index = _c2;
5445 }
5446 }
5447
5448 clusters[index].push(nodes[n]);
5449 } // Turn every array into a collection of nodes
5450
5451
5452 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5453 clusters[_c3] = cy.collection(clusters[_c3]);
5454 }
5455
5456 return clusters;
5457 };
5458
5459 var fuzzyCMeans = function fuzzyCMeans(options) {
5460 var cy = this.cy();
5461 var nodes = this.nodes();
5462 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
5463
5464 var clusters;
5465 var centroids;
5466 var U;
5467
5468 var _U;
5469
5470 var weight; // Step 1: Initialize letiables.
5471
5472 _U = new Array(nodes.length);
5473
5474 for (var i = 0; i < nodes.length; i++) {
5475 // N x C matrix
5476 _U[i] = new Array(opts.k);
5477 }
5478
5479 U = new Array(nodes.length);
5480
5481 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5482 // N x C matrix
5483 U[_i3] = new Array(opts.k);
5484 }
5485
5486 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5487 var total = 0;
5488
5489 for (var j = 0; j < opts.k; j++) {
5490 U[_i4][j] = Math.random();
5491 total += U[_i4][j];
5492 }
5493
5494 for (var _j = 0; _j < opts.k; _j++) {
5495 U[_i4][_j] = U[_i4][_j] / total;
5496 }
5497 }
5498
5499 centroids = new Array(opts.k);
5500
5501 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5502 centroids[_i5] = new Array(opts.attributes.length);
5503 }
5504
5505 weight = new Array(nodes.length);
5506
5507 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5508 // N x C matrix
5509 weight[_i6] = new Array(opts.k);
5510 } // end init FCM
5511
5512
5513 var isStillMoving = true;
5514 var iterations = 0;
5515
5516 while (isStillMoving && iterations < opts.maxIterations) {
5517 isStillMoving = false; // Step 2: Calculate the centroids for each step.
5518
5519 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
5520
5521 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
5522
5523 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5524 isStillMoving = true;
5525 }
5526
5527 iterations++;
5528 } // Assign nodes to clusters with highest probability.
5529
5530
5531 clusters = assign$1(nodes, U, opts, cy);
5532 return {
5533 clusters: clusters,
5534 degreeOfMembership: U
5535 };
5536 };
5537
5538 var kClustering = {
5539 kMeans: kMeans,
5540 kMedoids: kMedoids,
5541 fuzzyCMeans: fuzzyCMeans,
5542 fcm: fuzzyCMeans
5543 };
5544
5545 // Implemented by Zoe Xi @zoexi for GSOC 2016
5546 var defaults$6 = defaults({
5547 distance: 'euclidean',
5548 // distance metric to compare nodes
5549 linkage: 'min',
5550 // linkage criterion : how to determine the distance between clusters of nodes
5551 mode: 'threshold',
5552 // mode:'threshold' => clusters must be threshold distance apart
5553 threshold: Infinity,
5554 // the distance threshold
5555 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5556 addDendrogram: false,
5557 // whether to add the dendrogram to the graph for viz
5558 dendrogramDepth: 0,
5559 // depth at which dendrogram branches are merged into the returned clusters
5560 attributes: [] // array of attr functions
5561
5562 });
5563 var linkageAliases = {
5564 'single': 'min',
5565 'complete': 'max'
5566 };
5567
5568 var setOptions$2 = function setOptions(options) {
5569 var opts = defaults$6(options);
5570 var preferredAlias = linkageAliases[opts.linkage];
5571
5572 if (preferredAlias != null) {
5573 opts.linkage = preferredAlias;
5574 }
5575
5576 return opts;
5577 };
5578
5579 var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5580 // Find two closest clusters from cached mins
5581 var minKey = 0;
5582 var min = Infinity;
5583 var dist;
5584 var attrs = opts.attributes;
5585
5586 var getDist = function getDist(n1, n2) {
5587 return clusteringDistance(opts.distance, attrs.length, function (i) {
5588 return attrs[i](n1);
5589 }, function (i) {
5590 return attrs[i](n2);
5591 }, n1, n2);
5592 };
5593
5594 for (var i = 0; i < clusters.length; i++) {
5595 var key = clusters[i].key;
5596 var _dist = dists[key][mins[key]];
5597
5598 if (_dist < min) {
5599 minKey = key;
5600 min = _dist;
5601 }
5602 }
5603
5604 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5605 return false;
5606 }
5607
5608 var c1 = index[minKey];
5609 var c2 = index[mins[minKey]];
5610 var merged; // Merge two closest clusters
5611
5612 if (opts.mode === 'dendrogram') {
5613 merged = {
5614 left: c1,
5615 right: c2,
5616 key: c1.key
5617 };
5618 } else {
5619 merged = {
5620 value: c1.value.concat(c2.value),
5621 key: c1.key
5622 };
5623 }
5624
5625 clusters[c1.index] = merged;
5626 clusters.splice(c2.index, 1);
5627 index[c1.key] = merged; // Update distances with new merged cluster
5628
5629 for (var _i = 0; _i < clusters.length; _i++) {
5630 var cur = clusters[_i];
5631
5632 if (c1.key === cur.key) {
5633 dist = Infinity;
5634 } else if (opts.linkage === 'min') {
5635 dist = dists[c1.key][cur.key];
5636
5637 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5638 dist = dists[c2.key][cur.key];
5639 }
5640 } else if (opts.linkage === 'max') {
5641 dist = dists[c1.key][cur.key];
5642
5643 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5644 dist = dists[c2.key][cur.key];
5645 }
5646 } else if (opts.linkage === 'mean') {
5647 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5648 } else {
5649 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5650 }
5651
5652 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5653 } // Update cached mins
5654
5655
5656 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5657 var key1 = clusters[_i2].key;
5658
5659 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5660 var _min = key1;
5661
5662 for (var j = 0; j < clusters.length; j++) {
5663 var key2 = clusters[j].key;
5664
5665 if (dists[key1][key2] < dists[key1][_min]) {
5666 _min = key2;
5667 }
5668 }
5669
5670 mins[key1] = _min;
5671 }
5672
5673 clusters[_i2].index = _i2;
5674 } // Clean up meta data used for clustering
5675
5676
5677 c1.key = c2.key = c1.index = c2.index = null;
5678 return true;
5679 };
5680
5681 var getAllChildren = function getAllChildren(root, arr, cy) {
5682 if (!root) return;
5683
5684 if (root.value) {
5685 arr.push(root.value);
5686 } else {
5687 if (root.left) getAllChildren(root.left, arr);
5688 if (root.right) getAllChildren(root.right, arr);
5689 }
5690 };
5691
5692 var buildDendrogram = function buildDendrogram(root, cy) {
5693 if (!root) return '';
5694
5695 if (root.left && root.right) {
5696 var leftStr = buildDendrogram(root.left, cy);
5697 var rightStr = buildDendrogram(root.right, cy);
5698 var node = cy.add({
5699 group: 'nodes',
5700 data: {
5701 id: leftStr + ',' + rightStr
5702 }
5703 });
5704 cy.add({
5705 group: 'edges',
5706 data: {
5707 source: leftStr,
5708 target: node.id()
5709 }
5710 });
5711 cy.add({
5712 group: 'edges',
5713 data: {
5714 source: rightStr,
5715 target: node.id()
5716 }
5717 });
5718 return node.id();
5719 } else if (root.value) {
5720 return root.value.id();
5721 }
5722 };
5723
5724 var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5725 if (!root) return [];
5726 var left = [],
5727 right = [],
5728 leaves = [];
5729
5730 if (k === 0) {
5731 // don't cut tree, simply return all nodes as 1 single cluster
5732 if (root.left) getAllChildren(root.left, left);
5733 if (root.right) getAllChildren(root.right, right);
5734 leaves = left.concat(right);
5735 return [cy.collection(leaves)];
5736 } else if (k === 1) {
5737 // cut at root
5738 if (root.value) {
5739 // leaf node
5740 return [cy.collection(root.value)];
5741 } else {
5742 if (root.left) getAllChildren(root.left, left);
5743 if (root.right) getAllChildren(root.right, right);
5744 return [cy.collection(left), cy.collection(right)];
5745 }
5746 } else {
5747 if (root.value) {
5748 return [cy.collection(root.value)];
5749 } else {
5750 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5751 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5752 return left.concat(right);
5753 }
5754 }
5755 };
5756 /* eslint-enable */
5757
5758
5759 var hierarchicalClustering = function hierarchicalClustering(options) {
5760 var cy = this.cy();
5761 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5762
5763 var opts = setOptions$2(options);
5764 var attrs = opts.attributes;
5765
5766 var getDist = function getDist(n1, n2) {
5767 return clusteringDistance(opts.distance, attrs.length, function (i) {
5768 return attrs[i](n1);
5769 }, function (i) {
5770 return attrs[i](n2);
5771 }, n1, n2);
5772 }; // Begin hierarchical algorithm
5773
5774
5775 var clusters = [];
5776 var dists = []; // distances between each pair of clusters
5777
5778 var mins = []; // closest cluster for each cluster
5779
5780 var index = []; // hash of all clusters by key
5781 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5782
5783 for (var n = 0; n < nodes.length; n++) {
5784 var cluster = {
5785 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5786 key: n,
5787 index: n
5788 };
5789 clusters[n] = cluster;
5790 index[n] = cluster;
5791 dists[n] = [];
5792 mins[n] = 0;
5793 } // Calculate the distance between each pair of clusters
5794
5795
5796 for (var i = 0; i < clusters.length; i++) {
5797 for (var j = 0; j <= i; j++) {
5798 var dist = void 0;
5799
5800 if (opts.mode === 'dendrogram') {
5801 // modes store cluster values differently
5802 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5803 } else {
5804 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5805 }
5806
5807 dists[i][j] = dist;
5808 dists[j][i] = dist;
5809
5810 if (dist < dists[i][mins[i]]) {
5811 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5812 }
5813 }
5814 } // Find the closest pair of clusters and merge them into a single cluster.
5815 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5816
5817
5818 var merged = mergeClosest(clusters, index, dists, mins, opts);
5819
5820 while (merged) {
5821 merged = mergeClosest(clusters, index, dists, mins, opts);
5822 }
5823
5824 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5825 // in addition to returning the clusters.
5826
5827 if (opts.mode === 'dendrogram') {
5828 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5829 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5830 } else {
5831 // Regular mode simply returns the clusters
5832 retClusters = new Array(clusters.length);
5833 clusters.forEach(function (cluster, i) {
5834 // Clean up meta data used for clustering
5835 cluster.key = cluster.index = null;
5836 retClusters[i] = cy.collection(cluster.value);
5837 });
5838 }
5839
5840 return retClusters;
5841 };
5842
5843 var hierarchicalClustering$1 = {
5844 hierarchicalClustering: hierarchicalClustering,
5845 hca: hierarchicalClustering
5846 };
5847
5848 // Implemented by Zoe Xi @zoexi for GSOC 2016
5849 var defaults$7 = defaults({
5850 distance: 'euclidean',
5851 // distance metric to compare attributes between two nodes
5852 preference: 'median',
5853 // suitability of a data point to serve as an exemplar
5854 damping: 0.8,
5855 // damping factor between [0.5, 1)
5856 maxIterations: 1000,
5857 // max number of iterations to run
5858 minIterations: 100,
5859 // min number of iterations to run in order for clustering to stop
5860 attributes: [// functions to quantify the similarity between any two points
5861 // e.g. node => node.data('weight')
5862 ]
5863 });
5864
5865 var setOptions$3 = function setOptions(options) {
5866 var dmp = options.damping;
5867 var pref = options.preference;
5868
5869 if (!(0.5 <= dmp && dmp < 1)) {
5870 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5871 }
5872
5873 var validPrefs = ['median', 'mean', 'min', 'max'];
5874
5875 if (!(validPrefs.some(function (v) {
5876 return v === pref;
5877 }) || number(pref))) {
5878 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5879 return "'".concat(p, "'");
5880 }).join(', '), "] or a number. Got: ").concat(pref));
5881 }
5882
5883 return defaults$7(options);
5884 };
5885 /* eslint-enable */
5886
5887
5888 var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5889 var attr = function attr(n, i) {
5890 return attributes[i](n);
5891 }; // nb negative because similarity should have an inverse relationship to distance
5892
5893
5894 return -clusteringDistance(type, attributes.length, function (i) {
5895 return attr(n1, i);
5896 }, function (i) {
5897 return attr(n2, i);
5898 }, n1, n2);
5899 };
5900
5901 var getPreference = function getPreference(S, preference) {
5902 // larger preference = greater # of clusters
5903 var p = null;
5904
5905 if (preference === 'median') {
5906 p = median(S);
5907 } else if (preference === 'mean') {
5908 p = mean(S);
5909 } else if (preference === 'min') {
5910 p = min(S);
5911 } else if (preference === 'max') {
5912 p = max(S);
5913 } else {
5914 // Custom preference number, as set by user
5915 p = preference;
5916 }
5917
5918 return p;
5919 };
5920
5921 var findExemplars = function findExemplars(n, R, A) {
5922 var indices = [];
5923
5924 for (var i = 0; i < n; i++) {
5925 if (R[i * n + i] + A[i * n + i] > 0) {
5926 indices.push(i);
5927 }
5928 }
5929
5930 return indices;
5931 };
5932
5933 var assignClusters = function assignClusters(n, S, exemplars) {
5934 var clusters = [];
5935
5936 for (var i = 0; i < n; i++) {
5937 var index = -1;
5938 var max = -Infinity;
5939
5940 for (var ei = 0; ei < exemplars.length; ei++) {
5941 var e = exemplars[ei];
5942
5943 if (S[i * n + e] > max) {
5944 index = e;
5945 max = S[i * n + e];
5946 }
5947 }
5948
5949 if (index > 0) {
5950 clusters.push(index);
5951 }
5952 }
5953
5954 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5955 clusters[exemplars[_ei]] = exemplars[_ei];
5956 }
5957
5958 return clusters;
5959 };
5960
5961 var assign$2 = function assign(n, S, exemplars) {
5962 var clusters = assignClusters(n, S, exemplars);
5963
5964 for (var ei = 0; ei < exemplars.length; ei++) {
5965 var ii = [];
5966
5967 for (var c = 0; c < clusters.length; c++) {
5968 if (clusters[c] === exemplars[ei]) {
5969 ii.push(c);
5970 }
5971 }
5972
5973 var maxI = -1;
5974 var maxSum = -Infinity;
5975
5976 for (var i = 0; i < ii.length; i++) {
5977 var sum = 0;
5978
5979 for (var j = 0; j < ii.length; j++) {
5980 sum += S[ii[j] * n + ii[i]];
5981 }
5982
5983 if (sum > maxSum) {
5984 maxI = i;
5985 maxSum = sum;
5986 }
5987 }
5988
5989 exemplars[ei] = ii[maxI];
5990 }
5991
5992 clusters = assignClusters(n, S, exemplars);
5993 return clusters;
5994 };
5995
5996 var affinityPropagation = function affinityPropagation(options) {
5997 var cy = this.cy();
5998 var nodes = this.nodes();
5999 var opts = setOptions$3(options); // Map each node to its position in node array
6000
6001 var id2position = {};
6002
6003 for (var i = 0; i < nodes.length; i++) {
6004 id2position[nodes[i].id()] = i;
6005 } // Begin affinity propagation algorithm
6006
6007
6008 var n; // number of data points
6009
6010 var n2; // size of matrices
6011
6012 var S; // similarity matrix (1D array)
6013
6014 var p; // preference/suitability of a data point to serve as an exemplar
6015
6016 var R; // responsibility matrix (1D array)
6017
6018 var A; // availability matrix (1D array)
6019
6020 n = nodes.length;
6021 n2 = n * n; // Initialize and build S similarity matrix
6022
6023 S = new Array(n2);
6024
6025 for (var _i = 0; _i < n2; _i++) {
6026 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
6027 }
6028
6029 for (var _i2 = 0; _i2 < n; _i2++) {
6030 for (var j = 0; j < n; j++) {
6031 if (_i2 !== j) {
6032 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
6033 }
6034 }
6035 } // Place preferences on the diagonal of S
6036
6037
6038 p = getPreference(S, opts.preference);
6039
6040 for (var _i3 = 0; _i3 < n; _i3++) {
6041 S[_i3 * n + _i3] = p;
6042 } // Initialize R responsibility matrix
6043
6044
6045 R = new Array(n2);
6046
6047 for (var _i4 = 0; _i4 < n2; _i4++) {
6048 R[_i4] = 0.0;
6049 } // Initialize A availability matrix
6050
6051
6052 A = new Array(n2);
6053
6054 for (var _i5 = 0; _i5 < n2; _i5++) {
6055 A[_i5] = 0.0;
6056 }
6057
6058 var old = new Array(n);
6059 var Rp = new Array(n);
6060 var se = new Array(n);
6061
6062 for (var _i6 = 0; _i6 < n; _i6++) {
6063 old[_i6] = 0.0;
6064 Rp[_i6] = 0.0;
6065 se[_i6] = 0;
6066 }
6067
6068 var e = new Array(n * opts.minIterations);
6069
6070 for (var _i7 = 0; _i7 < e.length; _i7++) {
6071 e[_i7] = 0;
6072 }
6073
6074 var iter;
6075
6076 for (iter = 0; iter < opts.maxIterations; iter++) {
6077 // main algorithmic loop
6078 // Update R responsibility matrix
6079 for (var _i8 = 0; _i8 < n; _i8++) {
6080 var max = -Infinity,
6081 max2 = -Infinity,
6082 maxI = -1,
6083 AS = 0.0;
6084
6085 for (var _j = 0; _j < n; _j++) {
6086 old[_j] = R[_i8 * n + _j];
6087 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
6088
6089 if (AS >= max) {
6090 max2 = max;
6091 max = AS;
6092 maxI = _j;
6093 } else if (AS > max2) {
6094 max2 = AS;
6095 }
6096 }
6097
6098 for (var _j2 = 0; _j2 < n; _j2++) {
6099 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
6100 }
6101
6102 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
6103 } // Update A availability matrix
6104
6105
6106 for (var _i9 = 0; _i9 < n; _i9++) {
6107 var sum = 0;
6108
6109 for (var _j3 = 0; _j3 < n; _j3++) {
6110 old[_j3] = A[_j3 * n + _i9];
6111 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
6112 sum += Rp[_j3];
6113 }
6114
6115 sum -= Rp[_i9];
6116 Rp[_i9] = R[_i9 * n + _i9];
6117 sum += Rp[_i9];
6118
6119 for (var _j4 = 0; _j4 < n; _j4++) {
6120 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
6121 }
6122
6123 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
6124 } // Check for convergence
6125
6126
6127 var K = 0;
6128
6129 for (var _i10 = 0; _i10 < n; _i10++) {
6130 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
6131 e[iter % opts.minIterations * n + _i10] = E;
6132 K += E;
6133 }
6134
6135 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
6136 var _sum = 0;
6137
6138 for (var _i11 = 0; _i11 < n; _i11++) {
6139 se[_i11] = 0;
6140
6141 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
6142 se[_i11] += e[_j5 * n + _i11];
6143 }
6144
6145 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
6146 _sum++;
6147 }
6148 }
6149
6150 if (_sum === n) {
6151 // then we have convergence
6152 break;
6153 }
6154 }
6155 } // Identify exemplars (cluster centers)
6156
6157
6158 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
6159
6160 var clusterIndices = assign$2(n, S, exemplarsIndices);
6161 var clusters = {};
6162
6163 for (var c = 0; c < exemplarsIndices.length; c++) {
6164 clusters[exemplarsIndices[c]] = [];
6165 }
6166
6167 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
6168 var pos = id2position[nodes[_i12].id()];
6169
6170 var clusterIndex = clusterIndices[pos];
6171
6172 if (clusterIndex != null) {
6173 // the node may have not been assigned a cluster if no valid attributes were specified
6174 clusters[clusterIndex].push(nodes[_i12]);
6175 }
6176 }
6177
6178 var retClusters = new Array(exemplarsIndices.length);
6179
6180 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
6181 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
6182 }
6183
6184 return retClusters;
6185 };
6186
6187 var affinityPropagation$1 = {
6188 affinityPropagation: affinityPropagation,
6189 ap: affinityPropagation
6190 };
6191
6192 var hierholzerDefaults = defaults({
6193 root: undefined,
6194 directed: false
6195 });
6196 var elesfn$b = {
6197 hierholzer: function hierholzer(options) {
6198 if (!plainObject(options)) {
6199 var args = arguments;
6200 options = {
6201 root: args[0],
6202 directed: args[1]
6203 };
6204 }
6205
6206 var _hierholzerDefaults = hierholzerDefaults(options),
6207 root = _hierholzerDefaults.root,
6208 directed = _hierholzerDefaults.directed;
6209
6210 var eles = this;
6211 var dflag = false;
6212 var oddIn;
6213 var oddOut;
6214 var startVertex;
6215 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
6216 var nodes = {};
6217 var edges = {};
6218
6219 if (directed) {
6220 eles.forEach(function (ele) {
6221 var id = ele.id();
6222
6223 if (ele.isNode()) {
6224 var ind = ele.indegree(true);
6225 var outd = ele.outdegree(true);
6226 var d1 = ind - outd;
6227 var d2 = outd - ind;
6228
6229 if (d1 == 1) {
6230 if (oddIn) dflag = true;else oddIn = id;
6231 } else if (d2 == 1) {
6232 if (oddOut) dflag = true;else oddOut = id;
6233 } else if (d2 > 1 || d1 > 1) {
6234 dflag = true;
6235 }
6236
6237 nodes[id] = [];
6238 ele.outgoers().forEach(function (e) {
6239 if (e.isEdge()) nodes[id].push(e.id());
6240 });
6241 } else {
6242 edges[id] = [undefined, ele.target().id()];
6243 }
6244 });
6245 } else {
6246 eles.forEach(function (ele) {
6247 var id = ele.id();
6248
6249 if (ele.isNode()) {
6250 var d = ele.degree(true);
6251
6252 if (d % 2) {
6253 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
6254 }
6255
6256 nodes[id] = [];
6257 ele.connectedEdges().forEach(function (e) {
6258 return nodes[id].push(e.id());
6259 });
6260 } else {
6261 edges[id] = [ele.source().id(), ele.target().id()];
6262 }
6263 });
6264 }
6265
6266 var result = {
6267 found: false,
6268 trail: undefined
6269 };
6270 if (dflag) return result;else if (oddOut && oddIn) {
6271 if (directed) {
6272 if (startVertex && oddOut != startVertex) {
6273 return result;
6274 }
6275
6276 startVertex = oddOut;
6277 } else {
6278 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
6279 return result;
6280 } else if (!startVertex) {
6281 startVertex = oddOut;
6282 }
6283 }
6284 } else {
6285 if (!startVertex) startVertex = eles[0].id();
6286 }
6287
6288 var walk = function walk(v) {
6289 var currentNode = v;
6290 var subtour = [v];
6291 var adj, adjTail, adjHead;
6292
6293 while (nodes[currentNode].length) {
6294 adj = nodes[currentNode].shift();
6295 adjTail = edges[adj][0];
6296 adjHead = edges[adj][1];
6297
6298 if (currentNode != adjHead) {
6299 nodes[adjHead] = nodes[adjHead].filter(function (e) {
6300 return e != adj;
6301 });
6302 currentNode = adjHead;
6303 } else if (!directed && currentNode != adjTail) {
6304 nodes[adjTail] = nodes[adjTail].filter(function (e) {
6305 return e != adj;
6306 });
6307 currentNode = adjTail;
6308 }
6309
6310 subtour.unshift(adj);
6311 subtour.unshift(currentNode);
6312 }
6313
6314 return subtour;
6315 };
6316
6317 var trail = [];
6318 var subtour = [];
6319 subtour = walk(startVertex);
6320
6321 while (subtour.length != 1) {
6322 if (nodes[subtour[0]].length == 0) {
6323 trail.unshift(eles.getElementById(subtour.shift()));
6324 trail.unshift(eles.getElementById(subtour.shift()));
6325 } else {
6326 subtour = walk(subtour.shift()).concat(subtour);
6327 }
6328 }
6329
6330 trail.unshift(eles.getElementById(subtour.shift())); // final node
6331
6332 for (var d in nodes) {
6333 if (nodes[d].length) {
6334 return result;
6335 }
6336 }
6337
6338 result.found = true;
6339 result.trail = this.spawn(trail, true);
6340 return result;
6341 }
6342 };
6343
6344 var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
6345 var eles = this;
6346 var nodes = {};
6347 var id = 0;
6348 var edgeCount = 0;
6349 var components = [];
6350 var stack = [];
6351 var visitedEdges = {};
6352
6353 var buildComponent = function buildComponent(x, y) {
6354 var i = stack.length - 1;
6355 var cutset = [];
6356 var component = eles.spawn();
6357
6358 while (stack[i].x != x || stack[i].y != y) {
6359 cutset.push(stack.pop().edge);
6360 i--;
6361 }
6362
6363 cutset.push(stack.pop().edge);
6364 cutset.forEach(function (edge) {
6365 var connectedNodes = edge.connectedNodes().intersection(eles);
6366 component.merge(edge);
6367 connectedNodes.forEach(function (node) {
6368 var nodeId = node.id();
6369 var connectedEdges = node.connectedEdges().intersection(eles);
6370 component.merge(node);
6371
6372 if (!nodes[nodeId].cutVertex) {
6373 component.merge(connectedEdges);
6374 } else {
6375 component.merge(connectedEdges.filter(function (edge) {
6376 return edge.isLoop();
6377 }));
6378 }
6379 });
6380 });
6381 components.push(component);
6382 };
6383
6384 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
6385 if (root === parent) edgeCount += 1;
6386 nodes[currentNode] = {
6387 id: id,
6388 low: id++,
6389 cutVertex: false
6390 };
6391 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
6392
6393 if (edges.size() === 0) {
6394 components.push(eles.spawn(eles.getElementById(currentNode)));
6395 } else {
6396 var sourceId, targetId, otherNodeId, edgeId;
6397 edges.forEach(function (edge) {
6398 sourceId = edge.source().id();
6399 targetId = edge.target().id();
6400 otherNodeId = sourceId === currentNode ? targetId : sourceId;
6401
6402 if (otherNodeId !== parent) {
6403 edgeId = edge.id();
6404
6405 if (!visitedEdges[edgeId]) {
6406 visitedEdges[edgeId] = true;
6407 stack.push({
6408 x: currentNode,
6409 y: otherNodeId,
6410 edge: edge
6411 });
6412 }
6413
6414 if (!(otherNodeId in nodes)) {
6415 biconnectedSearch(root, otherNodeId, currentNode);
6416 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6417
6418 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6419 nodes[currentNode].cutVertex = true;
6420 buildComponent(currentNode, otherNodeId);
6421 }
6422 } else {
6423 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6424 }
6425 }
6426 });
6427 }
6428 };
6429
6430 eles.forEach(function (ele) {
6431 if (ele.isNode()) {
6432 var nodeId = ele.id();
6433
6434 if (!(nodeId in nodes)) {
6435 edgeCount = 0;
6436 biconnectedSearch(nodeId, nodeId);
6437 nodes[nodeId].cutVertex = edgeCount > 1;
6438 }
6439 }
6440 });
6441 var cutVertices = Object.keys(nodes).filter(function (id) {
6442 return nodes[id].cutVertex;
6443 }).map(function (id) {
6444 return eles.getElementById(id);
6445 });
6446 return {
6447 cut: eles.spawn(cutVertices),
6448 components: components
6449 };
6450 };
6451
6452 var hopcroftTarjanBiconnected$1 = {
6453 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6454 htbc: hopcroftTarjanBiconnected,
6455 htb: hopcroftTarjanBiconnected,
6456 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6457 };
6458
6459 var tarjanStronglyConnected = function tarjanStronglyConnected() {
6460 var eles = this;
6461 var nodes = {};
6462 var index = 0;
6463 var components = [];
6464 var stack = [];
6465 var cut = eles.spawn(eles);
6466
6467 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6468 stack.push(sourceNodeId);
6469 nodes[sourceNodeId] = {
6470 index: index,
6471 low: index++,
6472 explored: false
6473 };
6474 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6475 connectedEdges.forEach(function (edge) {
6476 var targetNodeId = edge.target().id();
6477
6478 if (targetNodeId !== sourceNodeId) {
6479 if (!(targetNodeId in nodes)) {
6480 stronglyConnectedSearch(targetNodeId);
6481 }
6482
6483 if (!nodes[targetNodeId].explored) {
6484 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6485 }
6486 }
6487 });
6488
6489 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6490 var componentNodes = eles.spawn();
6491
6492 for (;;) {
6493 var nodeId = stack.pop();
6494 componentNodes.merge(eles.getElementById(nodeId));
6495 nodes[nodeId].low = nodes[sourceNodeId].index;
6496 nodes[nodeId].explored = true;
6497
6498 if (nodeId === sourceNodeId) {
6499 break;
6500 }
6501 }
6502
6503 var componentEdges = componentNodes.edgesWith(componentNodes);
6504 var component = componentNodes.merge(componentEdges);
6505 components.push(component);
6506 cut = cut.difference(component);
6507 }
6508 };
6509
6510 eles.forEach(function (ele) {
6511 if (ele.isNode()) {
6512 var nodeId = ele.id();
6513
6514 if (!(nodeId in nodes)) {
6515 stronglyConnectedSearch(nodeId);
6516 }
6517 }
6518 });
6519 return {
6520 cut: cut,
6521 components: components
6522 };
6523 };
6524
6525 var tarjanStronglyConnected$1 = {
6526 tarjanStronglyConnected: tarjanStronglyConnected,
6527 tsc: tarjanStronglyConnected,
6528 tscc: tarjanStronglyConnected,
6529 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6530 };
6531
6532 var elesfn$c = {};
6533 [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) {
6534 extend(elesfn$c, props);
6535 });
6536
6537 /*!
6538 Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6539 Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6540 Licensed under The MIT License (http://opensource.org/licenses/MIT)
6541 */
6542
6543 /* promise states [Promises/A+ 2.1] */
6544 var STATE_PENDING = 0;
6545 /* [Promises/A+ 2.1.1] */
6546
6547 var STATE_FULFILLED = 1;
6548 /* [Promises/A+ 2.1.2] */
6549
6550 var STATE_REJECTED = 2;
6551 /* [Promises/A+ 2.1.3] */
6552
6553 /* promise object constructor */
6554
6555 var api = function api(executor) {
6556 /* optionally support non-constructor/plain-function call */
6557 if (!(this instanceof api)) return new api(executor);
6558 /* initialize object */
6559
6560 this.id = 'Thenable/1.0.7';
6561 this.state = STATE_PENDING;
6562 /* initial state */
6563
6564 this.fulfillValue = undefined;
6565 /* initial value */
6566
6567 /* [Promises/A+ 1.3, 2.1.2.2] */
6568
6569 this.rejectReason = undefined;
6570 /* initial reason */
6571
6572 /* [Promises/A+ 1.5, 2.1.3.2] */
6573
6574 this.onFulfilled = [];
6575 /* initial handlers */
6576
6577 this.onRejected = [];
6578 /* initial handlers */
6579
6580 /* provide optional information-hiding proxy */
6581
6582 this.proxy = {
6583 then: this.then.bind(this)
6584 };
6585 /* support optional executor function */
6586
6587 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6588 };
6589 /* promise API methods */
6590
6591
6592 api.prototype = {
6593 /* promise resolving methods */
6594 fulfill: function fulfill(value) {
6595 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6596 },
6597 reject: function reject(value) {
6598 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6599 },
6600
6601 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6602 then: function then(onFulfilled, onRejected) {
6603 var curr = this;
6604 var next = new api();
6605 /* [Promises/A+ 2.2.7] */
6606
6607 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
6608 /* [Promises/A+ 2.2.2/2.2.6] */
6609
6610 curr.onRejected.push(resolver(onRejected, next, 'reject'));
6611 /* [Promises/A+ 2.2.3/2.2.6] */
6612
6613 execute(curr);
6614 return next.proxy;
6615 /* [Promises/A+ 2.2.7, 3.3] */
6616 }
6617 };
6618 /* deliver an action */
6619
6620 var deliver = function deliver(curr, state, name, value) {
6621 if (curr.state === STATE_PENDING) {
6622 curr.state = state;
6623 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6624
6625 curr[name] = value;
6626 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6627
6628 execute(curr);
6629 }
6630
6631 return curr;
6632 };
6633 /* execute all handlers */
6634
6635
6636 var execute = function execute(curr) {
6637 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6638 };
6639 /* execute particular set of handlers */
6640
6641
6642 var execute_handlers = function execute_handlers(curr, name, value) {
6643 /* global setImmediate: true */
6644
6645 /* global setTimeout: true */
6646
6647 /* short-circuit processing */
6648 if (curr[name].length === 0) return;
6649 /* iterate over all handlers, exactly once */
6650
6651 var handlers = curr[name];
6652 curr[name] = [];
6653 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6654
6655 var func = function func() {
6656 for (var i = 0; i < handlers.length; i++) {
6657 handlers[i](value);
6658 }
6659 /* [Promises/A+ 2.2.5] */
6660
6661 };
6662 /* execute procedure asynchronously */
6663
6664 /* [Promises/A+ 2.2.4, 3.1] */
6665
6666
6667 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6668 };
6669 /* generate a resolver function */
6670
6671
6672 var resolver = function resolver(cb, next, method) {
6673 return function (value) {
6674 if (typeof cb !== 'function')
6675 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6676 next[method].call(next, value);
6677 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
6678 else {
6679 var result;
6680
6681 try {
6682 result = cb(value);
6683 }
6684 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
6685 catch (e) {
6686 next.reject(e);
6687 /* [Promises/A+ 2.2.7.2] */
6688
6689 return;
6690 }
6691
6692 resolve(next, result);
6693 /* [Promises/A+ 2.2.7.1] */
6694 }
6695 };
6696 };
6697 /* "Promise Resolution Procedure" */
6698
6699 /* [Promises/A+ 2.3] */
6700
6701
6702 var resolve = function resolve(promise, x) {
6703 /* sanity check arguments */
6704
6705 /* [Promises/A+ 2.3.1] */
6706 if (promise === x || promise.proxy === x) {
6707 promise.reject(new TypeError('cannot resolve promise with itself'));
6708 return;
6709 }
6710 /* surgically check for a "then" method
6711 (mainly to just call the "getter" of "then" only once) */
6712
6713
6714 var then;
6715
6716 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6717 try {
6718 then = x.then;
6719 }
6720 /* [Promises/A+ 2.3.3.1, 3.5] */
6721 catch (e) {
6722 promise.reject(e);
6723 /* [Promises/A+ 2.3.3.2] */
6724
6725 return;
6726 }
6727 }
6728 /* handle own Thenables [Promises/A+ 2.3.2]
6729 and similar "thenables" [Promises/A+ 2.3.3] */
6730
6731
6732 if (typeof then === 'function') {
6733 var resolved = false;
6734
6735 try {
6736 /* call retrieved "then" method */
6737
6738 /* [Promises/A+ 2.3.3.3] */
6739 then.call(x,
6740 /* resolvePromise */
6741
6742 /* [Promises/A+ 2.3.3.3.1] */
6743 function (y) {
6744 if (resolved) return;
6745 resolved = true;
6746 /* [Promises/A+ 2.3.3.3.3] */
6747
6748 if (y === x)
6749 /* [Promises/A+ 3.6] */
6750 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6751 },
6752 /* rejectPromise */
6753
6754 /* [Promises/A+ 2.3.3.3.2] */
6755 function (r) {
6756 if (resolved) return;
6757 resolved = true;
6758 /* [Promises/A+ 2.3.3.3.3] */
6759
6760 promise.reject(r);
6761 });
6762 } catch (e) {
6763 if (!resolved)
6764 /* [Promises/A+ 2.3.3.3.3] */
6765 promise.reject(e);
6766 /* [Promises/A+ 2.3.3.3.4] */
6767 }
6768
6769 return;
6770 }
6771 /* handle other values */
6772
6773
6774 promise.fulfill(x);
6775 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6776 }; // so we always have Promise.all()
6777
6778
6779 api.all = function (ps) {
6780 return new api(function (resolveAll, rejectAll) {
6781 var vals = new Array(ps.length);
6782 var doneCount = 0;
6783
6784 var fulfill = function fulfill(i, val) {
6785 vals[i] = val;
6786 doneCount++;
6787
6788 if (doneCount === ps.length) {
6789 resolveAll(vals);
6790 }
6791 };
6792
6793 for (var i = 0; i < ps.length; i++) {
6794 (function (i) {
6795 var p = ps[i];
6796 var isPromise = p != null && p.then != null;
6797
6798 if (isPromise) {
6799 p.then(function (val) {
6800 fulfill(i, val);
6801 }, function (err) {
6802 rejectAll(err);
6803 });
6804 } else {
6805 var val = p;
6806 fulfill(i, val);
6807 }
6808 })(i);
6809 }
6810 });
6811 };
6812
6813 api.resolve = function (val) {
6814 return new api(function (resolve, reject) {
6815 resolve(val);
6816 });
6817 };
6818
6819 api.reject = function (val) {
6820 return new api(function (resolve, reject) {
6821 reject(val);
6822 });
6823 };
6824
6825 var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6826
6827 var Animation = function Animation(target, opts, opts2) {
6828 var isCore = core(target);
6829 var isEle = !isCore;
6830
6831 var _p = this._private = extend({
6832 duration: 1000
6833 }, opts, opts2);
6834
6835 _p.target = target;
6836 _p.style = _p.style || _p.css;
6837 _p.started = false;
6838 _p.playing = false;
6839 _p.hooked = false;
6840 _p.applying = false;
6841 _p.progress = 0;
6842 _p.completes = [];
6843 _p.frames = [];
6844
6845 if (_p.complete && fn(_p.complete)) {
6846 _p.completes.push(_p.complete);
6847 }
6848
6849 if (isEle) {
6850 var pos = target.position();
6851 _p.startPosition = _p.startPosition || {
6852 x: pos.x,
6853 y: pos.y
6854 };
6855 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6856 }
6857
6858 if (isCore) {
6859 var pan = target.pan();
6860 _p.startPan = {
6861 x: pan.x,
6862 y: pan.y
6863 };
6864 _p.startZoom = target.zoom();
6865 } // for future timeline/animations impl
6866
6867
6868 this.length = 1;
6869 this[0] = this;
6870 };
6871
6872 var anifn = Animation.prototype;
6873 extend(anifn, {
6874 instanceString: function instanceString() {
6875 return 'animation';
6876 },
6877 hook: function hook() {
6878 var _p = this._private;
6879
6880 if (!_p.hooked) {
6881 // add to target's animation queue
6882 var q;
6883 var tAni = _p.target._private.animation;
6884
6885 if (_p.queue) {
6886 q = tAni.queue;
6887 } else {
6888 q = tAni.current;
6889 }
6890
6891 q.push(this); // add to the animation loop pool
6892
6893 if (elementOrCollection(_p.target)) {
6894 _p.target.cy().addToAnimationPool(_p.target);
6895 }
6896
6897 _p.hooked = true;
6898 }
6899
6900 return this;
6901 },
6902 play: function play() {
6903 var _p = this._private; // autorewind
6904
6905 if (_p.progress === 1) {
6906 _p.progress = 0;
6907 }
6908
6909 _p.playing = true;
6910 _p.started = false; // needs to be started by animation loop
6911
6912 _p.stopped = false;
6913 this.hook(); // the animation loop will start the animation...
6914
6915 return this;
6916 },
6917 playing: function playing() {
6918 return this._private.playing;
6919 },
6920 apply: function apply() {
6921 var _p = this._private;
6922 _p.applying = true;
6923 _p.started = false; // needs to be started by animation loop
6924
6925 _p.stopped = false;
6926 this.hook(); // the animation loop will apply the animation at this progress
6927
6928 return this;
6929 },
6930 applying: function applying() {
6931 return this._private.applying;
6932 },
6933 pause: function pause() {
6934 var _p = this._private;
6935 _p.playing = false;
6936 _p.started = false;
6937 return this;
6938 },
6939 stop: function stop() {
6940 var _p = this._private;
6941 _p.playing = false;
6942 _p.started = false;
6943 _p.stopped = true; // to be removed from animation queues
6944
6945 return this;
6946 },
6947 rewind: function rewind() {
6948 return this.progress(0);
6949 },
6950 fastforward: function fastforward() {
6951 return this.progress(1);
6952 },
6953 time: function time(t) {
6954 var _p = this._private;
6955
6956 if (t === undefined) {
6957 return _p.progress * _p.duration;
6958 } else {
6959 return this.progress(t / _p.duration);
6960 }
6961 },
6962 progress: function progress(p) {
6963 var _p = this._private;
6964 var wasPlaying = _p.playing;
6965
6966 if (p === undefined) {
6967 return _p.progress;
6968 } else {
6969 if (wasPlaying) {
6970 this.pause();
6971 }
6972
6973 _p.progress = p;
6974 _p.started = false;
6975
6976 if (wasPlaying) {
6977 this.play();
6978 }
6979 }
6980
6981 return this;
6982 },
6983 completed: function completed() {
6984 return this._private.progress === 1;
6985 },
6986 reverse: function reverse() {
6987 var _p = this._private;
6988 var wasPlaying = _p.playing;
6989
6990 if (wasPlaying) {
6991 this.pause();
6992 }
6993
6994 _p.progress = 1 - _p.progress;
6995 _p.started = false;
6996
6997 var swap = function swap(a, b) {
6998 var _pa = _p[a];
6999
7000 if (_pa == null) {
7001 return;
7002 }
7003
7004 _p[a] = _p[b];
7005 _p[b] = _pa;
7006 };
7007
7008 swap('zoom', 'startZoom');
7009 swap('pan', 'startPan');
7010 swap('position', 'startPosition'); // swap styles
7011
7012 if (_p.style) {
7013 for (var i = 0; i < _p.style.length; i++) {
7014 var prop = _p.style[i];
7015 var name = prop.name;
7016 var startStyleProp = _p.startStyle[name];
7017 _p.startStyle[name] = prop;
7018 _p.style[i] = startStyleProp;
7019 }
7020 }
7021
7022 if (wasPlaying) {
7023 this.play();
7024 }
7025
7026 return this;
7027 },
7028 promise: function promise(type) {
7029 var _p = this._private;
7030 var arr;
7031
7032 switch (type) {
7033 case 'frame':
7034 arr = _p.frames;
7035 break;
7036
7037 default:
7038 case 'complete':
7039 case 'completed':
7040 arr = _p.completes;
7041 }
7042
7043 return new Promise$1(function (resolve, reject) {
7044 arr.push(function () {
7045 resolve();
7046 });
7047 });
7048 }
7049 });
7050 anifn.complete = anifn.completed;
7051 anifn.run = anifn.play;
7052 anifn.running = anifn.playing;
7053
7054 var define = {
7055 animated: function animated() {
7056 return function animatedImpl() {
7057 var self = this;
7058 var selfIsArrayLike = self.length !== undefined;
7059 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7060
7061 var cy = this._private.cy || this;
7062
7063 if (!cy.styleEnabled()) {
7064 return false;
7065 }
7066
7067 var ele = all[0];
7068
7069 if (ele) {
7070 return ele._private.animation.current.length > 0;
7071 }
7072 };
7073 },
7074 // animated
7075 clearQueue: function clearQueue() {
7076 return function clearQueueImpl() {
7077 var self = this;
7078 var selfIsArrayLike = self.length !== undefined;
7079 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7080
7081 var cy = this._private.cy || this;
7082
7083 if (!cy.styleEnabled()) {
7084 return this;
7085 }
7086
7087 for (var i = 0; i < all.length; i++) {
7088 var ele = all[i];
7089 ele._private.animation.queue = [];
7090 }
7091
7092 return this;
7093 };
7094 },
7095 // clearQueue
7096 delay: function delay() {
7097 return function delayImpl(time, complete) {
7098 var cy = this._private.cy || this;
7099
7100 if (!cy.styleEnabled()) {
7101 return this;
7102 }
7103
7104 return this.animate({
7105 delay: time,
7106 duration: time,
7107 complete: complete
7108 });
7109 };
7110 },
7111 // delay
7112 delayAnimation: function delayAnimation() {
7113 return function delayAnimationImpl(time, complete) {
7114 var cy = this._private.cy || this;
7115
7116 if (!cy.styleEnabled()) {
7117 return this;
7118 }
7119
7120 return this.animation({
7121 delay: time,
7122 duration: time,
7123 complete: complete
7124 });
7125 };
7126 },
7127 // delay
7128 animation: function animation() {
7129 return function animationImpl(properties, params) {
7130 var self = this;
7131 var selfIsArrayLike = self.length !== undefined;
7132 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7133
7134 var cy = this._private.cy || this;
7135 var isCore = !selfIsArrayLike;
7136 var isEles = !isCore;
7137
7138 if (!cy.styleEnabled()) {
7139 return this;
7140 }
7141
7142 var style = cy.style();
7143 properties = extend({}, properties, params);
7144 var propertiesEmpty = Object.keys(properties).length === 0;
7145
7146 if (propertiesEmpty) {
7147 return new Animation(all[0], properties); // nothing to animate
7148 }
7149
7150 if (properties.duration === undefined) {
7151 properties.duration = 400;
7152 }
7153
7154 switch (properties.duration) {
7155 case 'slow':
7156 properties.duration = 600;
7157 break;
7158
7159 case 'fast':
7160 properties.duration = 200;
7161 break;
7162 }
7163
7164 if (isEles) {
7165 properties.style = style.getPropsList(properties.style || properties.css);
7166 properties.css = undefined;
7167 }
7168
7169 if (isEles && properties.renderedPosition != null) {
7170 var rpos = properties.renderedPosition;
7171 var pan = cy.pan();
7172 var zoom = cy.zoom();
7173 properties.position = renderedToModelPosition(rpos, zoom, pan);
7174 } // override pan w/ panBy if set
7175
7176
7177 if (isCore && properties.panBy != null) {
7178 var panBy = properties.panBy;
7179 var cyPan = cy.pan();
7180 properties.pan = {
7181 x: cyPan.x + panBy.x,
7182 y: cyPan.y + panBy.y
7183 };
7184 } // override pan w/ center if set
7185
7186
7187 var center = properties.center || properties.centre;
7188
7189 if (isCore && center != null) {
7190 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
7191
7192 if (centerPan != null) {
7193 properties.pan = centerPan;
7194 }
7195 } // override pan & zoom w/ fit if set
7196
7197
7198 if (isCore && properties.fit != null) {
7199 var fit = properties.fit;
7200 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
7201
7202 if (fitVp != null) {
7203 properties.pan = fitVp.pan;
7204 properties.zoom = fitVp.zoom;
7205 }
7206 } // override zoom (& potentially pan) w/ zoom obj if set
7207
7208
7209 if (isCore && plainObject(properties.zoom)) {
7210 var vp = cy.getZoomedViewport(properties.zoom);
7211
7212 if (vp != null) {
7213 if (vp.zoomed) {
7214 properties.zoom = vp.zoom;
7215 }
7216
7217 if (vp.panned) {
7218 properties.pan = vp.pan;
7219 }
7220 } else {
7221 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
7222 }
7223 }
7224
7225 return new Animation(all[0], properties);
7226 };
7227 },
7228 // animate
7229 animate: function animate() {
7230 return function animateImpl(properties, params) {
7231 var self = this;
7232 var selfIsArrayLike = self.length !== undefined;
7233 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7234
7235 var cy = this._private.cy || this;
7236
7237 if (!cy.styleEnabled()) {
7238 return this;
7239 }
7240
7241 if (params) {
7242 properties = extend({}, properties, params);
7243 } // manually hook and run the animation
7244
7245
7246 for (var i = 0; i < all.length; i++) {
7247 var ele = all[i];
7248 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
7249 var ani = ele.animation(properties, queue ? {
7250 queue: true
7251 } : undefined);
7252 ani.play();
7253 }
7254
7255 return this; // chaining
7256 };
7257 },
7258 // animate
7259 stop: function stop() {
7260 return function stopImpl(clearQueue, jumpToEnd) {
7261 var self = this;
7262 var selfIsArrayLike = self.length !== undefined;
7263 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7264
7265 var cy = this._private.cy || this;
7266
7267 if (!cy.styleEnabled()) {
7268 return this;
7269 }
7270
7271 for (var i = 0; i < all.length; i++) {
7272 var ele = all[i];
7273 var _p = ele._private;
7274 var anis = _p.animation.current;
7275
7276 for (var j = 0; j < anis.length; j++) {
7277 var ani = anis[j];
7278 var ani_p = ani._private;
7279
7280 if (jumpToEnd) {
7281 // next iteration of the animation loop, the animation
7282 // will go straight to the end and be removed
7283 ani_p.duration = 0;
7284 }
7285 } // clear the queue of future animations
7286
7287
7288 if (clearQueue) {
7289 _p.animation.queue = [];
7290 }
7291
7292 if (!jumpToEnd) {
7293 _p.animation.current = [];
7294 }
7295 } // we have to notify (the animation loop doesn't do it for us on `stop`)
7296
7297
7298 cy.notify('draw');
7299 return this;
7300 };
7301 } // stop
7302
7303 }; // define
7304
7305 var define$1 = {
7306 // access data field
7307 data: function data(params) {
7308 var defaults = {
7309 field: 'data',
7310 bindingEvent: 'data',
7311 allowBinding: false,
7312 allowSetting: false,
7313 allowGetting: false,
7314 settingEvent: 'data',
7315 settingTriggersEvent: false,
7316 triggerFnName: 'trigger',
7317 immutableKeys: {},
7318 // key => true if immutable
7319 updateStyle: false,
7320 beforeGet: function beforeGet(self) {},
7321 beforeSet: function beforeSet(self, obj) {},
7322 onSet: function onSet(self) {},
7323 canSet: function canSet(self) {
7324 return true;
7325 }
7326 };
7327 params = extend({}, defaults, params);
7328 return function dataImpl(name, value) {
7329 var p = params;
7330 var self = this;
7331 var selfIsArrayLike = self.length !== undefined;
7332 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7333
7334 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
7335
7336 if (string(name)) {
7337 // set or get property
7338 // .data('foo')
7339 if (p.allowGetting && value === undefined) {
7340 // get
7341 var ret;
7342
7343 if (single) {
7344 p.beforeGet(single);
7345 ret = single._private[p.field][name];
7346 }
7347
7348 return ret; // .data('foo', 'bar')
7349 } else if (p.allowSetting && value !== undefined) {
7350 // set
7351 var valid = !p.immutableKeys[name];
7352
7353 if (valid) {
7354 var change = _defineProperty({}, name, value);
7355
7356 p.beforeSet(self, change);
7357
7358 for (var i = 0, l = all.length; i < l; i++) {
7359 var ele = all[i];
7360
7361 if (p.canSet(ele)) {
7362 ele._private[p.field][name] = value;
7363 }
7364 } // update mappers if asked
7365
7366
7367 if (p.updateStyle) {
7368 self.updateStyle();
7369 } // call onSet callback
7370
7371
7372 p.onSet(self);
7373
7374 if (p.settingTriggersEvent) {
7375 self[p.triggerFnName](p.settingEvent);
7376 }
7377 }
7378 } // .data({ 'foo': 'bar' })
7379
7380 } else if (p.allowSetting && plainObject(name)) {
7381 // extend
7382 var obj = name;
7383 var k, v;
7384 var keys = Object.keys(obj);
7385 p.beforeSet(self, obj);
7386
7387 for (var _i = 0; _i < keys.length; _i++) {
7388 k = keys[_i];
7389 v = obj[k];
7390
7391 var _valid = !p.immutableKeys[k];
7392
7393 if (_valid) {
7394 for (var j = 0; j < all.length; j++) {
7395 var _ele = all[j];
7396
7397 if (p.canSet(_ele)) {
7398 _ele._private[p.field][k] = v;
7399 }
7400 }
7401 }
7402 } // update mappers if asked
7403
7404
7405 if (p.updateStyle) {
7406 self.updateStyle();
7407 } // call onSet callback
7408
7409
7410 p.onSet(self);
7411
7412 if (p.settingTriggersEvent) {
7413 self[p.triggerFnName](p.settingEvent);
7414 } // .data(function(){ ... })
7415
7416 } else if (p.allowBinding && fn(name)) {
7417 // bind to event
7418 var fn$1 = name;
7419 self.on(p.bindingEvent, fn$1); // .data()
7420 } else if (p.allowGetting && name === undefined) {
7421 // get whole object
7422 var _ret;
7423
7424 if (single) {
7425 p.beforeGet(single);
7426 _ret = single._private[p.field];
7427 }
7428
7429 return _ret;
7430 }
7431
7432 return self; // maintain chainability
7433 }; // function
7434 },
7435 // data
7436 // remove data field
7437 removeData: function removeData(params) {
7438 var defaults = {
7439 field: 'data',
7440 event: 'data',
7441 triggerFnName: 'trigger',
7442 triggerEvent: false,
7443 immutableKeys: {} // key => true if immutable
7444
7445 };
7446 params = extend({}, defaults, params);
7447 return function removeDataImpl(names) {
7448 var p = params;
7449 var self = this;
7450 var selfIsArrayLike = self.length !== undefined;
7451 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7452 // .removeData('foo bar')
7453
7454 if (string(names)) {
7455 // then get the list of keys, and delete them
7456 var keys = names.split(/\s+/);
7457 var l = keys.length;
7458
7459 for (var i = 0; i < l; i++) {
7460 // delete each non-empty key
7461 var key = keys[i];
7462
7463 if (emptyString(key)) {
7464 continue;
7465 }
7466
7467 var valid = !p.immutableKeys[key]; // not valid if immutable
7468
7469 if (valid) {
7470 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
7471 all[i_a]._private[p.field][key] = undefined;
7472 }
7473 }
7474 }
7475
7476 if (p.triggerEvent) {
7477 self[p.triggerFnName](p.event);
7478 } // .removeData()
7479
7480 } else if (names === undefined) {
7481 // then delete all keys
7482 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
7483 var _privateFields = all[_i_a]._private[p.field];
7484
7485 var _keys = Object.keys(_privateFields);
7486
7487 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
7488 var _key = _keys[_i2];
7489 var validKeyToDelete = !p.immutableKeys[_key];
7490
7491 if (validKeyToDelete) {
7492 _privateFields[_key] = undefined;
7493 }
7494 }
7495 }
7496
7497 if (p.triggerEvent) {
7498 self[p.triggerFnName](p.event);
7499 }
7500 }
7501
7502 return self; // maintain chaining
7503 }; // function
7504 } // removeData
7505
7506 }; // define
7507
7508 var define$2 = {
7509 eventAliasesOn: function eventAliasesOn(proto) {
7510 var p = proto;
7511 p.addListener = p.listen = p.bind = p.on;
7512 p.unlisten = p.unbind = p.off = p.removeListener;
7513 p.trigger = p.emit; // this is just a wrapper alias of .on()
7514
7515 p.pon = p.promiseOn = function (events, selector) {
7516 var self = this;
7517 var args = Array.prototype.slice.call(arguments, 0);
7518 return new Promise$1(function (resolve, reject) {
7519 var callback = function callback(e) {
7520 self.off.apply(self, offArgs);
7521 resolve(e);
7522 };
7523
7524 var onArgs = args.concat([callback]);
7525 var offArgs = onArgs.concat([]);
7526 self.on.apply(self, onArgs);
7527 });
7528 };
7529 }
7530 }; // define
7531
7532 // use this module to cherry pick functions into your prototype
7533 var define$3 = {};
7534 [define, define$1, define$2].forEach(function (m) {
7535 extend(define$3, m);
7536 });
7537
7538 var elesfn$d = {
7539 animate: define$3.animate(),
7540 animation: define$3.animation(),
7541 animated: define$3.animated(),
7542 clearQueue: define$3.clearQueue(),
7543 delay: define$3.delay(),
7544 delayAnimation: define$3.delayAnimation(),
7545 stop: define$3.stop()
7546 };
7547
7548 var elesfn$e = {
7549 classes: function classes(_classes) {
7550 var self = this;
7551
7552 if (_classes === undefined) {
7553 var ret = [];
7554
7555 self[0]._private.classes.forEach(function (cls) {
7556 return ret.push(cls);
7557 });
7558
7559 return ret;
7560 } else if (!array(_classes)) {
7561 // extract classes from string
7562 _classes = (_classes || '').match(/\S+/g) || [];
7563 }
7564
7565 var changed = [];
7566 var classesSet = new Set$1(_classes); // check and update each ele
7567
7568 for (var j = 0; j < self.length; j++) {
7569 var ele = self[j];
7570 var _p = ele._private;
7571 var eleClasses = _p.classes;
7572 var changedEle = false; // check if ele has all of the passed classes
7573
7574 for (var i = 0; i < _classes.length; i++) {
7575 var cls = _classes[i];
7576 var eleHasClass = eleClasses.has(cls);
7577
7578 if (!eleHasClass) {
7579 changedEle = true;
7580 break;
7581 }
7582 } // check if ele has classes outside of those passed
7583
7584
7585 if (!changedEle) {
7586 changedEle = eleClasses.size !== _classes.length;
7587 }
7588
7589 if (changedEle) {
7590 _p.classes = classesSet;
7591 changed.push(ele);
7592 }
7593 } // trigger update style on those eles that had class changes
7594
7595
7596 if (changed.length > 0) {
7597 this.spawn(changed).updateStyle().emit('class');
7598 }
7599
7600 return self;
7601 },
7602 addClass: function addClass(classes) {
7603 return this.toggleClass(classes, true);
7604 },
7605 hasClass: function hasClass(className) {
7606 var ele = this[0];
7607 return ele != null && ele._private.classes.has(className);
7608 },
7609 toggleClass: function toggleClass(classes, toggle) {
7610 if (!array(classes)) {
7611 // extract classes from string
7612 classes = classes.match(/\S+/g) || [];
7613 }
7614
7615 var self = this;
7616 var toggleUndefd = toggle === undefined;
7617 var changed = []; // eles who had classes changed
7618
7619 for (var i = 0, il = self.length; i < il; i++) {
7620 var ele = self[i];
7621 var eleClasses = ele._private.classes;
7622 var changedEle = false;
7623
7624 for (var j = 0; j < classes.length; j++) {
7625 var cls = classes[j];
7626 var hasClass = eleClasses.has(cls);
7627 var changedNow = false;
7628
7629 if (toggle || toggleUndefd && !hasClass) {
7630 eleClasses.add(cls);
7631 changedNow = true;
7632 } else if (!toggle || toggleUndefd && hasClass) {
7633 eleClasses["delete"](cls);
7634 changedNow = true;
7635 }
7636
7637 if (!changedEle && changedNow) {
7638 changed.push(ele);
7639 changedEle = true;
7640 }
7641 } // for j classes
7642
7643 } // for i eles
7644 // trigger update style on those eles that had class changes
7645
7646
7647 if (changed.length > 0) {
7648 this.spawn(changed).updateStyle().emit('class');
7649 }
7650
7651 return self;
7652 },
7653 removeClass: function removeClass(classes) {
7654 return this.toggleClass(classes, false);
7655 },
7656 flashClass: function flashClass(classes, duration) {
7657 var self = this;
7658
7659 if (duration == null) {
7660 duration = 250;
7661 } else if (duration === 0) {
7662 return self; // nothing to do really
7663 }
7664
7665 self.addClass(classes);
7666 setTimeout(function () {
7667 self.removeClass(classes);
7668 }, duration);
7669 return self;
7670 }
7671 };
7672 elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
7673
7674 var tokens = {
7675 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
7676 // chars we need to escape in let names, etc
7677 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
7678 // binary comparison op (used in data selectors)
7679 boolOp: '\\?|\\!|\\^',
7680 // boolean (unary) operators (used in data selectors)
7681 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
7682 // string literals (used in data selectors) -- doublequotes | singlequotes
7683 number: number$1,
7684 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
7685 meta: 'degree|indegree|outdegree',
7686 // allowed metadata fields (i.e. allowed functions to use from Collection)
7687 separator: '\\s*,\\s*',
7688 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
7689 descendant: '\\s+',
7690 child: '\\s+>\\s+',
7691 subject: '\\$',
7692 group: 'node|edge|\\*',
7693 directedEdge: '\\s+->\\s+',
7694 undirectedEdge: '\\s+<->\\s+'
7695 };
7696 tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
7697
7698 tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
7699
7700 tokens.className = tokens.variable; // a class name (follows variable conventions)
7701
7702 tokens.id = tokens.variable; // an element id (follows variable conventions)
7703
7704 (function () {
7705 var ops, op, i; // add @ variants to comparatorOp
7706
7707 ops = tokens.comparatorOp.split('|');
7708
7709 for (i = 0; i < ops.length; i++) {
7710 op = ops[i];
7711 tokens.comparatorOp += '|@' + op;
7712 } // add ! variants to comparatorOp
7713
7714
7715 ops = tokens.comparatorOp.split('|');
7716
7717 for (i = 0; i < ops.length; i++) {
7718 op = ops[i];
7719
7720 if (op.indexOf('!') >= 0) {
7721 continue;
7722 } // skip ops that explicitly contain !
7723
7724
7725 if (op === '=') {
7726 continue;
7727 } // skip = b/c != is explicitly defined
7728
7729
7730 tokens.comparatorOp += '|\\!' + op;
7731 }
7732 })();
7733
7734 /**
7735 * Make a new query object
7736 *
7737 * @prop type {Type} The type enum (int) of the query
7738 * @prop checks List of checks to make against an ele to test for a match
7739 */
7740 var newQuery = function newQuery() {
7741 return {
7742 checks: []
7743 };
7744 };
7745
7746 /**
7747 * A check type enum-like object. Uses integer values for fast match() lookup.
7748 * The ordering does not matter as long as the ints are unique.
7749 */
7750 var Type = {
7751 /** E.g. node */
7752 GROUP: 0,
7753
7754 /** A collection of elements */
7755 COLLECTION: 1,
7756
7757 /** A filter(ele) function */
7758 FILTER: 2,
7759
7760 /** E.g. [foo > 1] */
7761 DATA_COMPARE: 3,
7762
7763 /** E.g. [foo] */
7764 DATA_EXIST: 4,
7765
7766 /** E.g. [?foo] */
7767 DATA_BOOL: 5,
7768
7769 /** E.g. [[degree > 2]] */
7770 META_COMPARE: 6,
7771
7772 /** E.g. :selected */
7773 STATE: 7,
7774
7775 /** E.g. #foo */
7776 ID: 8,
7777
7778 /** E.g. .foo */
7779 CLASS: 9,
7780
7781 /** E.g. #foo <-> #bar */
7782 UNDIRECTED_EDGE: 10,
7783
7784 /** E.g. #foo -> #bar */
7785 DIRECTED_EDGE: 11,
7786
7787 /** E.g. $#foo -> #bar */
7788 NODE_SOURCE: 12,
7789
7790 /** E.g. #foo -> $#bar */
7791 NODE_TARGET: 13,
7792
7793 /** E.g. $#foo <-> #bar */
7794 NODE_NEIGHBOR: 14,
7795
7796 /** E.g. #foo > #bar */
7797 CHILD: 15,
7798
7799 /** E.g. #foo #bar */
7800 DESCENDANT: 16,
7801
7802 /** E.g. $#foo > #bar */
7803 PARENT: 17,
7804
7805 /** E.g. $#foo #bar */
7806 ANCESTOR: 18,
7807
7808 /** E.g. #foo > $bar > #baz */
7809 COMPOUND_SPLIT: 19,
7810
7811 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7812 TRUE: 20
7813 };
7814
7815 var stateSelectors = [{
7816 selector: ':selected',
7817 matches: function matches(ele) {
7818 return ele.selected();
7819 }
7820 }, {
7821 selector: ':unselected',
7822 matches: function matches(ele) {
7823 return !ele.selected();
7824 }
7825 }, {
7826 selector: ':selectable',
7827 matches: function matches(ele) {
7828 return ele.selectable();
7829 }
7830 }, {
7831 selector: ':unselectable',
7832 matches: function matches(ele) {
7833 return !ele.selectable();
7834 }
7835 }, {
7836 selector: ':locked',
7837 matches: function matches(ele) {
7838 return ele.locked();
7839 }
7840 }, {
7841 selector: ':unlocked',
7842 matches: function matches(ele) {
7843 return !ele.locked();
7844 }
7845 }, {
7846 selector: ':visible',
7847 matches: function matches(ele) {
7848 return ele.visible();
7849 }
7850 }, {
7851 selector: ':hidden',
7852 matches: function matches(ele) {
7853 return !ele.visible();
7854 }
7855 }, {
7856 selector: ':transparent',
7857 matches: function matches(ele) {
7858 return ele.transparent();
7859 }
7860 }, {
7861 selector: ':grabbed',
7862 matches: function matches(ele) {
7863 return ele.grabbed();
7864 }
7865 }, {
7866 selector: ':free',
7867 matches: function matches(ele) {
7868 return !ele.grabbed();
7869 }
7870 }, {
7871 selector: ':removed',
7872 matches: function matches(ele) {
7873 return ele.removed();
7874 }
7875 }, {
7876 selector: ':inside',
7877 matches: function matches(ele) {
7878 return !ele.removed();
7879 }
7880 }, {
7881 selector: ':grabbable',
7882 matches: function matches(ele) {
7883 return ele.grabbable();
7884 }
7885 }, {
7886 selector: ':ungrabbable',
7887 matches: function matches(ele) {
7888 return !ele.grabbable();
7889 }
7890 }, {
7891 selector: ':animated',
7892 matches: function matches(ele) {
7893 return ele.animated();
7894 }
7895 }, {
7896 selector: ':unanimated',
7897 matches: function matches(ele) {
7898 return !ele.animated();
7899 }
7900 }, {
7901 selector: ':parent',
7902 matches: function matches(ele) {
7903 return ele.isParent();
7904 }
7905 }, {
7906 selector: ':childless',
7907 matches: function matches(ele) {
7908 return ele.isChildless();
7909 }
7910 }, {
7911 selector: ':child',
7912 matches: function matches(ele) {
7913 return ele.isChild();
7914 }
7915 }, {
7916 selector: ':orphan',
7917 matches: function matches(ele) {
7918 return ele.isOrphan();
7919 }
7920 }, {
7921 selector: ':nonorphan',
7922 matches: function matches(ele) {
7923 return ele.isChild();
7924 }
7925 }, {
7926 selector: ':compound',
7927 matches: function matches(ele) {
7928 if (ele.isNode()) {
7929 return ele.isParent();
7930 } else {
7931 return ele.source().isParent() || ele.target().isParent();
7932 }
7933 }
7934 }, {
7935 selector: ':loop',
7936 matches: function matches(ele) {
7937 return ele.isLoop();
7938 }
7939 }, {
7940 selector: ':simple',
7941 matches: function matches(ele) {
7942 return ele.isSimple();
7943 }
7944 }, {
7945 selector: ':active',
7946 matches: function matches(ele) {
7947 return ele.active();
7948 }
7949 }, {
7950 selector: ':inactive',
7951 matches: function matches(ele) {
7952 return !ele.active();
7953 }
7954 }, {
7955 selector: ':backgrounding',
7956 matches: function matches(ele) {
7957 return ele.backgrounding();
7958 }
7959 }, {
7960 selector: ':nonbackgrounding',
7961 matches: function matches(ele) {
7962 return !ele.backgrounding();
7963 }
7964 }].sort(function (a, b) {
7965 // n.b. selectors that are starting substrings of others must have the longer ones first
7966 return descending(a.selector, b.selector);
7967 });
7968
7969 var lookup = function () {
7970 var selToFn = {};
7971 var s;
7972
7973 for (var i = 0; i < stateSelectors.length; i++) {
7974 s = stateSelectors[i];
7975 selToFn[s.selector] = s.matches;
7976 }
7977
7978 return selToFn;
7979 }();
7980
7981 var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7982 return lookup[sel](ele);
7983 };
7984 var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7985 return s.selector;
7986 }).join('|') + ')';
7987
7988 // so that values get compared properly in Selector.filter()
7989
7990 var cleanMetaChars = function cleanMetaChars(str) {
7991 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7992 return $1;
7993 });
7994 };
7995
7996 var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7997 selector[selector.length - 1] = replacementQuery;
7998 }; // NOTE: add new expression syntax here to have it recognised by the parser;
7999 // - a query contains all adjacent (i.e. no separator in between) expressions;
8000 // - the current query is stored in selector[i]
8001 // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
8002
8003
8004 var exprs = [{
8005 name: 'group',
8006 // just used for identifying when debugging
8007 query: true,
8008 regex: '(' + tokens.group + ')',
8009 populate: function populate(selector, query, _ref) {
8010 var _ref2 = _slicedToArray(_ref, 1),
8011 group = _ref2[0];
8012
8013 query.checks.push({
8014 type: Type.GROUP,
8015 value: group === '*' ? group : group + 's'
8016 });
8017 }
8018 }, {
8019 name: 'state',
8020 query: true,
8021 regex: stateSelectorRegex,
8022 populate: function populate(selector, query, _ref3) {
8023 var _ref4 = _slicedToArray(_ref3, 1),
8024 state = _ref4[0];
8025
8026 query.checks.push({
8027 type: Type.STATE,
8028 value: state
8029 });
8030 }
8031 }, {
8032 name: 'id',
8033 query: true,
8034 regex: '\\#(' + tokens.id + ')',
8035 populate: function populate(selector, query, _ref5) {
8036 var _ref6 = _slicedToArray(_ref5, 1),
8037 id = _ref6[0];
8038
8039 query.checks.push({
8040 type: Type.ID,
8041 value: cleanMetaChars(id)
8042 });
8043 }
8044 }, {
8045 name: 'className',
8046 query: true,
8047 regex: '\\.(' + tokens.className + ')',
8048 populate: function populate(selector, query, _ref7) {
8049 var _ref8 = _slicedToArray(_ref7, 1),
8050 className = _ref8[0];
8051
8052 query.checks.push({
8053 type: Type.CLASS,
8054 value: cleanMetaChars(className)
8055 });
8056 }
8057 }, {
8058 name: 'dataExists',
8059 query: true,
8060 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
8061 populate: function populate(selector, query, _ref9) {
8062 var _ref10 = _slicedToArray(_ref9, 1),
8063 variable = _ref10[0];
8064
8065 query.checks.push({
8066 type: Type.DATA_EXIST,
8067 field: cleanMetaChars(variable)
8068 });
8069 }
8070 }, {
8071 name: 'dataCompare',
8072 query: true,
8073 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
8074 populate: function populate(selector, query, _ref11) {
8075 var _ref12 = _slicedToArray(_ref11, 3),
8076 variable = _ref12[0],
8077 comparatorOp = _ref12[1],
8078 value = _ref12[2];
8079
8080 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
8081
8082 if (valueIsString) {
8083 value = value.substring(1, value.length - 1);
8084 } else {
8085 value = parseFloat(value);
8086 }
8087
8088 query.checks.push({
8089 type: Type.DATA_COMPARE,
8090 field: cleanMetaChars(variable),
8091 operator: comparatorOp,
8092 value: value
8093 });
8094 }
8095 }, {
8096 name: 'dataBool',
8097 query: true,
8098 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
8099 populate: function populate(selector, query, _ref13) {
8100 var _ref14 = _slicedToArray(_ref13, 2),
8101 boolOp = _ref14[0],
8102 variable = _ref14[1];
8103
8104 query.checks.push({
8105 type: Type.DATA_BOOL,
8106 field: cleanMetaChars(variable),
8107 operator: boolOp
8108 });
8109 }
8110 }, {
8111 name: 'metaCompare',
8112 query: true,
8113 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
8114 populate: function populate(selector, query, _ref15) {
8115 var _ref16 = _slicedToArray(_ref15, 3),
8116 meta = _ref16[0],
8117 comparatorOp = _ref16[1],
8118 number = _ref16[2];
8119
8120 query.checks.push({
8121 type: Type.META_COMPARE,
8122 field: cleanMetaChars(meta),
8123 operator: comparatorOp,
8124 value: parseFloat(number)
8125 });
8126 }
8127 }, {
8128 name: 'nextQuery',
8129 separator: true,
8130 regex: tokens.separator,
8131 populate: function populate(selector, query) {
8132 var currentSubject = selector.currentSubject;
8133 var edgeCount = selector.edgeCount;
8134 var compoundCount = selector.compoundCount;
8135 var lastQ = selector[selector.length - 1];
8136
8137 if (currentSubject != null) {
8138 lastQ.subject = currentSubject;
8139 selector.currentSubject = null;
8140 }
8141
8142 lastQ.edgeCount = edgeCount;
8143 lastQ.compoundCount = compoundCount;
8144 selector.edgeCount = 0;
8145 selector.compoundCount = 0; // go on to next query
8146
8147 var nextQuery = selector[selector.length++] = newQuery();
8148 return nextQuery; // this is the new query to be filled by the following exprs
8149 }
8150 }, {
8151 name: 'directedEdge',
8152 separator: true,
8153 regex: tokens.directedEdge,
8154 populate: function populate(selector, query) {
8155 if (selector.currentSubject == null) {
8156 // undirected edge
8157 var edgeQuery = newQuery();
8158 var source = query;
8159 var target = newQuery();
8160 edgeQuery.checks.push({
8161 type: Type.DIRECTED_EDGE,
8162 source: source,
8163 target: target
8164 }); // the query in the selector should be the edge rather than the source
8165
8166 replaceLastQuery(selector, query, edgeQuery);
8167 selector.edgeCount++; // we're now populating the target query with expressions that follow
8168
8169 return target;
8170 } else {
8171 // source/target
8172 var srcTgtQ = newQuery();
8173 var _source = query;
8174
8175 var _target = newQuery();
8176
8177 srcTgtQ.checks.push({
8178 type: Type.NODE_SOURCE,
8179 source: _source,
8180 target: _target
8181 }); // the query in the selector should be the neighbourhood rather than the node
8182
8183 replaceLastQuery(selector, query, srcTgtQ);
8184 selector.edgeCount++;
8185 return _target; // now populating the target with the following expressions
8186 }
8187 }
8188 }, {
8189 name: 'undirectedEdge',
8190 separator: true,
8191 regex: tokens.undirectedEdge,
8192 populate: function populate(selector, query) {
8193 if (selector.currentSubject == null) {
8194 // undirected edge
8195 var edgeQuery = newQuery();
8196 var source = query;
8197 var target = newQuery();
8198 edgeQuery.checks.push({
8199 type: Type.UNDIRECTED_EDGE,
8200 nodes: [source, target]
8201 }); // the query in the selector should be the edge rather than the source
8202
8203 replaceLastQuery(selector, query, edgeQuery);
8204 selector.edgeCount++; // we're now populating the target query with expressions that follow
8205
8206 return target;
8207 } else {
8208 // neighbourhood
8209 var nhoodQ = newQuery();
8210 var node = query;
8211 var neighbor = newQuery();
8212 nhoodQ.checks.push({
8213 type: Type.NODE_NEIGHBOR,
8214 node: node,
8215 neighbor: neighbor
8216 }); // the query in the selector should be the neighbourhood rather than the node
8217
8218 replaceLastQuery(selector, query, nhoodQ);
8219 return neighbor; // now populating the neighbor with following expressions
8220 }
8221 }
8222 }, {
8223 name: 'child',
8224 separator: true,
8225 regex: tokens.child,
8226 populate: function populate(selector, query) {
8227 if (selector.currentSubject == null) {
8228 // default: child query
8229 var parentChildQuery = newQuery();
8230 var child = newQuery();
8231 var parent = selector[selector.length - 1];
8232 parentChildQuery.checks.push({
8233 type: Type.CHILD,
8234 parent: parent,
8235 child: child
8236 }); // the query in the selector should be the '>' itself
8237
8238 replaceLastQuery(selector, query, parentChildQuery);
8239 selector.compoundCount++; // we're now populating the child query with expressions that follow
8240
8241 return child;
8242 } else if (selector.currentSubject === query) {
8243 // compound split query
8244 var compound = newQuery();
8245 var left = selector[selector.length - 1];
8246 var right = newQuery();
8247 var subject = newQuery();
8248
8249 var _child = newQuery();
8250
8251 var _parent = newQuery(); // set up the root compound q
8252
8253
8254 compound.checks.push({
8255 type: Type.COMPOUND_SPLIT,
8256 left: left,
8257 right: right,
8258 subject: subject
8259 }); // populate the subject and replace the q at the old spot (within left) with TRUE
8260
8261 subject.checks = query.checks; // take the checks from the left
8262
8263 query.checks = [{
8264 type: Type.TRUE
8265 }]; // checks under left refs the subject implicitly
8266 // set up the right q
8267
8268 _parent.checks.push({
8269 type: Type.TRUE
8270 }); // parent implicitly refs the subject
8271
8272
8273 right.checks.push({
8274 type: Type.PARENT,
8275 // type is swapped on right side queries
8276 parent: _parent,
8277 child: _child // empty for now
8278
8279 });
8280 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
8281
8282 selector.currentSubject = subject;
8283 selector.compoundCount++;
8284 return _child; // now populating the right side's child
8285 } else {
8286 // parent query
8287 // info for parent query
8288 var _parent2 = newQuery();
8289
8290 var _child2 = newQuery();
8291
8292 var pcQChecks = [{
8293 type: Type.PARENT,
8294 parent: _parent2,
8295 child: _child2
8296 }]; // the parent-child query takes the place of the query previously being populated
8297
8298 _parent2.checks = query.checks; // the previous query contains the checks for the parent
8299
8300 query.checks = pcQChecks; // pc query takes over
8301
8302 selector.compoundCount++;
8303 return _child2; // we're now populating the child
8304 }
8305 }
8306 }, {
8307 name: 'descendant',
8308 separator: true,
8309 regex: tokens.descendant,
8310 populate: function populate(selector, query) {
8311 if (selector.currentSubject == null) {
8312 // default: descendant query
8313 var ancChQuery = newQuery();
8314 var descendant = newQuery();
8315 var ancestor = selector[selector.length - 1];
8316 ancChQuery.checks.push({
8317 type: Type.DESCENDANT,
8318 ancestor: ancestor,
8319 descendant: descendant
8320 }); // the query in the selector should be the '>' itself
8321
8322 replaceLastQuery(selector, query, ancChQuery);
8323 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
8324
8325 return descendant;
8326 } else if (selector.currentSubject === query) {
8327 // compound split query
8328 var compound = newQuery();
8329 var left = selector[selector.length - 1];
8330 var right = newQuery();
8331 var subject = newQuery();
8332
8333 var _descendant = newQuery();
8334
8335 var _ancestor = newQuery(); // set up the root compound q
8336
8337
8338 compound.checks.push({
8339 type: Type.COMPOUND_SPLIT,
8340 left: left,
8341 right: right,
8342 subject: subject
8343 }); // populate the subject and replace the q at the old spot (within left) with TRUE
8344
8345 subject.checks = query.checks; // take the checks from the left
8346
8347 query.checks = [{
8348 type: Type.TRUE
8349 }]; // checks under left refs the subject implicitly
8350 // set up the right q
8351
8352 _ancestor.checks.push({
8353 type: Type.TRUE
8354 }); // ancestor implicitly refs the subject
8355
8356
8357 right.checks.push({
8358 type: Type.ANCESTOR,
8359 // type is swapped on right side queries
8360 ancestor: _ancestor,
8361 descendant: _descendant // empty for now
8362
8363 });
8364 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
8365
8366 selector.currentSubject = subject;
8367 selector.compoundCount++;
8368 return _descendant; // now populating the right side's descendant
8369 } else {
8370 // ancestor query
8371 // info for parent query
8372 var _ancestor2 = newQuery();
8373
8374 var _descendant2 = newQuery();
8375
8376 var adQChecks = [{
8377 type: Type.ANCESTOR,
8378 ancestor: _ancestor2,
8379 descendant: _descendant2
8380 }]; // the parent-child query takes the place of the query previously being populated
8381
8382 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
8383
8384 query.checks = adQChecks; // pc query takes over
8385
8386 selector.compoundCount++;
8387 return _descendant2; // we're now populating the child
8388 }
8389 }
8390 }, {
8391 name: 'subject',
8392 modifier: true,
8393 regex: tokens.subject,
8394 populate: function populate(selector, query) {
8395 if (selector.currentSubject != null && selector.currentSubject !== query) {
8396 warn('Redefinition of subject in selector `' + selector.toString() + '`');
8397 return false;
8398 }
8399
8400 selector.currentSubject = query;
8401 var topQ = selector[selector.length - 1];
8402 var topChk = topQ.checks[0];
8403 var topType = topChk == null ? null : topChk.type;
8404
8405 if (topType === Type.DIRECTED_EDGE) {
8406 // directed edge with subject on the target
8407 // change to target node check
8408 topChk.type = Type.NODE_TARGET;
8409 } else if (topType === Type.UNDIRECTED_EDGE) {
8410 // undirected edge with subject on the second node
8411 // change to neighbor check
8412 topChk.type = Type.NODE_NEIGHBOR;
8413 topChk.node = topChk.nodes[1]; // second node is subject
8414
8415 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
8416
8417 topChk.nodes = null;
8418 }
8419 }
8420 }];
8421 exprs.forEach(function (e) {
8422 return e.regexObj = new RegExp('^' + e.regex);
8423 });
8424
8425 /**
8426 * Of all the expressions, find the first match in the remaining text.
8427 * @param {string} remaining The remaining text to parse
8428 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
8429 */
8430
8431 var consumeExpr = function consumeExpr(remaining) {
8432 var expr;
8433 var match;
8434 var name;
8435
8436 for (var j = 0; j < exprs.length; j++) {
8437 var e = exprs[j];
8438 var n = e.name;
8439 var m = remaining.match(e.regexObj);
8440
8441 if (m != null) {
8442 match = m;
8443 expr = e;
8444 name = n;
8445 var consumed = m[0];
8446 remaining = remaining.substring(consumed.length);
8447 break; // we've consumed one expr, so we can return now
8448 }
8449 }
8450
8451 return {
8452 expr: expr,
8453 match: match,
8454 name: name,
8455 remaining: remaining
8456 };
8457 };
8458 /**
8459 * Consume all the leading whitespace
8460 * @param {string} remaining The text to consume
8461 * @returns The text with the leading whitespace removed
8462 */
8463
8464
8465 var consumeWhitespace = function consumeWhitespace(remaining) {
8466 var match = remaining.match(/^\s+/);
8467
8468 if (match) {
8469 var consumed = match[0];
8470 remaining = remaining.substring(consumed.length);
8471 }
8472
8473 return remaining;
8474 };
8475 /**
8476 * Parse the string and store the parsed representation in the Selector.
8477 * @param {string} selector The selector string
8478 * @returns `true` if the selector was successfully parsed, `false` otherwise
8479 */
8480
8481
8482 var parse = function parse(selector) {
8483 var self = this;
8484 var remaining = self.inputText = selector;
8485 var currentQuery = self[0] = newQuery();
8486 self.length = 1;
8487 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
8488
8489 for (;;) {
8490 var exprInfo = consumeExpr(remaining);
8491
8492 if (exprInfo.expr == null) {
8493 warn('The selector `' + selector + '`is invalid');
8494 return false;
8495 } else {
8496 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
8497
8498 var ret = exprInfo.expr.populate(self, currentQuery, args);
8499
8500 if (ret === false) {
8501 return false; // exit if population failed
8502 } else if (ret != null) {
8503 currentQuery = ret; // change the current query to be filled if the expr specifies
8504 }
8505 }
8506
8507 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
8508
8509 if (remaining.match(/^\s*$/)) {
8510 break;
8511 }
8512 }
8513
8514 var lastQ = self[self.length - 1];
8515
8516 if (self.currentSubject != null) {
8517 lastQ.subject = self.currentSubject;
8518 }
8519
8520 lastQ.edgeCount = self.edgeCount;
8521 lastQ.compoundCount = self.compoundCount;
8522
8523 for (var i = 0; i < self.length; i++) {
8524 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
8525
8526 if (q.compoundCount > 0 && q.edgeCount > 0) {
8527 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
8528 return false;
8529 }
8530
8531 if (q.edgeCount > 1) {
8532 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
8533 return false;
8534 } else if (q.edgeCount === 1) {
8535 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.');
8536 }
8537 }
8538
8539 return true; // success
8540 };
8541 /**
8542 * Get the selector represented as a string. This value uses default formatting,
8543 * so things like spacing may differ from the input text passed to the constructor.
8544 * @returns {string} The selector string
8545 */
8546
8547
8548 var toString = function toString() {
8549 if (this.toStringCache != null) {
8550 return this.toStringCache;
8551 }
8552
8553 var clean = function clean(obj) {
8554 if (obj == null) {
8555 return '';
8556 } else {
8557 return obj;
8558 }
8559 };
8560
8561 var cleanVal = function cleanVal(val) {
8562 if (string(val)) {
8563 return '"' + val + '"';
8564 } else {
8565 return clean(val);
8566 }
8567 };
8568
8569 var space = function space(val) {
8570 return ' ' + val + ' ';
8571 };
8572
8573 var checkToString = function checkToString(check, subject) {
8574 var type = check.type,
8575 value = check.value;
8576
8577 switch (type) {
8578 case Type.GROUP:
8579 {
8580 var group = clean(value);
8581 return group.substring(0, group.length - 1);
8582 }
8583
8584 case Type.DATA_COMPARE:
8585 {
8586 var field = check.field,
8587 operator = check.operator;
8588 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
8589 }
8590
8591 case Type.DATA_BOOL:
8592 {
8593 var _operator = check.operator,
8594 _field = check.field;
8595 return '[' + clean(_operator) + _field + ']';
8596 }
8597
8598 case Type.DATA_EXIST:
8599 {
8600 var _field2 = check.field;
8601 return '[' + _field2 + ']';
8602 }
8603
8604 case Type.META_COMPARE:
8605 {
8606 var _operator2 = check.operator,
8607 _field3 = check.field;
8608 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
8609 }
8610
8611 case Type.STATE:
8612 {
8613 return value;
8614 }
8615
8616 case Type.ID:
8617 {
8618 return '#' + value;
8619 }
8620
8621 case Type.CLASS:
8622 {
8623 return '.' + value;
8624 }
8625
8626 case Type.PARENT:
8627 case Type.CHILD:
8628 {
8629 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
8630 }
8631
8632 case Type.ANCESTOR:
8633 case Type.DESCENDANT:
8634 {
8635 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
8636 }
8637
8638 case Type.COMPOUND_SPLIT:
8639 {
8640 var lhs = queryToString(check.left, subject);
8641 var sub = queryToString(check.subject, subject);
8642 var rhs = queryToString(check.right, subject);
8643 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
8644 }
8645
8646 case Type.TRUE:
8647 {
8648 return '';
8649 }
8650 }
8651 };
8652
8653 var queryToString = function queryToString(query, subject) {
8654 return query.checks.reduce(function (str, chk, i) {
8655 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
8656 }, '');
8657 };
8658
8659 var str = '';
8660
8661 for (var i = 0; i < this.length; i++) {
8662 var query = this[i];
8663 str += queryToString(query, query.subject);
8664
8665 if (this.length > 1 && i < this.length - 1) {
8666 str += ', ';
8667 }
8668 }
8669
8670 this.toStringCache = str;
8671 return str;
8672 };
8673 var parse$1 = {
8674 parse: parse,
8675 toString: toString
8676 };
8677
8678 var valCmp = function valCmp(fieldVal, operator, value) {
8679 var matches;
8680 var isFieldStr = string(fieldVal);
8681 var isFieldNum = number(fieldVal);
8682 var isValStr = string(value);
8683 var fieldStr, valStr;
8684 var caseInsensitive = false;
8685 var notExpr = false;
8686 var isIneqCmp = false;
8687
8688 if (operator.indexOf('!') >= 0) {
8689 operator = operator.replace('!', '');
8690 notExpr = true;
8691 }
8692
8693 if (operator.indexOf('@') >= 0) {
8694 operator = operator.replace('@', '');
8695 caseInsensitive = true;
8696 }
8697
8698 if (isFieldStr || isValStr || caseInsensitive) {
8699 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
8700 valStr = '' + value;
8701 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
8702 // even if we're comparing numbers
8703
8704
8705 if (caseInsensitive) {
8706 fieldVal = fieldStr = fieldStr.toLowerCase();
8707 value = valStr = valStr.toLowerCase();
8708 }
8709
8710 switch (operator) {
8711 case '*=':
8712 matches = fieldStr.indexOf(valStr) >= 0;
8713 break;
8714
8715 case '$=':
8716 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
8717 break;
8718
8719 case '^=':
8720 matches = fieldStr.indexOf(valStr) === 0;
8721 break;
8722
8723 case '=':
8724 matches = fieldVal === value;
8725 break;
8726
8727 case '>':
8728 isIneqCmp = true;
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 default:
8748 matches = false;
8749 break;
8750 } // apply the not op, but null vals for inequalities should always stay non-matching
8751
8752
8753 if (notExpr && (fieldVal != null || !isIneqCmp)) {
8754 matches = !matches;
8755 }
8756
8757 return matches;
8758 };
8759 var boolCmp = function boolCmp(fieldVal, operator) {
8760 switch (operator) {
8761 case '?':
8762 return fieldVal ? true : false;
8763
8764 case '!':
8765 return fieldVal ? false : true;
8766
8767 case '^':
8768 return fieldVal === undefined;
8769 }
8770 };
8771 var existCmp = function existCmp(fieldVal) {
8772 return fieldVal !== undefined;
8773 };
8774 var data = function data(ele, field) {
8775 return ele.data(field);
8776 };
8777 var meta = function meta(ele, field) {
8778 return ele[field]();
8779 };
8780
8781 /** A lookup of `match(check, ele)` functions by `Type` int */
8782
8783 var match = [];
8784 /**
8785 * Returns whether the query matches for the element
8786 * @param query The `{ type, value, ... }` query object
8787 * @param ele The element to compare against
8788 */
8789
8790 var matches = function matches(query, ele) {
8791 return query.checks.every(function (chk) {
8792 return match[chk.type](chk, ele);
8793 });
8794 };
8795
8796 match[Type.GROUP] = function (check, ele) {
8797 var group = check.value;
8798 return group === '*' || group === ele.group();
8799 };
8800
8801 match[Type.STATE] = function (check, ele) {
8802 var stateSelector = check.value;
8803 return stateSelectorMatches(stateSelector, ele);
8804 };
8805
8806 match[Type.ID] = function (check, ele) {
8807 var id = check.value;
8808 return ele.id() === id;
8809 };
8810
8811 match[Type.CLASS] = function (check, ele) {
8812 var cls = check.value;
8813 return ele.hasClass(cls);
8814 };
8815
8816 match[Type.META_COMPARE] = function (check, ele) {
8817 var field = check.field,
8818 operator = check.operator,
8819 value = check.value;
8820 return valCmp(meta(ele, field), operator, value);
8821 };
8822
8823 match[Type.DATA_COMPARE] = function (check, ele) {
8824 var field = check.field,
8825 operator = check.operator,
8826 value = check.value;
8827 return valCmp(data(ele, field), operator, value);
8828 };
8829
8830 match[Type.DATA_BOOL] = function (check, ele) {
8831 var field = check.field,
8832 operator = check.operator;
8833 return boolCmp(data(ele, field), operator);
8834 };
8835
8836 match[Type.DATA_EXIST] = function (check, ele) {
8837 var field = check.field,
8838 operator = check.operator;
8839 return existCmp(data(ele, field));
8840 };
8841
8842 match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8843 var qA = check.nodes[0];
8844 var qB = check.nodes[1];
8845 var src = ele.source();
8846 var tgt = ele.target();
8847 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8848 };
8849
8850 match[Type.NODE_NEIGHBOR] = function (check, ele) {
8851 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8852 return n.isNode() && matches(check.neighbor, n);
8853 });
8854 };
8855
8856 match[Type.DIRECTED_EDGE] = function (check, ele) {
8857 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8858 };
8859
8860 match[Type.NODE_SOURCE] = function (check, ele) {
8861 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8862 return n.isNode() && matches(check.target, n);
8863 });
8864 };
8865
8866 match[Type.NODE_TARGET] = function (check, ele) {
8867 return matches(check.target, ele) && ele.incomers().some(function (n) {
8868 return n.isNode() && matches(check.source, n);
8869 });
8870 };
8871
8872 match[Type.CHILD] = function (check, ele) {
8873 return matches(check.child, ele) && matches(check.parent, ele.parent());
8874 };
8875
8876 match[Type.PARENT] = function (check, ele) {
8877 return matches(check.parent, ele) && ele.children().some(function (c) {
8878 return matches(check.child, c);
8879 });
8880 };
8881
8882 match[Type.DESCENDANT] = function (check, ele) {
8883 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8884 return matches(check.ancestor, a);
8885 });
8886 };
8887
8888 match[Type.ANCESTOR] = function (check, ele) {
8889 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8890 return matches(check.descendant, d);
8891 });
8892 };
8893
8894 match[Type.COMPOUND_SPLIT] = function (check, ele) {
8895 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8896 };
8897
8898 match[Type.TRUE] = function () {
8899 return true;
8900 };
8901
8902 match[Type.COLLECTION] = function (check, ele) {
8903 var collection = check.value;
8904 return collection.has(ele);
8905 };
8906
8907 match[Type.FILTER] = function (check, ele) {
8908 var filter = check.value;
8909 return filter(ele);
8910 };
8911
8912 var filter = function filter(collection) {
8913 var self = this; // for 1 id #foo queries, just get the element
8914
8915 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8916 return collection.getElementById(self[0].checks[0].value).collection();
8917 }
8918
8919 var selectorFunction = function selectorFunction(element) {
8920 for (var j = 0; j < self.length; j++) {
8921 var query = self[j];
8922
8923 if (matches(query, element)) {
8924 return true;
8925 }
8926 }
8927
8928 return false;
8929 };
8930
8931 if (self.text() == null) {
8932 selectorFunction = function selectorFunction() {
8933 return true;
8934 };
8935 }
8936
8937 return collection.filter(selectorFunction);
8938 }; // filter
8939 // does selector match a single element?
8940
8941
8942 var matches$1 = function matches$1(ele) {
8943 var self = this;
8944
8945 for (var j = 0; j < self.length; j++) {
8946 var query = self[j];
8947
8948 if (matches(query, ele)) {
8949 return true;
8950 }
8951 }
8952
8953 return false;
8954 }; // matches
8955
8956
8957 var matching = {
8958 matches: matches$1,
8959 filter: filter
8960 };
8961
8962 var Selector = function Selector(selector) {
8963 this.inputText = selector;
8964 this.currentSubject = null;
8965 this.compoundCount = 0;
8966 this.edgeCount = 0;
8967 this.length = 0;
8968
8969 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8970 this.addQuery({
8971 checks: [{
8972 type: Type.COLLECTION,
8973 value: selector.collection()
8974 }]
8975 });
8976 } else if (fn(selector)) {
8977 this.addQuery({
8978 checks: [{
8979 type: Type.FILTER,
8980 value: selector
8981 }]
8982 });
8983 } else if (string(selector)) {
8984 if (!this.parse(selector)) {
8985 this.invalid = true;
8986 }
8987 } else {
8988 error('A selector must be created from a string; found ');
8989 }
8990 };
8991
8992 var selfn = Selector.prototype;
8993 [parse$1, matching].forEach(function (p) {
8994 return extend(selfn, p);
8995 });
8996
8997 selfn.text = function () {
8998 return this.inputText;
8999 };
9000
9001 selfn.size = function () {
9002 return this.length;
9003 };
9004
9005 selfn.eq = function (i) {
9006 return this[i];
9007 };
9008
9009 selfn.sameText = function (otherSel) {
9010 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
9011 };
9012
9013 selfn.addQuery = function (q) {
9014 this[this.length++] = q;
9015 };
9016
9017 selfn.selector = selfn.toString;
9018
9019 var elesfn$f = {
9020 allAre: function allAre(selector) {
9021 var selObj = new Selector(selector);
9022 return this.every(function (ele) {
9023 return selObj.matches(ele);
9024 });
9025 },
9026 is: function is(selector) {
9027 var selObj = new Selector(selector);
9028 return this.some(function (ele) {
9029 return selObj.matches(ele);
9030 });
9031 },
9032 some: function some(fn, thisArg) {
9033 for (var i = 0; i < this.length; i++) {
9034 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9035
9036 if (ret) {
9037 return true;
9038 }
9039 }
9040
9041 return false;
9042 },
9043 every: function every(fn, thisArg) {
9044 for (var i = 0; i < this.length; i++) {
9045 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9046
9047 if (!ret) {
9048 return false;
9049 }
9050 }
9051
9052 return true;
9053 },
9054 same: function same(collection) {
9055 // cheap collection ref check
9056 if (this === collection) {
9057 return true;
9058 }
9059
9060 collection = this.cy().collection(collection);
9061 var thisLength = this.length;
9062 var collectionLength = collection.length; // cheap length check
9063
9064 if (thisLength !== collectionLength) {
9065 return false;
9066 } // cheap element ref check
9067
9068
9069 if (thisLength === 1) {
9070 return this[0] === collection[0];
9071 }
9072
9073 return this.every(function (ele) {
9074 return collection.hasElementWithId(ele.id());
9075 });
9076 },
9077 anySame: function anySame(collection) {
9078 collection = this.cy().collection(collection);
9079 return this.some(function (ele) {
9080 return collection.hasElementWithId(ele.id());
9081 });
9082 },
9083 allAreNeighbors: function allAreNeighbors(collection) {
9084 collection = this.cy().collection(collection);
9085 var nhood = this.neighborhood();
9086 return collection.every(function (ele) {
9087 return nhood.hasElementWithId(ele.id());
9088 });
9089 },
9090 contains: function contains(collection) {
9091 collection = this.cy().collection(collection);
9092 var self = this;
9093 return collection.every(function (ele) {
9094 return self.hasElementWithId(ele.id());
9095 });
9096 }
9097 };
9098 elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
9099 elesfn$f.has = elesfn$f.contains;
9100 elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
9101
9102 var cache = function cache(fn, name) {
9103 return function traversalCache(arg1, arg2, arg3, arg4) {
9104 var selectorOrEles = arg1;
9105 var eles = this;
9106 var key;
9107
9108 if (selectorOrEles == null) {
9109 key = '';
9110 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
9111 key = selectorOrEles.id();
9112 }
9113
9114 if (eles.length === 1 && key) {
9115 var _p = eles[0]._private;
9116 var tch = _p.traversalCache = _p.traversalCache || {};
9117 var ch = tch[name] = tch[name] || [];
9118 var hash = hashString(key);
9119 var cacheHit = ch[hash];
9120
9121 if (cacheHit) {
9122 return cacheHit;
9123 } else {
9124 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
9125 }
9126 } else {
9127 return fn.call(eles, arg1, arg2, arg3, arg4);
9128 }
9129 };
9130 };
9131
9132 var elesfn$g = {
9133 parent: function parent(selector) {
9134 var parents = []; // optimisation for single ele call
9135
9136 if (this.length === 1) {
9137 var parent = this[0]._private.parent;
9138
9139 if (parent) {
9140 return parent;
9141 }
9142 }
9143
9144 for (var i = 0; i < this.length; i++) {
9145 var ele = this[i];
9146 var _parent = ele._private.parent;
9147
9148 if (_parent) {
9149 parents.push(_parent);
9150 }
9151 }
9152
9153 return this.spawn(parents, true).filter(selector);
9154 },
9155 parents: function parents(selector) {
9156 var parents = [];
9157 var eles = this.parent();
9158
9159 while (eles.nonempty()) {
9160 for (var i = 0; i < eles.length; i++) {
9161 var ele = eles[i];
9162 parents.push(ele);
9163 }
9164
9165 eles = eles.parent();
9166 }
9167
9168 return this.spawn(parents, true).filter(selector);
9169 },
9170 commonAncestors: function commonAncestors(selector) {
9171 var ancestors;
9172
9173 for (var i = 0; i < this.length; i++) {
9174 var ele = this[i];
9175 var parents = ele.parents();
9176 ancestors = ancestors || parents;
9177 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
9178 }
9179
9180 return ancestors.filter(selector);
9181 },
9182 orphans: function orphans(selector) {
9183 return this.stdFilter(function (ele) {
9184 return ele.isOrphan();
9185 }).filter(selector);
9186 },
9187 nonorphans: function nonorphans(selector) {
9188 return this.stdFilter(function (ele) {
9189 return ele.isChild();
9190 }).filter(selector);
9191 },
9192 children: cache(function (selector) {
9193 var children = [];
9194
9195 for (var i = 0; i < this.length; i++) {
9196 var ele = this[i];
9197 var eleChildren = ele._private.children;
9198
9199 for (var j = 0; j < eleChildren.length; j++) {
9200 children.push(eleChildren[j]);
9201 }
9202 }
9203
9204 return this.spawn(children, true).filter(selector);
9205 }, 'children'),
9206 siblings: function siblings(selector) {
9207 return this.parent().children().not(this).filter(selector);
9208 },
9209 isParent: function isParent() {
9210 var ele = this[0];
9211
9212 if (ele) {
9213 return ele.isNode() && ele._private.children.length !== 0;
9214 }
9215 },
9216 isChildless: function isChildless() {
9217 var ele = this[0];
9218
9219 if (ele) {
9220 return ele.isNode() && ele._private.children.length === 0;
9221 }
9222 },
9223 isChild: function isChild() {
9224 var ele = this[0];
9225
9226 if (ele) {
9227 return ele.isNode() && ele._private.parent != null;
9228 }
9229 },
9230 isOrphan: function isOrphan() {
9231 var ele = this[0];
9232
9233 if (ele) {
9234 return ele.isNode() && ele._private.parent == null;
9235 }
9236 },
9237 descendants: function descendants(selector) {
9238 var elements = [];
9239
9240 function add(eles) {
9241 for (var i = 0; i < eles.length; i++) {
9242 var ele = eles[i];
9243 elements.push(ele);
9244
9245 if (ele.children().nonempty()) {
9246 add(ele.children());
9247 }
9248 }
9249 }
9250
9251 add(this.children());
9252 return this.spawn(elements, true).filter(selector);
9253 }
9254 };
9255
9256 function forEachCompound(eles, fn, includeSelf, recursiveStep) {
9257 var q = [];
9258 var did = new Set$1();
9259 var cy = eles.cy();
9260 var hasCompounds = cy.hasCompoundNodes();
9261
9262 for (var i = 0; i < eles.length; i++) {
9263 var ele = eles[i];
9264
9265 if (includeSelf) {
9266 q.push(ele);
9267 } else if (hasCompounds) {
9268 recursiveStep(q, did, ele);
9269 }
9270 }
9271
9272 while (q.length > 0) {
9273 var _ele = q.shift();
9274
9275 fn(_ele);
9276 did.add(_ele.id());
9277
9278 if (hasCompounds) {
9279 recursiveStep(q, did, _ele);
9280 }
9281 }
9282
9283 return eles;
9284 }
9285
9286 function addChildren(q, did, ele) {
9287 if (ele.isParent()) {
9288 var children = ele._private.children;
9289
9290 for (var i = 0; i < children.length; i++) {
9291 var child = children[i];
9292
9293 if (!did.has(child.id())) {
9294 q.push(child);
9295 }
9296 }
9297 }
9298 } // very efficient version of eles.add( eles.descendants() ).forEach()
9299 // for internal use
9300
9301
9302 elesfn$g.forEachDown = function (fn) {
9303 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9304 return forEachCompound(this, fn, includeSelf, addChildren);
9305 };
9306
9307 function addParent(q, did, ele) {
9308 if (ele.isChild()) {
9309 var parent = ele._private.parent;
9310
9311 if (!did.has(parent.id())) {
9312 q.push(parent);
9313 }
9314 }
9315 }
9316
9317 elesfn$g.forEachUp = function (fn) {
9318 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9319 return forEachCompound(this, fn, includeSelf, addParent);
9320 };
9321
9322 function addParentAndChildren(q, did, ele) {
9323 addParent(q, did, ele);
9324 addChildren(q, did, ele);
9325 }
9326
9327 elesfn$g.forEachUpAndDown = function (fn) {
9328 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9329 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
9330 }; // aliases
9331
9332
9333 elesfn$g.ancestors = elesfn$g.parents;
9334
9335 var fn$1, elesfn$h;
9336 fn$1 = elesfn$h = {
9337 data: define$3.data({
9338 field: 'data',
9339 bindingEvent: 'data',
9340 allowBinding: true,
9341 allowSetting: true,
9342 settingEvent: 'data',
9343 settingTriggersEvent: true,
9344 triggerFnName: 'trigger',
9345 allowGetting: true,
9346 immutableKeys: {
9347 'id': true,
9348 'source': true,
9349 'target': true,
9350 'parent': true
9351 },
9352 updateStyle: true
9353 }),
9354 removeData: define$3.removeData({
9355 field: 'data',
9356 event: 'data',
9357 triggerFnName: 'trigger',
9358 triggerEvent: true,
9359 immutableKeys: {
9360 'id': true,
9361 'source': true,
9362 'target': true,
9363 'parent': true
9364 },
9365 updateStyle: true
9366 }),
9367 scratch: define$3.data({
9368 field: 'scratch',
9369 bindingEvent: 'scratch',
9370 allowBinding: true,
9371 allowSetting: true,
9372 settingEvent: 'scratch',
9373 settingTriggersEvent: true,
9374 triggerFnName: 'trigger',
9375 allowGetting: true,
9376 updateStyle: true
9377 }),
9378 removeScratch: define$3.removeData({
9379 field: 'scratch',
9380 event: 'scratch',
9381 triggerFnName: 'trigger',
9382 triggerEvent: true,
9383 updateStyle: true
9384 }),
9385 rscratch: define$3.data({
9386 field: 'rscratch',
9387 allowBinding: false,
9388 allowSetting: true,
9389 settingTriggersEvent: false,
9390 allowGetting: true
9391 }),
9392 removeRscratch: define$3.removeData({
9393 field: 'rscratch',
9394 triggerEvent: false
9395 }),
9396 id: function id() {
9397 var ele = this[0];
9398
9399 if (ele) {
9400 return ele._private.data.id;
9401 }
9402 }
9403 }; // aliases
9404
9405 fn$1.attr = fn$1.data;
9406 fn$1.removeAttr = fn$1.removeData;
9407 var data$1 = elesfn$h;
9408
9409 var elesfn$i = {};
9410
9411 function defineDegreeFunction(callback) {
9412 return function (includeLoops) {
9413 var self = this;
9414
9415 if (includeLoops === undefined) {
9416 includeLoops = true;
9417 }
9418
9419 if (self.length === 0) {
9420 return;
9421 }
9422
9423 if (self.isNode() && !self.removed()) {
9424 var degree = 0;
9425 var node = self[0];
9426 var connectedEdges = node._private.edges;
9427
9428 for (var i = 0; i < connectedEdges.length; i++) {
9429 var edge = connectedEdges[i];
9430
9431 if (!includeLoops && edge.isLoop()) {
9432 continue;
9433 }
9434
9435 degree += callback(node, edge);
9436 }
9437
9438 return degree;
9439 } else {
9440 return;
9441 }
9442 };
9443 }
9444
9445 extend(elesfn$i, {
9446 degree: defineDegreeFunction(function (node, edge) {
9447 if (edge.source().same(edge.target())) {
9448 return 2;
9449 } else {
9450 return 1;
9451 }
9452 }),
9453 indegree: defineDegreeFunction(function (node, edge) {
9454 if (edge.target().same(node)) {
9455 return 1;
9456 } else {
9457 return 0;
9458 }
9459 }),
9460 outdegree: defineDegreeFunction(function (node, edge) {
9461 if (edge.source().same(node)) {
9462 return 1;
9463 } else {
9464 return 0;
9465 }
9466 })
9467 });
9468
9469 function defineDegreeBoundsFunction(degreeFn, callback) {
9470 return function (includeLoops) {
9471 var ret;
9472 var nodes = this.nodes();
9473
9474 for (var i = 0; i < nodes.length; i++) {
9475 var ele = nodes[i];
9476 var degree = ele[degreeFn](includeLoops);
9477
9478 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
9479 ret = degree;
9480 }
9481 }
9482
9483 return ret;
9484 };
9485 }
9486
9487 extend(elesfn$i, {
9488 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
9489 return degree < min;
9490 }),
9491 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
9492 return degree > max;
9493 }),
9494 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
9495 return degree < min;
9496 }),
9497 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
9498 return degree > max;
9499 }),
9500 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
9501 return degree < min;
9502 }),
9503 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
9504 return degree > max;
9505 })
9506 });
9507 extend(elesfn$i, {
9508 totalDegree: function totalDegree(includeLoops) {
9509 var total = 0;
9510 var nodes = this.nodes();
9511
9512 for (var i = 0; i < nodes.length; i++) {
9513 total += nodes[i].degree(includeLoops);
9514 }
9515
9516 return total;
9517 }
9518 });
9519
9520 var fn$2, elesfn$j;
9521
9522 var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
9523 for (var i = 0; i < eles.length; i++) {
9524 var ele = eles[i];
9525
9526 if (!ele.locked()) {
9527 var oldPos = ele._private.position;
9528 var delta = {
9529 x: newPos.x != null ? newPos.x - oldPos.x : 0,
9530 y: newPos.y != null ? newPos.y - oldPos.y : 0
9531 };
9532
9533 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
9534 ele.children().shift(delta, silent);
9535 }
9536
9537 ele.dirtyBoundingBoxCache();
9538 }
9539 }
9540 };
9541
9542 var positionDef = {
9543 field: 'position',
9544 bindingEvent: 'position',
9545 allowBinding: true,
9546 allowSetting: true,
9547 settingEvent: 'position',
9548 settingTriggersEvent: true,
9549 triggerFnName: 'emitAndNotify',
9550 allowGetting: true,
9551 validKeys: ['x', 'y'],
9552 beforeGet: function beforeGet(ele) {
9553 ele.updateCompoundBounds();
9554 },
9555 beforeSet: function beforeSet(eles, newPos) {
9556 beforePositionSet(eles, newPos, false);
9557 },
9558 onSet: function onSet(eles) {
9559 eles.dirtyCompoundBoundsCache();
9560 },
9561 canSet: function canSet(ele) {
9562 return !ele.locked();
9563 }
9564 };
9565 fn$2 = elesfn$j = {
9566 position: define$3.data(positionDef),
9567 // position but no notification to renderer
9568 silentPosition: define$3.data(extend({}, positionDef, {
9569 allowBinding: false,
9570 allowSetting: true,
9571 settingTriggersEvent: false,
9572 allowGetting: false,
9573 beforeSet: function beforeSet(eles, newPos) {
9574 beforePositionSet(eles, newPos, true);
9575 },
9576 onSet: function onSet(eles) {
9577 eles.dirtyCompoundBoundsCache();
9578 }
9579 })),
9580 positions: function positions(pos, silent) {
9581 if (plainObject(pos)) {
9582 if (silent) {
9583 this.silentPosition(pos);
9584 } else {
9585 this.position(pos);
9586 }
9587 } else if (fn(pos)) {
9588 var _fn = pos;
9589 var cy = this.cy();
9590 cy.startBatch();
9591
9592 for (var i = 0; i < this.length; i++) {
9593 var ele = this[i];
9594
9595 var _pos = void 0;
9596
9597 if (_pos = _fn(ele, i)) {
9598 if (silent) {
9599 ele.silentPosition(_pos);
9600 } else {
9601 ele.position(_pos);
9602 }
9603 }
9604 }
9605
9606 cy.endBatch();
9607 }
9608
9609 return this; // chaining
9610 },
9611 silentPositions: function silentPositions(pos) {
9612 return this.positions(pos, true);
9613 },
9614 shift: function shift(dim, val, silent) {
9615 var delta;
9616
9617 if (plainObject(dim)) {
9618 delta = {
9619 x: number(dim.x) ? dim.x : 0,
9620 y: number(dim.y) ? dim.y : 0
9621 };
9622 silent = val;
9623 } else if (string(dim) && number(val)) {
9624 delta = {
9625 x: 0,
9626 y: 0
9627 };
9628 delta[dim] = val;
9629 }
9630
9631 if (delta != null) {
9632 var cy = this.cy();
9633 cy.startBatch();
9634
9635 for (var i = 0; i < this.length; i++) {
9636 var ele = this[i];
9637 var pos = ele.position();
9638 var newPos = {
9639 x: pos.x + delta.x,
9640 y: pos.y + delta.y
9641 };
9642
9643 if (silent) {
9644 ele.silentPosition(newPos);
9645 } else {
9646 ele.position(newPos);
9647 }
9648 }
9649
9650 cy.endBatch();
9651 }
9652
9653 return this;
9654 },
9655 silentShift: function silentShift(dim, val) {
9656 if (plainObject(dim)) {
9657 this.shift(dim, true);
9658 } else if (string(dim) && number(val)) {
9659 this.shift(dim, val, true);
9660 }
9661
9662 return this;
9663 },
9664 // get/set the rendered (i.e. on screen) positon of the element
9665 renderedPosition: function renderedPosition(dim, val) {
9666 var ele = this[0];
9667 var cy = this.cy();
9668 var zoom = cy.zoom();
9669 var pan = cy.pan();
9670 var rpos = plainObject(dim) ? dim : undefined;
9671 var setting = rpos !== undefined || val !== undefined && string(dim);
9672
9673 if (ele && ele.isNode()) {
9674 // must have an element and must be a node to return position
9675 if (setting) {
9676 for (var i = 0; i < this.length; i++) {
9677 var _ele = this[i];
9678
9679 if (val !== undefined) {
9680 // set one dimension
9681 _ele.position(dim, (val - pan[dim]) / zoom);
9682 } else if (rpos !== undefined) {
9683 // set whole position
9684 _ele.position(renderedToModelPosition(rpos, zoom, pan));
9685 }
9686 }
9687 } else {
9688 // getting
9689 var pos = ele.position();
9690 rpos = modelToRenderedPosition(pos, zoom, pan);
9691
9692 if (dim === undefined) {
9693 // then return the whole rendered position
9694 return rpos;
9695 } else {
9696 // then return the specified dimension
9697 return rpos[dim];
9698 }
9699 }
9700 } else if (!setting) {
9701 return undefined; // for empty collection case
9702 }
9703
9704 return this; // chaining
9705 },
9706 // get/set the position relative to the parent
9707 relativePosition: function relativePosition(dim, val) {
9708 var ele = this[0];
9709 var cy = this.cy();
9710 var ppos = plainObject(dim) ? dim : undefined;
9711 var setting = ppos !== undefined || val !== undefined && string(dim);
9712 var hasCompoundNodes = cy.hasCompoundNodes();
9713
9714 if (ele && ele.isNode()) {
9715 // must have an element and must be a node to return position
9716 if (setting) {
9717 for (var i = 0; i < this.length; i++) {
9718 var _ele2 = this[i];
9719 var parent = hasCompoundNodes ? _ele2.parent() : null;
9720 var hasParent = parent && parent.length > 0;
9721 var relativeToParent = hasParent;
9722
9723 if (hasParent) {
9724 parent = parent[0];
9725 }
9726
9727 var origin = relativeToParent ? parent.position() : {
9728 x: 0,
9729 y: 0
9730 };
9731
9732 if (val !== undefined) {
9733 // set one dimension
9734 _ele2.position(dim, val + origin[dim]);
9735 } else if (ppos !== undefined) {
9736 // set whole position
9737 _ele2.position({
9738 x: ppos.x + origin.x,
9739 y: ppos.y + origin.y
9740 });
9741 }
9742 }
9743 } else {
9744 // getting
9745 var pos = ele.position();
9746
9747 var _parent = hasCompoundNodes ? ele.parent() : null;
9748
9749 var _hasParent = _parent && _parent.length > 0;
9750
9751 var _relativeToParent = _hasParent;
9752
9753 if (_hasParent) {
9754 _parent = _parent[0];
9755 }
9756
9757 var _origin = _relativeToParent ? _parent.position() : {
9758 x: 0,
9759 y: 0
9760 };
9761
9762 ppos = {
9763 x: pos.x - _origin.x,
9764 y: pos.y - _origin.y
9765 };
9766
9767 if (dim === undefined) {
9768 // then return the whole rendered position
9769 return ppos;
9770 } else {
9771 // then return the specified dimension
9772 return ppos[dim];
9773 }
9774 }
9775 } else if (!setting) {
9776 return undefined; // for empty collection case
9777 }
9778
9779 return this; // chaining
9780 }
9781 }; // aliases
9782
9783 fn$2.modelPosition = fn$2.point = fn$2.position;
9784 fn$2.modelPositions = fn$2.points = fn$2.positions;
9785 fn$2.renderedPoint = fn$2.renderedPosition;
9786 fn$2.relativePoint = fn$2.relativePosition;
9787 var position = elesfn$j;
9788
9789 var fn$3, elesfn$k;
9790 fn$3 = elesfn$k = {};
9791
9792 elesfn$k.renderedBoundingBox = function (options) {
9793 var bb = this.boundingBox(options);
9794 var cy = this.cy();
9795 var zoom = cy.zoom();
9796 var pan = cy.pan();
9797 var x1 = bb.x1 * zoom + pan.x;
9798 var x2 = bb.x2 * zoom + pan.x;
9799 var y1 = bb.y1 * zoom + pan.y;
9800 var y2 = bb.y2 * zoom + pan.y;
9801 return {
9802 x1: x1,
9803 x2: x2,
9804 y1: y1,
9805 y2: y2,
9806 w: x2 - x1,
9807 h: y2 - y1
9808 };
9809 };
9810
9811 elesfn$k.dirtyCompoundBoundsCache = function () {
9812 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9813 var cy = this.cy();
9814
9815 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9816 return this;
9817 }
9818
9819 this.forEachUp(function (ele) {
9820 if (ele.isParent()) {
9821 var _p = ele._private;
9822 _p.compoundBoundsClean = false;
9823 _p.bbCache = null;
9824
9825 if (!silent) {
9826 ele.emitAndNotify('bounds');
9827 }
9828 }
9829 });
9830 return this;
9831 };
9832
9833 elesfn$k.updateCompoundBounds = function () {
9834 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9835 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9836
9837 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9838 return this;
9839 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9840
9841
9842 if (!force && cy.batching()) {
9843 return this;
9844 }
9845
9846 function update(parent) {
9847 if (!parent.isParent()) {
9848 return;
9849 }
9850
9851 var _p = parent._private;
9852 var children = parent.children();
9853 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9854 var min = {
9855 width: {
9856 val: parent.pstyle('min-width').pfValue,
9857 left: parent.pstyle('min-width-bias-left'),
9858 right: parent.pstyle('min-width-bias-right')
9859 },
9860 height: {
9861 val: parent.pstyle('min-height').pfValue,
9862 top: parent.pstyle('min-height-bias-top'),
9863 bottom: parent.pstyle('min-height-bias-bottom')
9864 }
9865 };
9866 var bb = children.boundingBox({
9867 includeLabels: includeLabels,
9868 includeOverlays: false,
9869 // updating the compound bounds happens outside of the regular
9870 // cache cycle (i.e. before fired events)
9871 useCache: false
9872 });
9873 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9874
9875 if (bb.w === 0 || bb.h === 0) {
9876 bb = {
9877 w: parent.pstyle('width').pfValue,
9878 h: parent.pstyle('height').pfValue
9879 };
9880 bb.x1 = pos.x - bb.w / 2;
9881 bb.x2 = pos.x + bb.w / 2;
9882 bb.y1 = pos.y - bb.h / 2;
9883 bb.y2 = pos.y + bb.h / 2;
9884 }
9885
9886 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9887 var biasDiff = 0;
9888 var biasComplementDiff = 0;
9889 var biasTotal = propBias + propBiasComplement;
9890
9891 if (propDiff > 0 && biasTotal > 0) {
9892 biasDiff = propBias / biasTotal * propDiff;
9893 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9894 }
9895
9896 return {
9897 biasDiff: biasDiff,
9898 biasComplementDiff: biasComplementDiff
9899 };
9900 }
9901
9902 function computePaddingValues(width, height, paddingObject, relativeTo) {
9903 // Assuming percentage is number from 0 to 1
9904 if (paddingObject.units === '%') {
9905 switch (relativeTo) {
9906 case 'width':
9907 return width > 0 ? paddingObject.pfValue * width : 0;
9908
9909 case 'height':
9910 return height > 0 ? paddingObject.pfValue * height : 0;
9911
9912 case 'average':
9913 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9914
9915 case 'min':
9916 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9917
9918 case 'max':
9919 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9920
9921 default:
9922 return 0;
9923 }
9924 } else if (paddingObject.units === 'px') {
9925 return paddingObject.pfValue;
9926 } else {
9927 return 0;
9928 }
9929 }
9930
9931 var leftVal = min.width.left.value;
9932
9933 if (min.width.left.units === 'px' && min.width.val > 0) {
9934 leftVal = leftVal * 100 / min.width.val;
9935 }
9936
9937 var rightVal = min.width.right.value;
9938
9939 if (min.width.right.units === 'px' && min.width.val > 0) {
9940 rightVal = rightVal * 100 / min.width.val;
9941 }
9942
9943 var topVal = min.height.top.value;
9944
9945 if (min.height.top.units === 'px' && min.height.val > 0) {
9946 topVal = topVal * 100 / min.height.val;
9947 }
9948
9949 var bottomVal = min.height.bottom.value;
9950
9951 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9952 bottomVal = bottomVal * 100 / min.height.val;
9953 }
9954
9955 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9956 var diffLeft = widthBiasDiffs.biasDiff;
9957 var diffRight = widthBiasDiffs.biasComplementDiff;
9958 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9959 var diffTop = heightBiasDiffs.biasDiff;
9960 var diffBottom = heightBiasDiffs.biasComplementDiff;
9961 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9962 _p.autoWidth = Math.max(bb.w, min.width.val);
9963 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9964 _p.autoHeight = Math.max(bb.h, min.height.val);
9965 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9966 }
9967
9968 for (var i = 0; i < this.length; i++) {
9969 var ele = this[i];
9970 var _p = ele._private;
9971
9972 if (!_p.compoundBoundsClean || force) {
9973 update(ele);
9974
9975 if (!cy.batching()) {
9976 _p.compoundBoundsClean = true;
9977 }
9978 }
9979 }
9980
9981 return this;
9982 };
9983
9984 var noninf = function noninf(x) {
9985 if (x === Infinity || x === -Infinity) {
9986 return 0;
9987 }
9988
9989 return x;
9990 };
9991
9992 var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9993 // don't update with zero area boxes
9994 if (x2 - x1 === 0 || y2 - y1 === 0) {
9995 return;
9996 } // don't update with null dim
9997
9998
9999 if (x1 == null || y1 == null || x2 == null || y2 == null) {
10000 return;
10001 }
10002
10003 b.x1 = x1 < b.x1 ? x1 : b.x1;
10004 b.x2 = x2 > b.x2 ? x2 : b.x2;
10005 b.y1 = y1 < b.y1 ? y1 : b.y1;
10006 b.y2 = y2 > b.y2 ? y2 : b.y2;
10007 b.w = b.x2 - b.x1;
10008 b.h = b.y2 - b.y1;
10009 };
10010
10011 var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
10012 if (b2 == null) {
10013 return b;
10014 }
10015
10016 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
10017 };
10018
10019 var prefixedProperty = function prefixedProperty(obj, field, prefix) {
10020 return getPrefixedProperty(obj, field, prefix);
10021 };
10022
10023 var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
10024 if (ele.cy().headless()) {
10025 return;
10026 }
10027
10028 var _p = ele._private;
10029 var rstyle = _p.rstyle;
10030 var halfArW = rstyle.arrowWidth / 2;
10031 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
10032 var x;
10033 var y;
10034
10035 if (arrowType !== 'none') {
10036 if (prefix === 'source') {
10037 x = rstyle.srcX;
10038 y = rstyle.srcY;
10039 } else if (prefix === 'target') {
10040 x = rstyle.tgtX;
10041 y = rstyle.tgtY;
10042 } else {
10043 x = rstyle.midX;
10044 y = rstyle.midY;
10045 } // always store the individual arrow bounds
10046
10047
10048 var bbs = _p.arrowBounds = _p.arrowBounds || {};
10049 var bb = bbs[prefix] = bbs[prefix] || {};
10050 bb.x1 = x - halfArW;
10051 bb.y1 = y - halfArW;
10052 bb.x2 = x + halfArW;
10053 bb.y2 = y + halfArW;
10054 bb.w = bb.x2 - bb.x1;
10055 bb.h = bb.y2 - bb.y1;
10056 expandBoundingBox(bb, 1);
10057 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
10058 }
10059 };
10060
10061 var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
10062 if (ele.cy().headless()) {
10063 return;
10064 }
10065
10066 var prefixDash;
10067
10068 if (prefix) {
10069 prefixDash = prefix + '-';
10070 } else {
10071 prefixDash = '';
10072 }
10073
10074 var _p = ele._private;
10075 var rstyle = _p.rstyle;
10076 var label = ele.pstyle(prefixDash + 'label').strValue;
10077
10078 if (label) {
10079 var halign = ele.pstyle('text-halign');
10080 var valign = ele.pstyle('text-valign');
10081 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
10082 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
10083 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
10084 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
10085 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
10086 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
10087 var isEdge = ele.isEdge();
10088 var rotation = ele.pstyle(prefixDash + 'text-rotation');
10089 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
10090 var borderWidth = ele.pstyle('text-border-width').pfValue;
10091 var halfBorderWidth = borderWidth / 2;
10092 var padding = ele.pstyle('text-background-padding').pfValue;
10093 var marginOfError = 2; // expand to work around browser dimension inaccuracies
10094
10095 var lh = labelHeight;
10096 var lw = labelWidth;
10097 var lw_2 = lw / 2;
10098 var lh_2 = lh / 2;
10099 var lx1, lx2, ly1, ly2;
10100
10101 if (isEdge) {
10102 lx1 = labelX - lw_2;
10103 lx2 = labelX + lw_2;
10104 ly1 = labelY - lh_2;
10105 ly2 = labelY + lh_2;
10106 } else {
10107 switch (halign.value) {
10108 case 'left':
10109 lx1 = labelX - lw;
10110 lx2 = labelX;
10111 break;
10112
10113 case 'center':
10114 lx1 = labelX - lw_2;
10115 lx2 = labelX + lw_2;
10116 break;
10117
10118 case 'right':
10119 lx1 = labelX;
10120 lx2 = labelX + lw;
10121 break;
10122 }
10123
10124 switch (valign.value) {
10125 case 'top':
10126 ly1 = labelY - lh;
10127 ly2 = labelY;
10128 break;
10129
10130 case 'center':
10131 ly1 = labelY - lh_2;
10132 ly2 = labelY + lh_2;
10133 break;
10134
10135 case 'bottom':
10136 ly1 = labelY;
10137 ly2 = labelY + lh;
10138 break;
10139 }
10140 } // shift by margin and expand by outline and border
10141
10142
10143 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10144 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10145 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10146 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
10147
10148 var bbPrefix = prefix || 'main';
10149 var bbs = _p.labelBounds;
10150 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
10151 bb.x1 = lx1;
10152 bb.y1 = ly1;
10153 bb.x2 = lx2;
10154 bb.y2 = ly2;
10155 bb.w = lx2 - lx1;
10156 bb.h = ly2 - ly1;
10157 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
10158 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
10159
10160 if (isAutorotate || isPfValue) {
10161 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
10162 var cos = Math.cos(theta);
10163 var sin = Math.sin(theta); // rotation point (default value for center-center)
10164
10165 var xo = (lx1 + lx2) / 2;
10166 var yo = (ly1 + ly2) / 2;
10167
10168 if (!isEdge) {
10169 switch (halign.value) {
10170 case 'left':
10171 xo = lx2;
10172 break;
10173
10174 case 'right':
10175 xo = lx1;
10176 break;
10177 }
10178
10179 switch (valign.value) {
10180 case 'top':
10181 yo = ly2;
10182 break;
10183
10184 case 'bottom':
10185 yo = ly1;
10186 break;
10187 }
10188 }
10189
10190 var rotate = function rotate(x, y) {
10191 x = x - xo;
10192 y = y - yo;
10193 return {
10194 x: x * cos - y * sin + xo,
10195 y: x * sin + y * cos + yo
10196 };
10197 };
10198
10199 var px1y1 = rotate(lx1, ly1);
10200 var px1y2 = rotate(lx1, ly2);
10201 var px2y1 = rotate(lx2, ly1);
10202 var px2y2 = rotate(lx2, ly2);
10203 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10204 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10205 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10206 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10207 }
10208
10209 var bbPrefixRot = bbPrefix + 'Rot';
10210 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
10211 bbRot.x1 = lx1;
10212 bbRot.y1 = ly1;
10213 bbRot.x2 = lx2;
10214 bbRot.y2 = ly2;
10215 bbRot.w = lx2 - lx1;
10216 bbRot.h = ly2 - ly1;
10217 updateBounds(bounds, lx1, ly1, lx2, ly2);
10218 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
10219 }
10220
10221 return bounds;
10222 }; // get the bounding box of the elements (in raw model position)
10223
10224
10225 var boundingBoxImpl = function boundingBoxImpl(ele, options) {
10226 var cy = ele._private.cy;
10227 var styleEnabled = cy.styleEnabled();
10228 var headless = cy.headless();
10229 var bounds = makeBoundingBox();
10230 var _p = ele._private;
10231 var isNode = ele.isNode();
10232 var isEdge = ele.isEdge();
10233 var ex1, ex2, ey1, ey2; // extrema of body / lines
10234
10235 var x, y; // node pos
10236
10237 var rstyle = _p.rstyle;
10238 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
10239 // (other factors like width values will be considered later in this function anyway)
10240
10241 var isDisplayed = function isDisplayed(ele) {
10242 return ele.pstyle('display').value !== 'none';
10243 };
10244
10245 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
10246 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
10247
10248 if (displayed) {
10249 // displayed suffices, since we will find zero area eles anyway
10250 var overlayOpacity = 0;
10251 var overlayPadding = 0;
10252
10253 if (styleEnabled && options.includeOverlays) {
10254 overlayOpacity = ele.pstyle('overlay-opacity').value;
10255
10256 if (overlayOpacity !== 0) {
10257 overlayPadding = ele.pstyle('overlay-padding').value;
10258 }
10259 }
10260
10261 var w = 0;
10262 var wHalf = 0;
10263
10264 if (styleEnabled) {
10265 w = ele.pstyle('width').pfValue;
10266 wHalf = w / 2;
10267 }
10268
10269 if (isNode && options.includeNodes) {
10270 var pos = ele.position();
10271 x = pos.x;
10272 y = pos.y;
10273
10274 var _w = ele.outerWidth();
10275
10276 var halfW = _w / 2;
10277 var h = ele.outerHeight();
10278 var halfH = h / 2; // handle node dimensions
10279 /////////////////////////
10280
10281 ex1 = x - halfW;
10282 ex2 = x + halfW;
10283 ey1 = y - halfH;
10284 ey2 = y + halfH;
10285 updateBounds(bounds, ex1, ey1, ex2, ey2);
10286 } else if (isEdge && options.includeEdges) {
10287 if (styleEnabled && !headless) {
10288 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
10289 //////////////////////////////////////////////
10290
10291 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10292 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10293 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10294 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
10295
10296 ex1 -= wHalf;
10297 ex2 += wHalf;
10298 ey1 -= wHalf;
10299 ey2 += wHalf;
10300 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
10301 ////////////////
10302
10303 if (curveStyle === 'haystack') {
10304 var hpts = rstyle.haystackPts;
10305
10306 if (hpts && hpts.length === 2) {
10307 ex1 = hpts[0].x;
10308 ey1 = hpts[0].y;
10309 ex2 = hpts[1].x;
10310 ey2 = hpts[1].y;
10311
10312 if (ex1 > ex2) {
10313 var temp = ex1;
10314 ex1 = ex2;
10315 ex2 = temp;
10316 }
10317
10318 if (ey1 > ey2) {
10319 var _temp = ey1;
10320 ey1 = ey2;
10321 ey2 = _temp;
10322 }
10323
10324 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
10325 }
10326 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
10327 var pts;
10328
10329 switch (curveStyle) {
10330 case 'bezier':
10331 case 'unbundled-bezier':
10332 pts = rstyle.bezierPts;
10333 break;
10334
10335 case 'segments':
10336 case 'taxi':
10337 pts = rstyle.linePts;
10338 break;
10339 }
10340
10341 if (pts != null) {
10342 for (var j = 0; j < pts.length; j++) {
10343 var pt = pts[j];
10344 ex1 = pt.x - wHalf;
10345 ex2 = pt.x + wHalf;
10346 ey1 = pt.y - wHalf;
10347 ey2 = pt.y + wHalf;
10348 updateBounds(bounds, ex1, ey1, ex2, ey2);
10349 }
10350 }
10351 } // bezier-like or segment-like edge
10352
10353 } else {
10354 // headless or style disabled
10355 // fallback on source and target positions
10356 //////////////////////////////////////////
10357 var n1 = ele.source();
10358 var n1pos = n1.position();
10359 var n2 = ele.target();
10360 var n2pos = n2.position();
10361 ex1 = n1pos.x;
10362 ex2 = n2pos.x;
10363 ey1 = n1pos.y;
10364 ey2 = n2pos.y;
10365
10366 if (ex1 > ex2) {
10367 var _temp2 = ex1;
10368 ex1 = ex2;
10369 ex2 = _temp2;
10370 }
10371
10372 if (ey1 > ey2) {
10373 var _temp3 = ey1;
10374 ey1 = ey2;
10375 ey2 = _temp3;
10376 } // take into account edge width
10377
10378
10379 ex1 -= wHalf;
10380 ex2 += wHalf;
10381 ey1 -= wHalf;
10382 ey2 += wHalf;
10383 updateBounds(bounds, ex1, ey1, ex2, ey2);
10384 } // headless or style disabled
10385
10386 } // edges
10387 // handle edge arrow size
10388 /////////////////////////
10389
10390
10391 if (styleEnabled && options.includeEdges && isEdge) {
10392 updateBoundsFromArrow(bounds, ele, 'mid-source');
10393 updateBoundsFromArrow(bounds, ele, 'mid-target');
10394 updateBoundsFromArrow(bounds, ele, 'source');
10395 updateBoundsFromArrow(bounds, ele, 'target');
10396 } // ghost
10397 ////////
10398
10399
10400 if (styleEnabled) {
10401 var ghost = ele.pstyle('ghost').value === 'yes';
10402
10403 if (ghost) {
10404 var gx = ele.pstyle('ghost-offset-x').pfValue;
10405 var gy = ele.pstyle('ghost-offset-y').pfValue;
10406 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
10407 }
10408 } // always store the body bounds separately from the labels
10409
10410
10411 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
10412 assignBoundingBox(bbBody, bounds);
10413 expandBoundingBoxSides(bbBody, manualExpansion);
10414 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
10415 // overlay
10416 //////////
10417
10418 if (styleEnabled) {
10419 ex1 = bounds.x1;
10420 ex2 = bounds.x2;
10421 ey1 = bounds.y1;
10422 ey2 = bounds.y2;
10423 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
10424 } // always store the body bounds separately from the labels
10425
10426
10427 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
10428 assignBoundingBox(bbOverlay, bounds);
10429 expandBoundingBoxSides(bbOverlay, manualExpansion);
10430 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
10431 // handle label dimensions
10432 //////////////////////////
10433
10434 var bbLabels = _p.labelBounds = _p.labelBounds || {};
10435
10436 if (bbLabels.all != null) {
10437 clearBoundingBox(bbLabels.all);
10438 } else {
10439 bbLabels.all = makeBoundingBox();
10440 }
10441
10442 if (styleEnabled && options.includeLabels) {
10443 if (options.includeMainLabels) {
10444 updateBoundsFromLabel(bounds, ele, null);
10445 }
10446
10447 if (isEdge) {
10448 if (options.includeSourceLabels) {
10449 updateBoundsFromLabel(bounds, ele, 'source');
10450 }
10451
10452 if (options.includeTargetLabels) {
10453 updateBoundsFromLabel(bounds, ele, 'target');
10454 }
10455 }
10456 } // style enabled for labels
10457
10458 } // if displayed
10459
10460
10461 bounds.x1 = noninf(bounds.x1);
10462 bounds.y1 = noninf(bounds.y1);
10463 bounds.x2 = noninf(bounds.x2);
10464 bounds.y2 = noninf(bounds.y2);
10465 bounds.w = noninf(bounds.x2 - bounds.x1);
10466 bounds.h = noninf(bounds.y2 - bounds.y1);
10467
10468 if (bounds.w > 0 && bounds.h > 0 && displayed) {
10469 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
10470
10471 expandBoundingBox(bounds, 1);
10472 }
10473
10474 return bounds;
10475 };
10476
10477 var getKey = function getKey(opts) {
10478 var i = 0;
10479
10480 var tf = function tf(val) {
10481 return (val ? 1 : 0) << i++;
10482 };
10483
10484 var key = 0;
10485 key += tf(opts.incudeNodes);
10486 key += tf(opts.includeEdges);
10487 key += tf(opts.includeLabels);
10488 key += tf(opts.includeMainLabels);
10489 key += tf(opts.includeSourceLabels);
10490 key += tf(opts.includeTargetLabels);
10491 key += tf(opts.includeOverlays);
10492 return key;
10493 };
10494
10495 var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
10496 if (ele.isEdge()) {
10497 var p1 = ele.source().position();
10498 var p2 = ele.target().position();
10499
10500 var r = function r(x) {
10501 return Math.round(x);
10502 };
10503
10504 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
10505 } else {
10506 return 0;
10507 }
10508 };
10509
10510 var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
10511 var _p = ele._private;
10512 var bb;
10513 var isEdge = ele.isEdge();
10514 var key = opts == null ? defBbOptsKey : getKey(opts);
10515 var usingDefOpts = key === defBbOptsKey;
10516 var currPosKey = getBoundingBoxPosKey(ele);
10517 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10518 var useCache = opts.useCache && isPosKeySame;
10519
10520 var isDirty = function isDirty(ele) {
10521 return ele._private.bbCache == null || ele._private.styleDirty;
10522 };
10523
10524 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
10525
10526 if (needRecalc) {
10527 if (!isPosKeySame) {
10528 ele.recalculateRenderedStyle(useCache);
10529 }
10530
10531 bb = boundingBoxImpl(ele, defBbOpts);
10532 _p.bbCache = bb;
10533 _p.bbCachePosKey = currPosKey;
10534 } else {
10535 bb = _p.bbCache;
10536 } // not using def opts => need to build up bb from combination of sub bbs
10537
10538
10539 if (!usingDefOpts) {
10540 var isNode = ele.isNode();
10541 bb = makeBoundingBox();
10542
10543 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
10544 if (opts.includeOverlays) {
10545 updateBoundsFromBox(bb, _p.overlayBounds);
10546 } else {
10547 updateBoundsFromBox(bb, _p.bodyBounds);
10548 }
10549 }
10550
10551 if (opts.includeLabels) {
10552 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
10553 updateBoundsFromBox(bb, _p.labelBounds.all);
10554 } else {
10555 if (opts.includeMainLabels) {
10556 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
10557 }
10558
10559 if (opts.includeSourceLabels) {
10560 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
10561 }
10562
10563 if (opts.includeTargetLabels) {
10564 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
10565 }
10566 }
10567 }
10568
10569 bb.w = bb.x2 - bb.x1;
10570 bb.h = bb.y2 - bb.y1;
10571 }
10572
10573 return bb;
10574 };
10575
10576 var defBbOpts = {
10577 includeNodes: true,
10578 includeEdges: true,
10579 includeLabels: true,
10580 includeMainLabels: true,
10581 includeSourceLabels: true,
10582 includeTargetLabels: true,
10583 includeOverlays: true,
10584 useCache: true
10585 };
10586 var defBbOptsKey = getKey(defBbOpts);
10587 var filledBbOpts = defaults(defBbOpts);
10588
10589 elesfn$k.boundingBox = function (options) {
10590 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
10591 // specified s.t. the cache is used, so check for this case to make it faster by
10592 // avoiding the overhead of the rest of the function
10593
10594 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
10595 if (options === undefined) {
10596 options = defBbOpts;
10597 } else {
10598 options = filledBbOpts(options);
10599 }
10600
10601 bounds = cachedBoundingBoxImpl(this[0], options);
10602 } else {
10603 bounds = makeBoundingBox();
10604 options = options || defBbOpts;
10605 var opts = filledBbOpts(options);
10606 var eles = this;
10607 var cy = eles.cy();
10608 var styleEnabled = cy.styleEnabled();
10609
10610 if (styleEnabled) {
10611 for (var i = 0; i < eles.length; i++) {
10612 var ele = eles[i];
10613 var _p = ele._private;
10614 var currPosKey = getBoundingBoxPosKey(ele);
10615 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10616 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
10617 ele.recalculateRenderedStyle(useCache);
10618 }
10619 }
10620
10621 this.updateCompoundBounds(!options.useCache);
10622
10623 for (var _i = 0; _i < eles.length; _i++) {
10624 var _ele = eles[_i];
10625 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
10626 }
10627 }
10628
10629 bounds.x1 = noninf(bounds.x1);
10630 bounds.y1 = noninf(bounds.y1);
10631 bounds.x2 = noninf(bounds.x2);
10632 bounds.y2 = noninf(bounds.y2);
10633 bounds.w = noninf(bounds.x2 - bounds.x1);
10634 bounds.h = noninf(bounds.y2 - bounds.y1);
10635 return bounds;
10636 };
10637
10638 elesfn$k.dirtyBoundingBoxCache = function () {
10639 for (var i = 0; i < this.length; i++) {
10640 var _p = this[i]._private;
10641 _p.bbCache = null;
10642 _p.bbCachePosKey = null;
10643 _p.bodyBounds = null;
10644 _p.overlayBounds = null;
10645 _p.labelBounds.all = null;
10646 _p.labelBounds.source = null;
10647 _p.labelBounds.target = null;
10648 _p.labelBounds.main = null;
10649 _p.labelBounds.sourceRot = null;
10650 _p.labelBounds.targetRot = null;
10651 _p.labelBounds.mainRot = null;
10652 _p.arrowBounds.source = null;
10653 _p.arrowBounds.target = null;
10654 _p.arrowBounds['mid-source'] = null;
10655 _p.arrowBounds['mid-target'] = null;
10656 }
10657
10658 this.emitAndNotify('bounds');
10659 return this;
10660 }; // private helper to get bounding box for custom node positions
10661 // - good for perf in certain cases but currently requires dirtying the rendered style
10662 // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
10663 // - try to use for only things like discrete layouts where the node position would change anyway
10664
10665
10666 elesfn$k.boundingBoxAt = function (fn) {
10667 var nodes = this.nodes();
10668 var cy = this.cy();
10669 var hasCompoundNodes = cy.hasCompoundNodes();
10670 var parents = cy.collection();
10671
10672 if (hasCompoundNodes) {
10673 parents = nodes.filter(function (node) {
10674 return node.isParent();
10675 });
10676 nodes = nodes.not(parents);
10677 }
10678
10679 if (plainObject(fn)) {
10680 var obj = fn;
10681
10682 fn = function fn() {
10683 return obj;
10684 };
10685 }
10686
10687 var storeOldPos = function storeOldPos(node, i) {
10688 return node._private.bbAtOldPos = fn(node, i);
10689 };
10690
10691 var getOldPos = function getOldPos(node) {
10692 return node._private.bbAtOldPos;
10693 };
10694
10695 cy.startBatch();
10696 nodes.forEach(storeOldPos).silentPositions(fn);
10697
10698 if (hasCompoundNodes) {
10699 parents.dirtyCompoundBoundsCache();
10700 parents.dirtyBoundingBoxCache();
10701 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10702 }
10703
10704 var bb = copyBoundingBox(this.boundingBox({
10705 useCache: false
10706 }));
10707 nodes.silentPositions(getOldPos);
10708
10709 if (hasCompoundNodes) {
10710 parents.dirtyCompoundBoundsCache();
10711 parents.dirtyBoundingBoxCache();
10712 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
10713 }
10714
10715 cy.endBatch();
10716 return bb;
10717 };
10718
10719 fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
10720 fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
10721 var bounds = elesfn$k;
10722
10723 var fn$4, elesfn$l;
10724 fn$4 = elesfn$l = {};
10725
10726 var defineDimFns = function defineDimFns(opts) {
10727 opts.uppercaseName = capitalize(opts.name);
10728 opts.autoName = 'auto' + opts.uppercaseName;
10729 opts.labelName = 'label' + opts.uppercaseName;
10730 opts.outerName = 'outer' + opts.uppercaseName;
10731 opts.uppercaseOuterName = capitalize(opts.outerName);
10732
10733 fn$4[opts.name] = function dimImpl() {
10734 var ele = this[0];
10735 var _p = ele._private;
10736 var cy = _p.cy;
10737 var styleEnabled = cy._private.styleEnabled;
10738
10739 if (ele) {
10740 if (styleEnabled) {
10741 if (ele.isParent()) {
10742 ele.updateCompoundBounds();
10743 return _p[opts.autoName] || 0;
10744 }
10745
10746 var d = ele.pstyle(opts.name);
10747
10748 switch (d.strValue) {
10749 case 'label':
10750 ele.recalculateRenderedStyle();
10751 return _p.rstyle[opts.labelName] || 0;
10752
10753 default:
10754 return d.pfValue;
10755 }
10756 } else {
10757 return 1;
10758 }
10759 }
10760 };
10761
10762 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10763 var ele = this[0];
10764 var _p = ele._private;
10765 var cy = _p.cy;
10766 var styleEnabled = cy._private.styleEnabled;
10767
10768 if (ele) {
10769 if (styleEnabled) {
10770 var dim = ele[opts.name]();
10771 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10772
10773 var padding = 2 * ele.padding();
10774 return dim + border + padding;
10775 } else {
10776 return 1;
10777 }
10778 }
10779 };
10780
10781 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10782 var ele = this[0];
10783
10784 if (ele) {
10785 var d = ele[opts.name]();
10786 return d * this.cy().zoom();
10787 }
10788 };
10789
10790 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10791 var ele = this[0];
10792
10793 if (ele) {
10794 var od = ele[opts.outerName]();
10795 return od * this.cy().zoom();
10796 }
10797 };
10798 };
10799
10800 defineDimFns({
10801 name: 'width'
10802 });
10803 defineDimFns({
10804 name: 'height'
10805 });
10806
10807 elesfn$l.padding = function () {
10808 var ele = this[0];
10809 var _p = ele._private;
10810
10811 if (ele.isParent()) {
10812 ele.updateCompoundBounds();
10813
10814 if (_p.autoPadding !== undefined) {
10815 return _p.autoPadding;
10816 } else {
10817 return ele.pstyle('padding').pfValue;
10818 }
10819 } else {
10820 return ele.pstyle('padding').pfValue;
10821 }
10822 };
10823
10824 elesfn$l.paddedHeight = function () {
10825 var ele = this[0];
10826 return ele.height() + 2 * ele.padding();
10827 };
10828
10829 elesfn$l.paddedWidth = function () {
10830 var ele = this[0];
10831 return ele.width() + 2 * ele.padding();
10832 };
10833
10834 var widthHeight = elesfn$l;
10835
10836 var ifEdge = function ifEdge(ele, getValue) {
10837 if (ele.isEdge()) {
10838 return getValue(ele);
10839 }
10840 };
10841
10842 var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10843 if (ele.isEdge()) {
10844 var cy = ele.cy();
10845 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10846 }
10847 };
10848
10849 var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10850 if (ele.isEdge()) {
10851 var cy = ele.cy();
10852 var pan = cy.pan();
10853 var zoom = cy.zoom();
10854 return getPoints(ele).map(function (p) {
10855 return modelToRenderedPosition(p, zoom, pan);
10856 });
10857 }
10858 };
10859
10860 var controlPoints = function controlPoints(ele) {
10861 return ele.renderer().getControlPoints(ele);
10862 };
10863
10864 var segmentPoints = function segmentPoints(ele) {
10865 return ele.renderer().getSegmentPoints(ele);
10866 };
10867
10868 var sourceEndpoint = function sourceEndpoint(ele) {
10869 return ele.renderer().getSourceEndpoint(ele);
10870 };
10871
10872 var targetEndpoint = function targetEndpoint(ele) {
10873 return ele.renderer().getTargetEndpoint(ele);
10874 };
10875
10876 var midpoint = function midpoint(ele) {
10877 return ele.renderer().getEdgeMidpoint(ele);
10878 };
10879
10880 var pts = {
10881 controlPoints: {
10882 get: controlPoints,
10883 mult: true
10884 },
10885 segmentPoints: {
10886 get: segmentPoints,
10887 mult: true
10888 },
10889 sourceEndpoint: {
10890 get: sourceEndpoint
10891 },
10892 targetEndpoint: {
10893 get: targetEndpoint
10894 },
10895 midpoint: {
10896 get: midpoint
10897 }
10898 };
10899
10900 var renderedName = function renderedName(name) {
10901 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10902 };
10903
10904 var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10905 var spec = pts[name];
10906 var rName = renderedName(name);
10907
10908 obj[name] = function () {
10909 return ifEdge(this, spec.get);
10910 };
10911
10912 if (spec.mult) {
10913 obj[rName] = function () {
10914 return ifEdgeRenderedPositions(this, spec.get);
10915 };
10916 } else {
10917 obj[rName] = function () {
10918 return ifEdgeRenderedPosition(this, spec.get);
10919 };
10920 }
10921
10922 return obj;
10923 }, {});
10924
10925 var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10926
10927 /*!
10928 Event object based on jQuery events, MIT license
10929
10930 https://jquery.org/license/
10931 https://tldrlegal.com/license/mit-license
10932 https://github.com/jquery/jquery/blob/master/src/event.js
10933 */
10934 var Event = function Event(src, props) {
10935 this.recycle(src, props);
10936 };
10937
10938 function returnFalse() {
10939 return false;
10940 }
10941
10942 function returnTrue() {
10943 return true;
10944 } // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10945
10946
10947 Event.prototype = {
10948 instanceString: function instanceString() {
10949 return 'event';
10950 },
10951 recycle: function recycle(src, props) {
10952 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10953
10954 if (src != null && src.preventDefault) {
10955 // Browser Event object
10956 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10957 // by a handler lower down the tree; reflect the correct value.
10958
10959 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10960 } else if (src != null && src.type) {
10961 // Plain object containing all event details
10962 props = src;
10963 } else {
10964 // Event string
10965 this.type = src;
10966 } // Put explicitly provided properties onto the event object
10967
10968
10969 if (props != null) {
10970 // more efficient to manually copy fields we use
10971 this.originalEvent = props.originalEvent;
10972 this.type = props.type != null ? props.type : this.type;
10973 this.cy = props.cy;
10974 this.target = props.target;
10975 this.position = props.position;
10976 this.renderedPosition = props.renderedPosition;
10977 this.namespace = props.namespace;
10978 this.layout = props.layout;
10979 }
10980
10981 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10982 // create a rendered position based on the passed position
10983 var pos = this.position;
10984 var zoom = this.cy.zoom();
10985 var pan = this.cy.pan();
10986 this.renderedPosition = {
10987 x: pos.x * zoom + pan.x,
10988 y: pos.y * zoom + pan.y
10989 };
10990 } // Create a timestamp if incoming event doesn't have one
10991
10992
10993 this.timeStamp = src && src.timeStamp || Date.now();
10994 },
10995 preventDefault: function preventDefault() {
10996 this.isDefaultPrevented = returnTrue;
10997 var e = this.originalEvent;
10998
10999 if (!e) {
11000 return;
11001 } // if preventDefault exists run it on the original event
11002
11003
11004 if (e.preventDefault) {
11005 e.preventDefault();
11006 }
11007 },
11008 stopPropagation: function stopPropagation() {
11009 this.isPropagationStopped = returnTrue;
11010 var e = this.originalEvent;
11011
11012 if (!e) {
11013 return;
11014 } // if stopPropagation exists run it on the original event
11015
11016
11017 if (e.stopPropagation) {
11018 e.stopPropagation();
11019 }
11020 },
11021 stopImmediatePropagation: function stopImmediatePropagation() {
11022 this.isImmediatePropagationStopped = returnTrue;
11023 this.stopPropagation();
11024 },
11025 isDefaultPrevented: returnFalse,
11026 isPropagationStopped: returnFalse,
11027 isImmediatePropagationStopped: returnFalse
11028 };
11029
11030 var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
11031
11032 var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
11033
11034 var defaults$8 = {
11035 qualifierCompare: function qualifierCompare(q1, q2) {
11036 return q1 === q2;
11037 },
11038 eventMatches: function eventMatches()
11039 /*context, listener, eventObj*/
11040 {
11041 return true;
11042 },
11043 addEventFields: function addEventFields()
11044 /*context, evt*/
11045 {},
11046 callbackContext: function callbackContext(context
11047 /*, listener, eventObj*/
11048 ) {
11049 return context;
11050 },
11051 beforeEmit: function beforeEmit()
11052 /* context, listener, eventObj */
11053 {},
11054 afterEmit: function afterEmit()
11055 /* context, listener, eventObj */
11056 {},
11057 bubble: function bubble()
11058 /*context*/
11059 {
11060 return false;
11061 },
11062 parent: function parent()
11063 /*context*/
11064 {
11065 return null;
11066 },
11067 context: null
11068 };
11069 var defaultsKeys = Object.keys(defaults$8);
11070 var emptyOpts = {};
11071
11072 function Emitter() {
11073 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
11074 var context = arguments.length > 1 ? arguments[1] : undefined;
11075
11076 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
11077 for (var i = 0; i < defaultsKeys.length; i++) {
11078 var key = defaultsKeys[i];
11079 this[key] = opts[key] || defaults$8[key];
11080 }
11081
11082 this.context = context || this.context;
11083 this.listeners = [];
11084 this.emitting = 0;
11085 }
11086
11087 var p = Emitter.prototype;
11088
11089 var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
11090 if (fn(qualifier)) {
11091 callback = qualifier;
11092 qualifier = null;
11093 }
11094
11095 if (confOverrides) {
11096 if (conf == null) {
11097 conf = confOverrides;
11098 } else {
11099 conf = extend({}, conf, confOverrides);
11100 }
11101 }
11102
11103 var eventList = array(events) ? events : events.split(/\s+/);
11104
11105 for (var i = 0; i < eventList.length; i++) {
11106 var evt = eventList[i];
11107
11108 if (emptyString(evt)) {
11109 continue;
11110 }
11111
11112 var match = evt.match(eventRegex); // type[.namespace]
11113
11114 if (match) {
11115 var type = match[1];
11116 var namespace = match[2] ? match[2] : null;
11117 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
11118
11119 if (ret === false) {
11120 break;
11121 } // allow exiting early
11122
11123 }
11124 }
11125 };
11126
11127 var makeEventObj = function makeEventObj(self, obj) {
11128 self.addEventFields(self.context, obj);
11129 return new Event(obj.type, obj);
11130 };
11131
11132 var forEachEventObj = function forEachEventObj(self, handler, events) {
11133 if (event(events)) {
11134 handler(self, events);
11135 return;
11136 } else if (plainObject(events)) {
11137 handler(self, makeEventObj(self, events));
11138 return;
11139 }
11140
11141 var eventList = array(events) ? events : events.split(/\s+/);
11142
11143 for (var i = 0; i < eventList.length; i++) {
11144 var evt = eventList[i];
11145
11146 if (emptyString(evt)) {
11147 continue;
11148 }
11149
11150 var match = evt.match(eventRegex); // type[.namespace]
11151
11152 if (match) {
11153 var type = match[1];
11154 var namespace = match[2] ? match[2] : null;
11155 var eventObj = makeEventObj(self, {
11156 type: type,
11157 namespace: namespace,
11158 target: self.context
11159 });
11160 handler(self, eventObj);
11161 }
11162 }
11163 };
11164
11165 p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
11166 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
11167 if (fn(callback)) {
11168 self.listeners.push({
11169 event: event,
11170 // full event string
11171 callback: callback,
11172 // callback to run
11173 type: type,
11174 // the event type (e.g. 'click')
11175 namespace: namespace,
11176 // the event namespace (e.g. ".foo")
11177 qualifier: qualifier,
11178 // a restriction on whether to match this emitter
11179 conf: conf // additional configuration
11180
11181 });
11182 }
11183 }, events, qualifier, callback, conf, confOverrides);
11184 return this;
11185 };
11186
11187 p.one = function (events, qualifier, callback, conf) {
11188 return this.on(events, qualifier, callback, conf, {
11189 one: true
11190 });
11191 };
11192
11193 p.removeListener = p.off = function (events, qualifier, callback, conf) {
11194 var _this = this;
11195
11196 if (this.emitting !== 0) {
11197 this.listeners = copyArray(this.listeners);
11198 }
11199
11200 var listeners = this.listeners;
11201
11202 var _loop = function _loop(i) {
11203 var listener = listeners[i];
11204 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
11205 /*, conf*/
11206 ) {
11207 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
11208 listeners.splice(i, 1);
11209 return false;
11210 }
11211 }, events, qualifier, callback, conf);
11212 };
11213
11214 for (var i = listeners.length - 1; i >= 0; i--) {
11215 _loop(i);
11216 }
11217
11218 return this;
11219 };
11220
11221 p.removeAllListeners = function () {
11222 return this.removeListener('*');
11223 };
11224
11225 p.emit = p.trigger = function (events, extraParams, manualCallback) {
11226 var listeners = this.listeners;
11227 var numListenersBeforeEmit = listeners.length;
11228 this.emitting++;
11229
11230 if (!array(extraParams)) {
11231 extraParams = [extraParams];
11232 }
11233
11234 forEachEventObj(this, function (self, eventObj) {
11235 if (manualCallback != null) {
11236 listeners = [{
11237 event: eventObj.event,
11238 type: eventObj.type,
11239 namespace: eventObj.namespace,
11240 callback: manualCallback
11241 }];
11242 numListenersBeforeEmit = listeners.length;
11243 }
11244
11245 var _loop2 = function _loop2(i) {
11246 var listener = listeners[i];
11247
11248 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
11249 var args = [eventObj];
11250
11251 if (extraParams != null) {
11252 push(args, extraParams);
11253 }
11254
11255 self.beforeEmit(self.context, listener, eventObj);
11256
11257 if (listener.conf && listener.conf.one) {
11258 self.listeners = self.listeners.filter(function (l) {
11259 return l !== listener;
11260 });
11261 }
11262
11263 var context = self.callbackContext(self.context, listener, eventObj);
11264 var ret = listener.callback.apply(context, args);
11265 self.afterEmit(self.context, listener, eventObj);
11266
11267 if (ret === false) {
11268 eventObj.stopPropagation();
11269 eventObj.preventDefault();
11270 }
11271 } // if listener matches
11272
11273 };
11274
11275 for (var i = 0; i < numListenersBeforeEmit; i++) {
11276 _loop2(i);
11277 } // for listener
11278
11279
11280 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
11281 self.parent(self.context).emit(eventObj, extraParams);
11282 }
11283 }, events);
11284 this.emitting--;
11285 return this;
11286 };
11287
11288 var emitterOptions = {
11289 qualifierCompare: function qualifierCompare(selector1, selector2) {
11290 if (selector1 == null || selector2 == null) {
11291 return selector1 == null && selector2 == null;
11292 } else {
11293 return selector1.sameText(selector2);
11294 }
11295 },
11296 eventMatches: function eventMatches(ele, listener, eventObj) {
11297 var selector = listener.qualifier;
11298
11299 if (selector != null) {
11300 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
11301 }
11302
11303 return true;
11304 },
11305 addEventFields: function addEventFields(ele, evt) {
11306 evt.cy = ele.cy();
11307 evt.target = ele;
11308 },
11309 callbackContext: function callbackContext(ele, listener, eventObj) {
11310 return listener.qualifier != null ? eventObj.target : ele;
11311 },
11312 beforeEmit: function beforeEmit(context, listener
11313 /*, eventObj*/
11314 ) {
11315 if (listener.conf && listener.conf.once) {
11316 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
11317 }
11318 },
11319 bubble: function bubble() {
11320 return true;
11321 },
11322 parent: function parent(ele) {
11323 return ele.isChild() ? ele.parent() : ele.cy();
11324 }
11325 };
11326
11327 var argSelector = function argSelector(arg) {
11328 if (string(arg)) {
11329 return new Selector(arg);
11330 } else {
11331 return arg;
11332 }
11333 };
11334
11335 var elesfn$m = {
11336 createEmitter: function createEmitter() {
11337 for (var i = 0; i < this.length; i++) {
11338 var ele = this[i];
11339 var _p = ele._private;
11340
11341 if (!_p.emitter) {
11342 _p.emitter = new Emitter(emitterOptions, ele);
11343 }
11344 }
11345
11346 return this;
11347 },
11348 emitter: function emitter() {
11349 return this._private.emitter;
11350 },
11351 on: function on(events, selector, callback) {
11352 var argSel = argSelector(selector);
11353
11354 for (var i = 0; i < this.length; i++) {
11355 var ele = this[i];
11356 ele.emitter().on(events, argSel, callback);
11357 }
11358
11359 return this;
11360 },
11361 removeListener: function removeListener(events, selector, callback) {
11362 var argSel = argSelector(selector);
11363
11364 for (var i = 0; i < this.length; i++) {
11365 var ele = this[i];
11366 ele.emitter().removeListener(events, argSel, callback);
11367 }
11368
11369 return this;
11370 },
11371 removeAllListeners: function removeAllListeners() {
11372 for (var i = 0; i < this.length; i++) {
11373 var ele = this[i];
11374 ele.emitter().removeAllListeners();
11375 }
11376
11377 return this;
11378 },
11379 one: function one(events, selector, callback) {
11380 var argSel = argSelector(selector);
11381
11382 for (var i = 0; i < this.length; i++) {
11383 var ele = this[i];
11384 ele.emitter().one(events, argSel, callback);
11385 }
11386
11387 return this;
11388 },
11389 once: function once(events, selector, callback) {
11390 var argSel = argSelector(selector);
11391
11392 for (var i = 0; i < this.length; i++) {
11393 var ele = this[i];
11394 ele.emitter().on(events, argSel, callback, {
11395 once: true,
11396 onceCollection: this
11397 });
11398 }
11399 },
11400 emit: function emit(events, extraParams) {
11401 for (var i = 0; i < this.length; i++) {
11402 var ele = this[i];
11403 ele.emitter().emit(events, extraParams);
11404 }
11405
11406 return this;
11407 },
11408 emitAndNotify: function emitAndNotify(event, extraParams) {
11409 // for internal use only
11410 if (this.length === 0) {
11411 return;
11412 } // empty collections don't need to notify anything
11413 // notify renderer
11414
11415
11416 this.cy().notify(event, this);
11417 this.emit(event, extraParams);
11418 return this;
11419 }
11420 };
11421 define$3.eventAliasesOn(elesfn$m);
11422
11423 var elesfn$n = {
11424 nodes: function nodes(selector) {
11425 return this.filter(function (ele) {
11426 return ele.isNode();
11427 }).filter(selector);
11428 },
11429 edges: function edges(selector) {
11430 return this.filter(function (ele) {
11431 return ele.isEdge();
11432 }).filter(selector);
11433 },
11434 // internal helper to get nodes and edges as separate collections with single iteration over elements
11435 byGroup: function byGroup() {
11436 var nodes = this.spawn();
11437 var edges = this.spawn();
11438
11439 for (var i = 0; i < this.length; i++) {
11440 var ele = this[i];
11441
11442 if (ele.isNode()) {
11443 nodes.push(ele);
11444 } else {
11445 edges.push(ele);
11446 }
11447 }
11448
11449 return {
11450 nodes: nodes,
11451 edges: edges
11452 };
11453 },
11454 filter: function filter(_filter, thisArg) {
11455 if (_filter === undefined) {
11456 // check this first b/c it's the most common/performant case
11457 return this;
11458 } else if (string(_filter) || elementOrCollection(_filter)) {
11459 return new Selector(_filter).filter(this);
11460 } else if (fn(_filter)) {
11461 var filterEles = this.spawn();
11462 var eles = this;
11463
11464 for (var i = 0; i < eles.length; i++) {
11465 var ele = eles[i];
11466 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
11467
11468 if (include) {
11469 filterEles.push(ele);
11470 }
11471 }
11472
11473 return filterEles;
11474 }
11475
11476 return this.spawn(); // if not handled by above, give 'em an empty collection
11477 },
11478 not: function not(toRemove) {
11479 if (!toRemove) {
11480 return this;
11481 } else {
11482 if (string(toRemove)) {
11483 toRemove = this.filter(toRemove);
11484 }
11485
11486 var elements = this.spawn();
11487
11488 for (var i = 0; i < this.length; i++) {
11489 var element = this[i];
11490 var remove = toRemove.has(element);
11491
11492 if (!remove) {
11493 elements.push(element);
11494 }
11495 }
11496
11497 return elements;
11498 }
11499 },
11500 absoluteComplement: function absoluteComplement() {
11501 var cy = this.cy();
11502 return cy.mutableElements().not(this);
11503 },
11504 intersect: function intersect(other) {
11505 // if a selector is specified, then filter by it instead
11506 if (string(other)) {
11507 var selector = other;
11508 return this.filter(selector);
11509 }
11510
11511 var elements = this.spawn();
11512 var col1 = this;
11513 var col2 = other;
11514 var col1Smaller = this.length < other.length;
11515 var colS = col1Smaller ? col1 : col2;
11516 var colL = col1Smaller ? col2 : col1;
11517
11518 for (var i = 0; i < colS.length; i++) {
11519 var ele = colS[i];
11520
11521 if (colL.has(ele)) {
11522 elements.push(ele);
11523 }
11524 }
11525
11526 return elements;
11527 },
11528 xor: function xor(other) {
11529 var cy = this._private.cy;
11530
11531 if (string(other)) {
11532 other = cy.$(other);
11533 }
11534
11535 var elements = this.spawn();
11536 var col1 = this;
11537 var col2 = other;
11538
11539 var add = function add(col, other) {
11540 for (var i = 0; i < col.length; i++) {
11541 var ele = col[i];
11542 var id = ele._private.data.id;
11543 var inOther = other.hasElementWithId(id);
11544
11545 if (!inOther) {
11546 elements.push(ele);
11547 }
11548 }
11549 };
11550
11551 add(col1, col2);
11552 add(col2, col1);
11553 return elements;
11554 },
11555 diff: function diff(other) {
11556 var cy = this._private.cy;
11557
11558 if (string(other)) {
11559 other = cy.$(other);
11560 }
11561
11562 var left = this.spawn();
11563 var right = this.spawn();
11564 var both = this.spawn();
11565 var col1 = this;
11566 var col2 = other;
11567
11568 var add = function add(col, other, retEles) {
11569 for (var i = 0; i < col.length; i++) {
11570 var ele = col[i];
11571 var id = ele._private.data.id;
11572 var inOther = other.hasElementWithId(id);
11573
11574 if (inOther) {
11575 both.merge(ele);
11576 } else {
11577 retEles.push(ele);
11578 }
11579 }
11580 };
11581
11582 add(col1, col2, left);
11583 add(col2, col1, right);
11584 return {
11585 left: left,
11586 right: right,
11587 both: both
11588 };
11589 },
11590 add: function add(toAdd) {
11591 var cy = this._private.cy;
11592
11593 if (!toAdd) {
11594 return this;
11595 }
11596
11597 if (string(toAdd)) {
11598 var selector = toAdd;
11599 toAdd = cy.mutableElements().filter(selector);
11600 }
11601
11602 var elements = this.spawnSelf();
11603
11604 for (var i = 0; i < toAdd.length; i++) {
11605 var ele = toAdd[i];
11606 var add = !this.has(ele);
11607
11608 if (add) {
11609 elements.push(ele);
11610 }
11611 }
11612
11613 return elements;
11614 },
11615 // in place merge on calling collection
11616 merge: function merge(toAdd) {
11617 var _p = this._private;
11618 var cy = _p.cy;
11619
11620 if (!toAdd) {
11621 return this;
11622 }
11623
11624 if (toAdd && string(toAdd)) {
11625 var selector = toAdd;
11626 toAdd = cy.mutableElements().filter(selector);
11627 }
11628
11629 var map = _p.map;
11630
11631 for (var i = 0; i < toAdd.length; i++) {
11632 var toAddEle = toAdd[i];
11633 var id = toAddEle._private.data.id;
11634 var add = !map.has(id);
11635
11636 if (add) {
11637 var index = this.length++;
11638 this[index] = toAddEle;
11639 map.set(id, {
11640 ele: toAddEle,
11641 index: index
11642 });
11643 }
11644 }
11645
11646 return this; // chaining
11647 },
11648 unmergeAt: function unmergeAt(i) {
11649 var ele = this[i];
11650 var id = ele.id();
11651 var _p = this._private;
11652 var map = _p.map; // remove ele
11653
11654 this[i] = undefined;
11655 map["delete"](id);
11656 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
11657
11658 if (this.length > 1 && !unmergedLastEle) {
11659 var lastEleI = this.length - 1;
11660 var lastEle = this[lastEleI];
11661 var lastEleId = lastEle._private.data.id;
11662 this[lastEleI] = undefined;
11663 this[i] = lastEle;
11664 map.set(lastEleId, {
11665 ele: lastEle,
11666 index: i
11667 });
11668 } // the collection is now 1 ele smaller
11669
11670
11671 this.length--;
11672 return this;
11673 },
11674 // remove single ele in place in calling collection
11675 unmergeOne: function unmergeOne(ele) {
11676 ele = ele[0];
11677 var _p = this._private;
11678 var id = ele._private.data.id;
11679 var map = _p.map;
11680 var entry = map.get(id);
11681
11682 if (!entry) {
11683 return this; // no need to remove
11684 }
11685
11686 var i = entry.index;
11687 this.unmergeAt(i);
11688 return this;
11689 },
11690 // remove eles in place on calling collection
11691 unmerge: function unmerge(toRemove) {
11692 var cy = this._private.cy;
11693
11694 if (!toRemove) {
11695 return this;
11696 }
11697
11698 if (toRemove && string(toRemove)) {
11699 var selector = toRemove;
11700 toRemove = cy.mutableElements().filter(selector);
11701 }
11702
11703 for (var i = 0; i < toRemove.length; i++) {
11704 this.unmergeOne(toRemove[i]);
11705 }
11706
11707 return this; // chaining
11708 },
11709 unmergeBy: function unmergeBy(toRmFn) {
11710 for (var i = this.length - 1; i >= 0; i--) {
11711 var ele = this[i];
11712
11713 if (toRmFn(ele)) {
11714 this.unmergeAt(i);
11715 }
11716 }
11717
11718 return this;
11719 },
11720 map: function map(mapFn, thisArg) {
11721 var arr = [];
11722 var eles = this;
11723
11724 for (var i = 0; i < eles.length; i++) {
11725 var ele = eles[i];
11726 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11727 arr.push(ret);
11728 }
11729
11730 return arr;
11731 },
11732 reduce: function reduce(fn, initialValue) {
11733 var val = initialValue;
11734 var eles = this;
11735
11736 for (var i = 0; i < eles.length; i++) {
11737 val = fn(val, eles[i], i, eles);
11738 }
11739
11740 return val;
11741 },
11742 max: function max(valFn, thisArg) {
11743 var max = -Infinity;
11744 var maxEle;
11745 var eles = this;
11746
11747 for (var i = 0; i < eles.length; i++) {
11748 var ele = eles[i];
11749 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11750
11751 if (val > max) {
11752 max = val;
11753 maxEle = ele;
11754 }
11755 }
11756
11757 return {
11758 value: max,
11759 ele: maxEle
11760 };
11761 },
11762 min: function min(valFn, thisArg) {
11763 var min = Infinity;
11764 var minEle;
11765 var eles = this;
11766
11767 for (var i = 0; i < eles.length; i++) {
11768 var ele = eles[i];
11769 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11770
11771 if (val < min) {
11772 min = val;
11773 minEle = ele;
11774 }
11775 }
11776
11777 return {
11778 value: min,
11779 ele: minEle
11780 };
11781 }
11782 }; // aliases
11783
11784 var fn$5 = elesfn$n;
11785 fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11786 fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11787 fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11788 fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11789 fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11790 fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11791
11792 var elesfn$o = {
11793 isNode: function isNode() {
11794 return this.group() === 'nodes';
11795 },
11796 isEdge: function isEdge() {
11797 return this.group() === 'edges';
11798 },
11799 isLoop: function isLoop() {
11800 return this.isEdge() && this.source()[0] === this.target()[0];
11801 },
11802 isSimple: function isSimple() {
11803 return this.isEdge() && this.source()[0] !== this.target()[0];
11804 },
11805 group: function group() {
11806 var ele = this[0];
11807
11808 if (ele) {
11809 return ele._private.group;
11810 }
11811 }
11812 };
11813
11814 /**
11815 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11816 * and z-index (low to high). These styles affect how this applies:
11817 *
11818 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11819 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11820 * root to leaves of the compound graph. The last drawn is `top`.
11821 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11822 * `manual` ignores this convention and draws based on the `z-index` value setting.
11823 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11824 * `z-index` will be drawn on top of an element with a lower `z-index`.
11825 */
11826
11827 var zIndexSort = function zIndexSort(a, b) {
11828 var cy = a.cy();
11829 var hasCompoundNodes = cy.hasCompoundNodes();
11830
11831 function getDepth(ele) {
11832 var style = ele.pstyle('z-compound-depth');
11833
11834 if (style.value === 'auto') {
11835 return hasCompoundNodes ? ele.zDepth() : 0;
11836 } else if (style.value === 'bottom') {
11837 return -1;
11838 } else if (style.value === 'top') {
11839 return MAX_INT;
11840 } // 'orphan'
11841
11842
11843 return 0;
11844 }
11845
11846 var depthDiff = getDepth(a) - getDepth(b);
11847
11848 if (depthDiff !== 0) {
11849 return depthDiff;
11850 }
11851
11852 function getEleDepth(ele) {
11853 var style = ele.pstyle('z-index-compare');
11854
11855 if (style.value === 'auto') {
11856 return ele.isNode() ? 1 : 0;
11857 } // 'manual'
11858
11859
11860 return 0;
11861 }
11862
11863 var eleDiff = getEleDepth(a) - getEleDepth(b);
11864
11865 if (eleDiff !== 0) {
11866 return eleDiff;
11867 }
11868
11869 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11870
11871 if (zDiff !== 0) {
11872 return zDiff;
11873 } // compare indices in the core (order added to graph w/ last on top)
11874
11875
11876 return a.poolIndex() - b.poolIndex();
11877 };
11878
11879 var elesfn$p = {
11880 forEach: function forEach(fn$1, thisArg) {
11881 if (fn(fn$1)) {
11882 var N = this.length;
11883
11884 for (var i = 0; i < N; i++) {
11885 var ele = this[i];
11886 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11887
11888 if (ret === false) {
11889 break;
11890 } // exit each early on return false
11891
11892 }
11893 }
11894
11895 return this;
11896 },
11897 toArray: function toArray() {
11898 var array = [];
11899
11900 for (var i = 0; i < this.length; i++) {
11901 array.push(this[i]);
11902 }
11903
11904 return array;
11905 },
11906 slice: function slice(start, end) {
11907 var array = [];
11908 var thisSize = this.length;
11909
11910 if (end == null) {
11911 end = thisSize;
11912 }
11913
11914 if (start == null) {
11915 start = 0;
11916 }
11917
11918 if (start < 0) {
11919 start = thisSize + start;
11920 }
11921
11922 if (end < 0) {
11923 end = thisSize + end;
11924 }
11925
11926 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11927 array.push(this[i]);
11928 }
11929
11930 return this.spawn(array);
11931 },
11932 size: function size() {
11933 return this.length;
11934 },
11935 eq: function eq(i) {
11936 return this[i] || this.spawn();
11937 },
11938 first: function first() {
11939 return this[0] || this.spawn();
11940 },
11941 last: function last() {
11942 return this[this.length - 1] || this.spawn();
11943 },
11944 empty: function empty() {
11945 return this.length === 0;
11946 },
11947 nonempty: function nonempty() {
11948 return !this.empty();
11949 },
11950 sort: function sort(sortFn) {
11951 if (!fn(sortFn)) {
11952 return this;
11953 }
11954
11955 var sorted = this.toArray().sort(sortFn);
11956 return this.spawn(sorted);
11957 },
11958 sortByZIndex: function sortByZIndex() {
11959 return this.sort(zIndexSort);
11960 },
11961 zDepth: function zDepth() {
11962 var ele = this[0];
11963
11964 if (!ele) {
11965 return undefined;
11966 } // let cy = ele.cy();
11967
11968
11969 var _p = ele._private;
11970 var group = _p.group;
11971
11972 if (group === 'nodes') {
11973 var depth = _p.data.parent ? ele.parents().size() : 0;
11974
11975 if (!ele.isParent()) {
11976 return MAX_INT - 1; // childless nodes always on top
11977 }
11978
11979 return depth;
11980 } else {
11981 var src = _p.source;
11982 var tgt = _p.target;
11983 var srcDepth = src.zDepth();
11984 var tgtDepth = tgt.zDepth();
11985 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11986 }
11987 }
11988 };
11989 elesfn$p.each = elesfn$p.forEach;
11990
11991 var defineSymbolIterator = function defineSymbolIterator() {
11992 var typeofUndef = "undefined" ;
11993 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11994
11995 if (isIteratorSupported) {
11996 elesfn$p[Symbol.iterator] = function () {
11997 var _this = this;
11998
11999 // eslint-disable-line no-undef
12000 var entry = {
12001 value: undefined,
12002 done: false
12003 };
12004 var i = 0;
12005 var length = this.length;
12006 return _defineProperty({
12007 next: function next() {
12008 if (i < length) {
12009 entry.value = _this[i++];
12010 } else {
12011 entry.value = undefined;
12012 entry.done = true;
12013 }
12014
12015 return entry;
12016 }
12017 }, Symbol.iterator, function () {
12018 // eslint-disable-line no-undef
12019 return this;
12020 });
12021 };
12022 }
12023 };
12024
12025 defineSymbolIterator();
12026
12027 var getLayoutDimensionOptions = defaults({
12028 nodeDimensionsIncludeLabels: false
12029 });
12030 var elesfn$q = {
12031 // Calculates and returns node dimensions { x, y } based on options given
12032 layoutDimensions: function layoutDimensions(options) {
12033 options = getLayoutDimensionOptions(options);
12034 var dims;
12035
12036 if (!this.takesUpSpace()) {
12037 dims = {
12038 w: 0,
12039 h: 0
12040 };
12041 } else if (options.nodeDimensionsIncludeLabels) {
12042 var bbDim = this.boundingBox();
12043 dims = {
12044 w: bbDim.w,
12045 h: bbDim.h
12046 };
12047 } else {
12048 dims = {
12049 w: this.outerWidth(),
12050 h: this.outerHeight()
12051 };
12052 } // sanitise the dimensions for external layouts (avoid division by zero)
12053
12054
12055 if (dims.w === 0 || dims.h === 0) {
12056 dims.w = dims.h = 1;
12057 }
12058
12059 return dims;
12060 },
12061 // using standard layout options, apply position function (w/ or w/o animation)
12062 layoutPositions: function layoutPositions(layout, options, fn) {
12063 var nodes = this.nodes().filter(function (n) {
12064 return !n.isParent();
12065 });
12066 var cy = this.cy();
12067 var layoutEles = options.eles; // nodes & edges
12068
12069 var getMemoizeKey = function getMemoizeKey(node) {
12070 return node.id();
12071 };
12072
12073 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
12074
12075 layout.emit({
12076 type: 'layoutstart',
12077 layout: layout
12078 });
12079 layout.animations = [];
12080
12081 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
12082 var center = {
12083 x: nodesBb.x1 + nodesBb.w / 2,
12084 y: nodesBb.y1 + nodesBb.h / 2
12085 };
12086 var spacingVector = {
12087 // scale from center of bounding box (not necessarily 0,0)
12088 x: (pos.x - center.x) * spacing,
12089 y: (pos.y - center.y) * spacing
12090 };
12091 return {
12092 x: center.x + spacingVector.x,
12093 y: center.y + spacingVector.y
12094 };
12095 };
12096
12097 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
12098
12099 var spacingBb = function spacingBb() {
12100 if (!useSpacingFactor) {
12101 return null;
12102 }
12103
12104 var bb = makeBoundingBox();
12105
12106 for (var i = 0; i < nodes.length; i++) {
12107 var node = nodes[i];
12108 var pos = fnMem(node, i);
12109 expandBoundingBoxByPoint(bb, pos.x, pos.y);
12110 }
12111
12112 return bb;
12113 };
12114
12115 var bb = spacingBb();
12116 var getFinalPos = memoize(function (node, i) {
12117 var newPos = fnMem(node, i);
12118
12119 if (useSpacingFactor) {
12120 var spacing = Math.abs(options.spacingFactor);
12121 newPos = calculateSpacing(spacing, bb, newPos);
12122 }
12123
12124 if (options.transform != null) {
12125 newPos = options.transform(node, newPos);
12126 }
12127
12128 return newPos;
12129 }, getMemoizeKey);
12130
12131 if (options.animate) {
12132 for (var i = 0; i < nodes.length; i++) {
12133 var node = nodes[i];
12134 var newPos = getFinalPos(node, i);
12135 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
12136
12137 if (animateNode) {
12138 var ani = node.animation({
12139 position: newPos,
12140 duration: options.animationDuration,
12141 easing: options.animationEasing
12142 });
12143 layout.animations.push(ani);
12144 } else {
12145 node.position(newPos);
12146 }
12147 }
12148
12149 if (options.fit) {
12150 var fitAni = cy.animation({
12151 fit: {
12152 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
12153 padding: options.padding
12154 },
12155 duration: options.animationDuration,
12156 easing: options.animationEasing
12157 });
12158 layout.animations.push(fitAni);
12159 } else if (options.zoom !== undefined && options.pan !== undefined) {
12160 var zoomPanAni = cy.animation({
12161 zoom: options.zoom,
12162 pan: options.pan,
12163 duration: options.animationDuration,
12164 easing: options.animationEasing
12165 });
12166 layout.animations.push(zoomPanAni);
12167 }
12168
12169 layout.animations.forEach(function (ani) {
12170 return ani.play();
12171 });
12172 layout.one('layoutready', options.ready);
12173 layout.emit({
12174 type: 'layoutready',
12175 layout: layout
12176 });
12177 Promise$1.all(layout.animations.map(function (ani) {
12178 return ani.promise();
12179 })).then(function () {
12180 layout.one('layoutstop', options.stop);
12181 layout.emit({
12182 type: 'layoutstop',
12183 layout: layout
12184 });
12185 });
12186 } else {
12187 nodes.positions(getFinalPos);
12188
12189 if (options.fit) {
12190 cy.fit(options.eles, options.padding);
12191 }
12192
12193 if (options.zoom != null) {
12194 cy.zoom(options.zoom);
12195 }
12196
12197 if (options.pan) {
12198 cy.pan(options.pan);
12199 }
12200
12201 layout.one('layoutready', options.ready);
12202 layout.emit({
12203 type: 'layoutready',
12204 layout: layout
12205 });
12206 layout.one('layoutstop', options.stop);
12207 layout.emit({
12208 type: 'layoutstop',
12209 layout: layout
12210 });
12211 }
12212
12213 return this; // chaining
12214 },
12215 layout: function layout(options) {
12216 var cy = this.cy();
12217 return cy.makeLayout(extend({}, options, {
12218 eles: this
12219 }));
12220 }
12221 }; // aliases:
12222
12223 elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
12224
12225 function styleCache(key, fn, ele) {
12226 var _p = ele._private;
12227 var cache = _p.styleCache = _p.styleCache || [];
12228 var val;
12229
12230 if ((val = cache[key]) != null) {
12231 return val;
12232 } else {
12233 val = cache[key] = fn(ele);
12234 return val;
12235 }
12236 }
12237
12238 function cacheStyleFunction(key, fn) {
12239 key = hashString(key);
12240 return function cachedStyleFunction(ele) {
12241 return styleCache(key, fn, ele);
12242 };
12243 }
12244
12245 function cachePrototypeStyleFunction(key, fn) {
12246 key = hashString(key);
12247
12248 var selfFn = function selfFn(ele) {
12249 return fn.call(ele);
12250 };
12251
12252 return function cachedPrototypeStyleFunction() {
12253 var ele = this[0];
12254
12255 if (ele) {
12256 return styleCache(key, selfFn, ele);
12257 }
12258 };
12259 }
12260
12261 var elesfn$r = {
12262 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
12263 var cy = this.cy();
12264 var renderer = cy.renderer();
12265 var styleEnabled = cy.styleEnabled();
12266
12267 if (renderer && styleEnabled) {
12268 renderer.recalculateRenderedStyle(this, useCache);
12269 }
12270
12271 return this;
12272 },
12273 dirtyStyleCache: function dirtyStyleCache() {
12274 var cy = this.cy();
12275
12276 var dirty = function dirty(ele) {
12277 return ele._private.styleCache = null;
12278 };
12279
12280 if (cy.hasCompoundNodes()) {
12281 var eles;
12282 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12283 eles.merge(eles.connectedEdges());
12284 eles.forEach(dirty);
12285 } else {
12286 this.forEach(function (ele) {
12287 dirty(ele);
12288 ele.connectedEdges().forEach(dirty);
12289 });
12290 }
12291
12292 return this;
12293 },
12294 // fully updates (recalculates) the style for the elements
12295 updateStyle: function updateStyle(notifyRenderer) {
12296 var cy = this._private.cy;
12297
12298 if (!cy.styleEnabled()) {
12299 return this;
12300 }
12301
12302 if (cy.batching()) {
12303 var bEles = cy._private.batchStyleEles;
12304 bEles.merge(this);
12305 return this; // chaining and exit early when batching
12306 }
12307
12308 var hasCompounds = cy.hasCompoundNodes();
12309 var updatedEles = this;
12310 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
12311
12312 if (hasCompounds) {
12313 // then add everything up and down for compound selector checks
12314 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12315 } // let changedEles = style.apply( updatedEles );
12316
12317
12318 var changedEles = updatedEles;
12319
12320 if (notifyRenderer) {
12321 changedEles.emitAndNotify('style'); // let renderer know we changed style
12322 } else {
12323 changedEles.emit('style'); // just fire the event
12324 }
12325
12326 updatedEles.forEach(function (ele) {
12327 return ele._private.styleDirty = true;
12328 });
12329 return this; // chaining
12330 },
12331 // private: clears dirty flag and recalculates style
12332 cleanStyle: function cleanStyle() {
12333 var cy = this.cy();
12334
12335 if (!cy.styleEnabled()) {
12336 return;
12337 }
12338
12339 for (var i = 0; i < this.length; i++) {
12340 var ele = this[i];
12341
12342 if (ele._private.styleDirty) {
12343 // n.b. this flag should be set before apply() to avoid potential infinite recursion
12344 ele._private.styleDirty = false;
12345 cy.style().apply(ele);
12346 }
12347 }
12348 },
12349 // get the internal parsed style object for the specified property
12350 parsedStyle: function parsedStyle(property) {
12351 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12352 var ele = this[0];
12353 var cy = ele.cy();
12354
12355 if (!cy.styleEnabled()) {
12356 return;
12357 }
12358
12359 if (ele) {
12360 this.cleanStyle();
12361 var overriddenStyle = ele._private.style[property];
12362
12363 if (overriddenStyle != null) {
12364 return overriddenStyle;
12365 } else if (includeNonDefault) {
12366 return cy.style().getDefaultProperty(property);
12367 } else {
12368 return null;
12369 }
12370 }
12371 },
12372 numericStyle: function numericStyle(property) {
12373 var ele = this[0];
12374
12375 if (!ele.cy().styleEnabled()) {
12376 return;
12377 }
12378
12379 if (ele) {
12380 var pstyle = ele.pstyle(property);
12381 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
12382 }
12383 },
12384 numericStyleUnits: function numericStyleUnits(property) {
12385 var ele = this[0];
12386
12387 if (!ele.cy().styleEnabled()) {
12388 return;
12389 }
12390
12391 if (ele) {
12392 return ele.pstyle(property).units;
12393 }
12394 },
12395 // get the specified css property as a rendered value (i.e. on-screen value)
12396 // or get the whole rendered style if no property specified (NB doesn't allow setting)
12397 renderedStyle: function renderedStyle(property) {
12398 var cy = this.cy();
12399
12400 if (!cy.styleEnabled()) {
12401 return this;
12402 }
12403
12404 var ele = this[0];
12405
12406 if (ele) {
12407 return cy.style().getRenderedStyle(ele, property);
12408 }
12409 },
12410 // read the calculated css style of the element or override the style (via a bypass)
12411 style: function style(name, value) {
12412 var cy = this.cy();
12413
12414 if (!cy.styleEnabled()) {
12415 return this;
12416 }
12417
12418 var updateTransitions = false;
12419 var style = cy.style();
12420
12421 if (plainObject(name)) {
12422 // then extend the bypass
12423 var props = name;
12424 style.applyBypass(this, props, updateTransitions);
12425 this.emitAndNotify('style'); // let the renderer know we've updated style
12426 } else if (string(name)) {
12427 if (value === undefined) {
12428 // then get the property from the style
12429 var ele = this[0];
12430
12431 if (ele) {
12432 return style.getStylePropertyValue(ele, name);
12433 } else {
12434 // empty collection => can't get any value
12435 return;
12436 }
12437 } else {
12438 // then set the bypass with the property value
12439 style.applyBypass(this, name, value, updateTransitions);
12440 this.emitAndNotify('style'); // let the renderer know we've updated style
12441 }
12442 } else if (name === undefined) {
12443 var _ele = this[0];
12444
12445 if (_ele) {
12446 return style.getRawStyle(_ele);
12447 } else {
12448 // empty collection => can't get any value
12449 return;
12450 }
12451 }
12452
12453 return this; // chaining
12454 },
12455 removeStyle: function removeStyle(names) {
12456 var cy = this.cy();
12457
12458 if (!cy.styleEnabled()) {
12459 return this;
12460 }
12461
12462 var updateTransitions = false;
12463 var style = cy.style();
12464 var eles = this;
12465
12466 if (names === undefined) {
12467 for (var i = 0; i < eles.length; i++) {
12468 var ele = eles[i];
12469 style.removeAllBypasses(ele, updateTransitions);
12470 }
12471 } else {
12472 names = names.split(/\s+/);
12473
12474 for (var _i = 0; _i < eles.length; _i++) {
12475 var _ele2 = eles[_i];
12476 style.removeBypasses(_ele2, names, updateTransitions);
12477 }
12478 }
12479
12480 this.emitAndNotify('style'); // let the renderer know we've updated style
12481
12482 return this; // chaining
12483 },
12484 show: function show() {
12485 this.css('display', 'element');
12486 return this; // chaining
12487 },
12488 hide: function hide() {
12489 this.css('display', 'none');
12490 return this; // chaining
12491 },
12492 effectiveOpacity: function effectiveOpacity() {
12493 var cy = this.cy();
12494
12495 if (!cy.styleEnabled()) {
12496 return 1;
12497 }
12498
12499 var hasCompoundNodes = cy.hasCompoundNodes();
12500 var ele = this[0];
12501
12502 if (ele) {
12503 var _p = ele._private;
12504 var parentOpacity = ele.pstyle('opacity').value;
12505
12506 if (!hasCompoundNodes) {
12507 return parentOpacity;
12508 }
12509
12510 var parents = !_p.data.parent ? null : ele.parents();
12511
12512 if (parents) {
12513 for (var i = 0; i < parents.length; i++) {
12514 var parent = parents[i];
12515 var opacity = parent.pstyle('opacity').value;
12516 parentOpacity = opacity * parentOpacity;
12517 }
12518 }
12519
12520 return parentOpacity;
12521 }
12522 },
12523 transparent: function transparent() {
12524 var cy = this.cy();
12525
12526 if (!cy.styleEnabled()) {
12527 return false;
12528 }
12529
12530 var ele = this[0];
12531 var hasCompoundNodes = ele.cy().hasCompoundNodes();
12532
12533 if (ele) {
12534 if (!hasCompoundNodes) {
12535 return ele.pstyle('opacity').value === 0;
12536 } else {
12537 return ele.effectiveOpacity() === 0;
12538 }
12539 }
12540 },
12541 backgrounding: function backgrounding() {
12542 var cy = this.cy();
12543
12544 if (!cy.styleEnabled()) {
12545 return false;
12546 }
12547
12548 var ele = this[0];
12549 return ele._private.backgrounding ? true : false;
12550 }
12551 };
12552
12553 function checkCompound(ele, parentOk) {
12554 var _p = ele._private;
12555 var parents = _p.data.parent ? ele.parents() : null;
12556
12557 if (parents) {
12558 for (var i = 0; i < parents.length; i++) {
12559 var parent = parents[i];
12560
12561 if (!parentOk(parent)) {
12562 return false;
12563 }
12564 }
12565 }
12566
12567 return true;
12568 }
12569
12570 function defineDerivedStateFunction(specs) {
12571 var ok = specs.ok;
12572 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
12573 var parentOk = specs.parentOk || specs.ok;
12574 return function () {
12575 var cy = this.cy();
12576
12577 if (!cy.styleEnabled()) {
12578 return true;
12579 }
12580
12581 var ele = this[0];
12582 var hasCompoundNodes = cy.hasCompoundNodes();
12583
12584 if (ele) {
12585 var _p = ele._private;
12586
12587 if (!ok(ele)) {
12588 return false;
12589 }
12590
12591 if (ele.isNode()) {
12592 return !hasCompoundNodes || checkCompound(ele, parentOk);
12593 } else {
12594 var src = _p.source;
12595 var tgt = _p.target;
12596 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
12597 }
12598 }
12599 };
12600 }
12601
12602 var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
12603 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
12604 });
12605 elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
12606 ok: eleTakesUpSpace
12607 }));
12608 var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
12609 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
12610 });
12611 var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
12612 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
12613 });
12614 elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
12615 ok: eleInteractive,
12616 parentOk: parentInteractive,
12617 edgeOkViaNode: eleTakesUpSpace
12618 }));
12619
12620 elesfn$r.noninteractive = function () {
12621 var ele = this[0];
12622
12623 if (ele) {
12624 return !ele.interactive();
12625 }
12626 };
12627
12628 var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
12629 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
12630 });
12631 var edgeVisibleViaNode = eleTakesUpSpace;
12632 elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
12633 ok: eleVisible,
12634 edgeOkViaNode: edgeVisibleViaNode
12635 }));
12636
12637 elesfn$r.hidden = function () {
12638 var ele = this[0];
12639
12640 if (ele) {
12641 return !ele.visible();
12642 }
12643 };
12644
12645 elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
12646 if (!this.cy().styleEnabled()) {
12647 return false;
12648 }
12649
12650 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
12651 });
12652 elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
12653 elesfn$r.renderedCss = elesfn$r.renderedStyle;
12654 elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
12655 elesfn$r.pstyle = elesfn$r.parsedStyle;
12656
12657 var elesfn$s = {};
12658
12659 function defineSwitchFunction(params) {
12660 return function () {
12661 var args = arguments;
12662 var changedEles = []; // e.g. cy.nodes().select( data, handler )
12663
12664 if (args.length === 2) {
12665 var data = args[0];
12666 var handler = args[1];
12667 this.on(params.event, data, handler);
12668 } // e.g. cy.nodes().select( handler )
12669 else if (args.length === 1 && fn(args[0])) {
12670 var _handler = args[0];
12671 this.on(params.event, _handler);
12672 } // e.g. cy.nodes().select()
12673 // e.g. (private) cy.nodes().select(['tapselect'])
12674 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12675 var addlEvents = args.length === 1 ? args[0] : null;
12676
12677 for (var i = 0; i < this.length; i++) {
12678 var ele = this[i];
12679 var able = !params.ableField || ele._private[params.ableField];
12680 var changed = ele._private[params.field] != params.value;
12681
12682 if (params.overrideAble) {
12683 var overrideAble = params.overrideAble(ele);
12684
12685 if (overrideAble !== undefined) {
12686 able = overrideAble;
12687
12688 if (!overrideAble) {
12689 return this;
12690 } // to save cycles assume not able for all on override
12691
12692 }
12693 }
12694
12695 if (able) {
12696 ele._private[params.field] = params.value;
12697
12698 if (changed) {
12699 changedEles.push(ele);
12700 }
12701 }
12702 }
12703
12704 var changedColl = this.spawn(changedEles);
12705 changedColl.updateStyle(); // change of state => possible change of style
12706
12707 changedColl.emit(params.event);
12708
12709 if (addlEvents) {
12710 changedColl.emit(addlEvents);
12711 }
12712 }
12713
12714 return this;
12715 };
12716 }
12717
12718 function defineSwitchSet(params) {
12719 elesfn$s[params.field] = function () {
12720 var ele = this[0];
12721
12722 if (ele) {
12723 if (params.overrideField) {
12724 var val = params.overrideField(ele);
12725
12726 if (val !== undefined) {
12727 return val;
12728 }
12729 }
12730
12731 return ele._private[params.field];
12732 }
12733 };
12734
12735 elesfn$s[params.on] = defineSwitchFunction({
12736 event: params.on,
12737 field: params.field,
12738 ableField: params.ableField,
12739 overrideAble: params.overrideAble,
12740 value: true
12741 });
12742 elesfn$s[params.off] = defineSwitchFunction({
12743 event: params.off,
12744 field: params.field,
12745 ableField: params.ableField,
12746 overrideAble: params.overrideAble,
12747 value: false
12748 });
12749 }
12750
12751 defineSwitchSet({
12752 field: 'locked',
12753 overrideField: function overrideField(ele) {
12754 return ele.cy().autolock() ? true : undefined;
12755 },
12756 on: 'lock',
12757 off: 'unlock'
12758 });
12759 defineSwitchSet({
12760 field: 'grabbable',
12761 overrideField: function overrideField(ele) {
12762 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12763 },
12764 on: 'grabify',
12765 off: 'ungrabify'
12766 });
12767 defineSwitchSet({
12768 field: 'selected',
12769 ableField: 'selectable',
12770 overrideAble: function overrideAble(ele) {
12771 return ele.cy().autounselectify() ? false : undefined;
12772 },
12773 on: 'select',
12774 off: 'unselect'
12775 });
12776 defineSwitchSet({
12777 field: 'selectable',
12778 overrideField: function overrideField(ele) {
12779 return ele.cy().autounselectify() ? false : undefined;
12780 },
12781 on: 'selectify',
12782 off: 'unselectify'
12783 });
12784 elesfn$s.deselect = elesfn$s.unselect;
12785
12786 elesfn$s.grabbed = function () {
12787 var ele = this[0];
12788
12789 if (ele) {
12790 return ele._private.grabbed;
12791 }
12792 };
12793
12794 defineSwitchSet({
12795 field: 'active',
12796 on: 'activate',
12797 off: 'unactivate'
12798 });
12799 defineSwitchSet({
12800 field: 'pannable',
12801 on: 'panify',
12802 off: 'unpanify'
12803 });
12804
12805 elesfn$s.inactive = function () {
12806 var ele = this[0];
12807
12808 if (ele) {
12809 return !ele._private.active;
12810 }
12811 };
12812
12813 var elesfn$t = {}; // DAG functions
12814 ////////////////
12815
12816 var defineDagExtremity = function defineDagExtremity(params) {
12817 return function dagExtremityImpl(selector) {
12818 var eles = this;
12819 var ret = [];
12820
12821 for (var i = 0; i < eles.length; i++) {
12822 var ele = eles[i];
12823
12824 if (!ele.isNode()) {
12825 continue;
12826 }
12827
12828 var disqualified = false;
12829 var edges = ele.connectedEdges();
12830
12831 for (var j = 0; j < edges.length; j++) {
12832 var edge = edges[j];
12833 var src = edge.source();
12834 var tgt = edge.target();
12835
12836 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12837 disqualified = true;
12838 break;
12839 }
12840 }
12841
12842 if (!disqualified) {
12843 ret.push(ele);
12844 }
12845 }
12846
12847 return this.spawn(ret, true).filter(selector);
12848 };
12849 };
12850
12851 var defineDagOneHop = function defineDagOneHop(params) {
12852 return function (selector) {
12853 var eles = this;
12854 var oEles = [];
12855
12856 for (var i = 0; i < eles.length; i++) {
12857 var ele = eles[i];
12858
12859 if (!ele.isNode()) {
12860 continue;
12861 }
12862
12863 var edges = ele.connectedEdges();
12864
12865 for (var j = 0; j < edges.length; j++) {
12866 var edge = edges[j];
12867 var src = edge.source();
12868 var tgt = edge.target();
12869
12870 if (params.outgoing && src === ele) {
12871 oEles.push(edge);
12872 oEles.push(tgt);
12873 } else if (params.incoming && tgt === ele) {
12874 oEles.push(edge);
12875 oEles.push(src);
12876 }
12877 }
12878 }
12879
12880 return this.spawn(oEles, true).filter(selector);
12881 };
12882 };
12883
12884 var defineDagAllHops = function defineDagAllHops(params) {
12885 return function (selector) {
12886 var eles = this;
12887 var sEles = [];
12888 var sElesIds = {};
12889
12890 for (;;) {
12891 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12892
12893 if (next.length === 0) {
12894 break;
12895 } // done if none left
12896
12897
12898 var newNext = false;
12899
12900 for (var i = 0; i < next.length; i++) {
12901 var n = next[i];
12902 var nid = n.id();
12903
12904 if (!sElesIds[nid]) {
12905 sElesIds[nid] = true;
12906 sEles.push(n);
12907 newNext = true;
12908 }
12909 }
12910
12911 if (!newNext) {
12912 break;
12913 } // done if touched all outgoers already
12914
12915
12916 eles = next;
12917 }
12918
12919 return this.spawn(sEles, true).filter(selector);
12920 };
12921 };
12922
12923 elesfn$t.clearTraversalCache = function () {
12924 for (var i = 0; i < this.length; i++) {
12925 this[i]._private.traversalCache = null;
12926 }
12927 };
12928
12929 extend(elesfn$t, {
12930 // get the root nodes in the DAG
12931 roots: defineDagExtremity({
12932 noIncomingEdges: true
12933 }),
12934 // get the leaf nodes in the DAG
12935 leaves: defineDagExtremity({
12936 noOutgoingEdges: true
12937 }),
12938 // normally called children in graph theory
12939 // these nodes =edges=> outgoing nodes
12940 outgoers: cache(defineDagOneHop({
12941 outgoing: true
12942 }), 'outgoers'),
12943 // aka DAG descendants
12944 successors: defineDagAllHops({
12945 outgoing: true
12946 }),
12947 // normally called parents in graph theory
12948 // these nodes <=edges= incoming nodes
12949 incomers: cache(defineDagOneHop({
12950 incoming: true
12951 }), 'incomers'),
12952 // aka DAG ancestors
12953 predecessors: defineDagAllHops({
12954 incoming: true
12955 })
12956 }); // Neighbourhood functions
12957 //////////////////////////
12958
12959 extend(elesfn$t, {
12960 neighborhood: cache(function (selector) {
12961 var elements = [];
12962 var nodes = this.nodes();
12963
12964 for (var i = 0; i < nodes.length; i++) {
12965 // for all nodes
12966 var node = nodes[i];
12967 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12968
12969 for (var j = 0; j < connectedEdges.length; j++) {
12970 var edge = connectedEdges[j];
12971 var src = edge.source();
12972 var tgt = edge.target();
12973 var otherNode = node === src ? tgt : src; // need check in case of loop
12974
12975 if (otherNode.length > 0) {
12976 elements.push(otherNode[0]); // add node 1 hop away
12977 } // add connected edge
12978
12979
12980 elements.push(edge[0]);
12981 }
12982 }
12983
12984 return this.spawn(elements, true).filter(selector);
12985 }, 'neighborhood'),
12986 closedNeighborhood: function closedNeighborhood(selector) {
12987 return this.neighborhood().add(this).filter(selector);
12988 },
12989 openNeighborhood: function openNeighborhood(selector) {
12990 return this.neighborhood(selector);
12991 }
12992 }); // aliases
12993
12994 elesfn$t.neighbourhood = elesfn$t.neighborhood;
12995 elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12996 elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12997 /////////////////
12998
12999 extend(elesfn$t, {
13000 source: cache(function sourceImpl(selector) {
13001 var ele = this[0];
13002 var src;
13003
13004 if (ele) {
13005 src = ele._private.source || ele.cy().collection();
13006 }
13007
13008 return src && selector ? src.filter(selector) : src;
13009 }, 'source'),
13010 target: cache(function targetImpl(selector) {
13011 var ele = this[0];
13012 var tgt;
13013
13014 if (ele) {
13015 tgt = ele._private.target || ele.cy().collection();
13016 }
13017
13018 return tgt && selector ? tgt.filter(selector) : tgt;
13019 }, 'target'),
13020 sources: defineSourceFunction({
13021 attr: 'source'
13022 }),
13023 targets: defineSourceFunction({
13024 attr: 'target'
13025 })
13026 });
13027
13028 function defineSourceFunction(params) {
13029 return function sourceImpl(selector) {
13030 var sources = [];
13031
13032 for (var i = 0; i < this.length; i++) {
13033 var ele = this[i];
13034 var src = ele._private[params.attr];
13035
13036 if (src) {
13037 sources.push(src);
13038 }
13039 }
13040
13041 return this.spawn(sources, true).filter(selector);
13042 };
13043 }
13044
13045 extend(elesfn$t, {
13046 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
13047 edgesTo: cache(defineEdgesWithFunction({
13048 thisIsSrc: true
13049 }), 'edgesTo')
13050 });
13051
13052 function defineEdgesWithFunction(params) {
13053 return function edgesWithImpl(otherNodes) {
13054 var elements = [];
13055 var cy = this._private.cy;
13056 var p = params || {}; // get elements if a selector is specified
13057
13058 if (string(otherNodes)) {
13059 otherNodes = cy.$(otherNodes);
13060 }
13061
13062 for (var h = 0; h < otherNodes.length; h++) {
13063 var edges = otherNodes[h]._private.edges;
13064
13065 for (var i = 0; i < edges.length; i++) {
13066 var edge = edges[i];
13067 var edgeData = edge._private.data;
13068 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
13069 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
13070 var edgeConnectsThisAndOther = thisToOther || otherToThis;
13071
13072 if (!edgeConnectsThisAndOther) {
13073 continue;
13074 }
13075
13076 if (p.thisIsSrc || p.thisIsTgt) {
13077 if (p.thisIsSrc && !thisToOther) {
13078 continue;
13079 }
13080
13081 if (p.thisIsTgt && !otherToThis) {
13082 continue;
13083 }
13084 }
13085
13086 elements.push(edge);
13087 }
13088 }
13089
13090 return this.spawn(elements, true);
13091 };
13092 }
13093
13094 extend(elesfn$t, {
13095 connectedEdges: cache(function (selector) {
13096 var retEles = [];
13097 var eles = this;
13098
13099 for (var i = 0; i < eles.length; i++) {
13100 var node = eles[i];
13101
13102 if (!node.isNode()) {
13103 continue;
13104 }
13105
13106 var edges = node._private.edges;
13107
13108 for (var j = 0; j < edges.length; j++) {
13109 var edge = edges[j];
13110 retEles.push(edge);
13111 }
13112 }
13113
13114 return this.spawn(retEles, true).filter(selector);
13115 }, 'connectedEdges'),
13116 connectedNodes: cache(function (selector) {
13117 var retEles = [];
13118 var eles = this;
13119
13120 for (var i = 0; i < eles.length; i++) {
13121 var edge = eles[i];
13122
13123 if (!edge.isEdge()) {
13124 continue;
13125 }
13126
13127 retEles.push(edge.source()[0]);
13128 retEles.push(edge.target()[0]);
13129 }
13130
13131 return this.spawn(retEles, true).filter(selector);
13132 }, 'connectedNodes'),
13133 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
13134 codirectedEdges: cache(defineParallelEdgesFunction({
13135 codirected: true
13136 }), 'codirectedEdges')
13137 });
13138
13139 function defineParallelEdgesFunction(params) {
13140 var defaults = {
13141 codirected: false
13142 };
13143 params = extend({}, defaults, params);
13144 return function parallelEdgesImpl(selector) {
13145 // micro-optimised for renderer
13146 var elements = [];
13147 var edges = this.edges();
13148 var p = params; // look at all the edges in the collection
13149
13150 for (var i = 0; i < edges.length; i++) {
13151 var edge1 = edges[i];
13152 var edge1_p = edge1._private;
13153 var src1 = edge1_p.source;
13154 var srcid1 = src1._private.data.id;
13155 var tgtid1 = edge1_p.data.target;
13156 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
13157
13158 for (var j = 0; j < srcEdges1.length; j++) {
13159 var edge2 = srcEdges1[j];
13160 var edge2data = edge2._private.data;
13161 var tgtid2 = edge2data.target;
13162 var srcid2 = edge2data.source;
13163 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
13164 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
13165
13166 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
13167 elements.push(edge2);
13168 }
13169 }
13170 }
13171
13172 return this.spawn(elements, true).filter(selector);
13173 };
13174 } // Misc functions
13175 /////////////////
13176
13177
13178 extend(elesfn$t, {
13179 components: function components(root) {
13180 var self = this;
13181 var cy = self.cy();
13182 var visited = cy.collection();
13183 var unvisited = root == null ? self.nodes() : root.nodes();
13184 var components = [];
13185
13186 if (root != null && unvisited.empty()) {
13187 // root may contain only edges
13188 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
13189 }
13190
13191 var visitInComponent = function visitInComponent(node, component) {
13192 visited.merge(node);
13193 unvisited.unmerge(node);
13194 component.merge(node);
13195 };
13196
13197 if (unvisited.empty()) {
13198 return self.spawn();
13199 }
13200
13201 var _loop = function _loop() {
13202 // each iteration yields a component
13203 var cmpt = cy.collection();
13204 components.push(cmpt);
13205 var root = unvisited[0];
13206 visitInComponent(root, cmpt);
13207 self.bfs({
13208 directed: false,
13209 roots: root,
13210 visit: function visit(v) {
13211 return visitInComponent(v, cmpt);
13212 }
13213 });
13214 cmpt.forEach(function (node) {
13215 node.connectedEdges().forEach(function (e) {
13216 // connectedEdges() usually cached
13217 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
13218 // has() is cheap
13219 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
13220 }
13221 });
13222 });
13223 };
13224
13225 do {
13226 _loop();
13227 } while (unvisited.length > 0);
13228
13229 return components;
13230 },
13231 component: function component() {
13232 var ele = this[0];
13233 return ele.cy().mutableElements().components(ele)[0];
13234 }
13235 });
13236 elesfn$t.componentsOf = elesfn$t.components;
13237
13238 var Collection = function Collection(cy, elements) {
13239 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13240
13241 if (cy === undefined) {
13242 error('A collection must have a reference to the core');
13243 return;
13244 }
13245
13246 var map = new Map$1();
13247 var createdElements = false;
13248
13249 if (!elements) {
13250 elements = [];
13251 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
13252 createdElements = true; // make elements from json and restore all at once later
13253
13254 var eles = [];
13255 var elesIds = new Set$1();
13256
13257 for (var i = 0, l = elements.length; i < l; i++) {
13258 var json = elements[i];
13259
13260 if (json.data == null) {
13261 json.data = {};
13262 }
13263
13264 var _data = json.data; // make sure newly created elements have valid ids
13265
13266 if (_data.id == null) {
13267 _data.id = uuid();
13268 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
13269 continue; // can't create element if prior id already exists
13270 }
13271
13272 var ele = new Element(cy, json, false);
13273 eles.push(ele);
13274 elesIds.add(_data.id);
13275 }
13276
13277 elements = eles;
13278 }
13279
13280 this.length = 0;
13281
13282 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
13283 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
13284
13285 if (element$1 == null) {
13286 continue;
13287 }
13288
13289 var id = element$1._private.data.id;
13290
13291 if (!unique || !map.has(id)) {
13292 if (unique) {
13293 map.set(id, {
13294 index: this.length,
13295 ele: element$1
13296 });
13297 }
13298
13299 this[this.length] = element$1;
13300 this.length++;
13301 }
13302 }
13303
13304 this._private = {
13305 eles: this,
13306 cy: cy,
13307
13308 get map() {
13309 if (this.lazyMap == null) {
13310 this.rebuildMap();
13311 }
13312
13313 return this.lazyMap;
13314 },
13315
13316 set map(m) {
13317 this.lazyMap = m;
13318 },
13319
13320 rebuildMap: function rebuildMap() {
13321 var m = this.lazyMap = new Map$1();
13322 var eles = this.eles;
13323
13324 for (var _i2 = 0; _i2 < eles.length; _i2++) {
13325 var _ele = eles[_i2];
13326 m.set(_ele.id(), {
13327 index: _i2,
13328 ele: _ele
13329 });
13330 }
13331 }
13332 };
13333
13334 if (unique) {
13335 this._private.map = map;
13336 } // restore the elements if we created them from json
13337
13338
13339 if (createdElements) {
13340 this.restore();
13341 }
13342 }; // Functions
13343 ////////////////////////////////////////////////////////////////////////////////////////////////////
13344 // keep the prototypes in sync (an element has the same functions as a collection)
13345 // and use elefn and elesfn as shorthands to the prototypes
13346
13347
13348 var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
13349
13350 elesfn$u.instanceString = function () {
13351 return 'collection';
13352 };
13353
13354 elesfn$u.spawn = function (eles, unique) {
13355 return new Collection(this.cy(), eles, unique);
13356 };
13357
13358 elesfn$u.spawnSelf = function () {
13359 return this.spawn(this);
13360 };
13361
13362 elesfn$u.cy = function () {
13363 return this._private.cy;
13364 };
13365
13366 elesfn$u.renderer = function () {
13367 return this._private.cy.renderer();
13368 };
13369
13370 elesfn$u.element = function () {
13371 return this[0];
13372 };
13373
13374 elesfn$u.collection = function () {
13375 if (collection(this)) {
13376 return this;
13377 } else {
13378 // an element
13379 return new Collection(this._private.cy, [this]);
13380 }
13381 };
13382
13383 elesfn$u.unique = function () {
13384 return new Collection(this._private.cy, this, true);
13385 };
13386
13387 elesfn$u.hasElementWithId = function (id) {
13388 id = '' + id; // id must be string
13389
13390 return this._private.map.has(id);
13391 };
13392
13393 elesfn$u.getElementById = function (id) {
13394 id = '' + id; // id must be string
13395
13396 var cy = this._private.cy;
13397
13398 var entry = this._private.map.get(id);
13399
13400 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
13401 };
13402
13403 elesfn$u.$id = elesfn$u.getElementById;
13404
13405 elesfn$u.poolIndex = function () {
13406 var cy = this._private.cy;
13407 var eles = cy._private.elements;
13408 var id = this[0]._private.data.id;
13409 return eles._private.map.get(id).index;
13410 };
13411
13412 elesfn$u.indexOf = function (ele) {
13413 var id = ele[0]._private.data.id;
13414 return this._private.map.get(id).index;
13415 };
13416
13417 elesfn$u.indexOfId = function (id) {
13418 id = '' + id; // id must be string
13419
13420 return this._private.map.get(id).index;
13421 };
13422
13423 elesfn$u.json = function (obj) {
13424 var ele = this.element();
13425 var cy = this.cy();
13426
13427 if (ele == null && obj) {
13428 return this;
13429 } // can't set to no eles
13430
13431
13432 if (ele == null) {
13433 return undefined;
13434 } // can't get from no eles
13435
13436
13437 var p = ele._private;
13438
13439 if (plainObject(obj)) {
13440 // set
13441 cy.startBatch();
13442
13443 if (obj.data) {
13444 ele.data(obj.data);
13445 var _data2 = p.data;
13446
13447 if (ele.isEdge()) {
13448 // source and target are immutable via data()
13449 var move = false;
13450 var spec = {};
13451 var src = obj.data.source;
13452 var tgt = obj.data.target;
13453
13454 if (src != null && src != _data2.source) {
13455 spec.source = '' + src; // id must be string
13456
13457 move = true;
13458 }
13459
13460 if (tgt != null && tgt != _data2.target) {
13461 spec.target = '' + tgt; // id must be string
13462
13463 move = true;
13464 }
13465
13466 if (move) {
13467 ele = ele.move(spec);
13468 }
13469 } else {
13470 // parent is immutable via data()
13471 var newParentValSpecd = 'parent' in obj.data;
13472 var parent = obj.data.parent;
13473
13474 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
13475 if (parent === undefined) {
13476 // can't set undefined imperatively, so use null
13477 parent = null;
13478 }
13479
13480 if (parent != null) {
13481 parent = '' + parent; // id must be string
13482 }
13483
13484 ele = ele.move({
13485 parent: parent
13486 });
13487 }
13488 }
13489 }
13490
13491 if (obj.position) {
13492 ele.position(obj.position);
13493 } // ignore group -- immutable
13494
13495
13496 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
13497 var obj_k = obj[k];
13498
13499 if (obj_k != null && obj_k !== p[k]) {
13500 if (obj_k) {
13501 ele[trueFnName]();
13502 } else {
13503 ele[falseFnName]();
13504 }
13505 }
13506 };
13507
13508 checkSwitch('removed', 'remove', 'restore');
13509 checkSwitch('selected', 'select', 'unselect');
13510 checkSwitch('selectable', 'selectify', 'unselectify');
13511 checkSwitch('locked', 'lock', 'unlock');
13512 checkSwitch('grabbable', 'grabify', 'ungrabify');
13513 checkSwitch('pannable', 'panify', 'unpanify');
13514
13515 if (obj.classes != null) {
13516 ele.classes(obj.classes);
13517 }
13518
13519 cy.endBatch();
13520 return this;
13521 } else if (obj === undefined) {
13522 // get
13523 var json = {
13524 data: copy(p.data),
13525 position: copy(p.position),
13526 group: p.group,
13527 removed: p.removed,
13528 selected: p.selected,
13529 selectable: p.selectable,
13530 locked: p.locked,
13531 grabbable: p.grabbable,
13532 pannable: p.pannable,
13533 classes: null
13534 };
13535 json.classes = '';
13536 var i = 0;
13537 p.classes.forEach(function (cls) {
13538 return json.classes += i++ === 0 ? cls : ' ' + cls;
13539 });
13540 return json;
13541 }
13542 };
13543
13544 elesfn$u.jsons = function () {
13545 var jsons = [];
13546
13547 for (var i = 0; i < this.length; i++) {
13548 var ele = this[i];
13549 var json = ele.json();
13550 jsons.push(json);
13551 }
13552
13553 return jsons;
13554 };
13555
13556 elesfn$u.clone = function () {
13557 var cy = this.cy();
13558 var elesArr = [];
13559
13560 for (var i = 0; i < this.length; i++) {
13561 var ele = this[i];
13562 var json = ele.json();
13563 var clone = new Element(cy, json, false); // NB no restore
13564
13565 elesArr.push(clone);
13566 }
13567
13568 return new Collection(cy, elesArr);
13569 };
13570
13571 elesfn$u.copy = elesfn$u.clone;
13572
13573 elesfn$u.restore = function () {
13574 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13575 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13576 var self = this;
13577 var cy = self.cy();
13578 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
13579 // restore the nodes first
13580
13581 var nodes = [];
13582 var edges = [];
13583 var elements;
13584
13585 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
13586 var ele = self[_i3];
13587
13588 if (addToPool && !ele.removed()) {
13589 // don't need to handle this ele
13590 continue;
13591 } // keep nodes first in the array and edges after
13592
13593
13594 if (ele.isNode()) {
13595 // put to front of array if node
13596 nodes.push(ele);
13597 } else {
13598 // put to end of array if edge
13599 edges.push(ele);
13600 }
13601 }
13602
13603 elements = nodes.concat(edges);
13604 var i;
13605
13606 var removeFromElements = function removeFromElements() {
13607 elements.splice(i, 1);
13608 i--;
13609 }; // now, restore each element
13610
13611
13612 for (i = 0; i < elements.length; i++) {
13613 var _ele2 = elements[i];
13614 var _private = _ele2._private;
13615 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
13616
13617 _ele2.clearTraversalCache(); // set id and validate
13618
13619
13620 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
13621 _data3.id = uuid();
13622 } else if (number(_data3.id)) {
13623 _data3.id = '' + _data3.id; // now it's a string
13624 } else if (emptyString(_data3.id) || !string(_data3.id)) {
13625 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
13626
13627 removeFromElements();
13628 continue;
13629 } else if (cy.hasElementWithId(_data3.id)) {
13630 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
13631
13632 removeFromElements();
13633 continue;
13634 }
13635
13636 var id = _data3.id; // id is finalised, now let's keep a ref
13637
13638 if (_ele2.isNode()) {
13639 // extra checks for nodes
13640 var pos = _private.position; // make sure the nodes have a defined position
13641
13642 if (pos.x == null) {
13643 pos.x = 0;
13644 }
13645
13646 if (pos.y == null) {
13647 pos.y = 0;
13648 }
13649 }
13650
13651 if (_ele2.isEdge()) {
13652 // extra checks for edges
13653 var edge = _ele2;
13654 var fields = ['source', 'target'];
13655 var fieldsLength = fields.length;
13656 var badSourceOrTarget = false;
13657
13658 for (var j = 0; j < fieldsLength; j++) {
13659 var field = fields[j];
13660 var val = _data3[field];
13661
13662 if (number(val)) {
13663 val = _data3[field] = '' + _data3[field]; // now string
13664 }
13665
13666 if (val == null || val === '') {
13667 // can't create if source or target is not defined properly
13668 error('Can not create edge `' + id + '` with unspecified ' + field);
13669 badSourceOrTarget = true;
13670 } else if (!cy.hasElementWithId(val)) {
13671 // can't create edge if one of its nodes doesn't exist
13672 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13673 badSourceOrTarget = true;
13674 }
13675 }
13676
13677 if (badSourceOrTarget) {
13678 removeFromElements();
13679 continue;
13680 } // can't create this
13681
13682
13683 var src = cy.getElementById(_data3.source);
13684 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
13685
13686 if (src.same(tgt)) {
13687 src._private.edges.push(edge);
13688 } else {
13689 src._private.edges.push(edge);
13690
13691 tgt._private.edges.push(edge);
13692 }
13693
13694 edge._private.source = src;
13695 edge._private.target = tgt;
13696 } // if is edge
13697 // create mock ids / indexes maps for element so it can be used like collections
13698
13699
13700 _private.map = new Map$1();
13701
13702 _private.map.set(id, {
13703 ele: _ele2,
13704 index: 0
13705 });
13706
13707 _private.removed = false;
13708
13709 if (addToPool) {
13710 cy.addToPool(_ele2);
13711 }
13712 } // for each element
13713 // do compound node sanity checks
13714
13715
13716 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13717 // each node
13718 var node = nodes[_i4];
13719 var _data4 = node._private.data;
13720
13721 if (number(_data4.parent)) {
13722 // then automake string
13723 _data4.parent = '' + _data4.parent;
13724 }
13725
13726 var parentId = _data4.parent;
13727 var specifiedParent = parentId != null;
13728
13729 if (specifiedParent) {
13730 var parent = cy.getElementById(parentId);
13731
13732 if (parent.empty()) {
13733 // non-existant parent; just remove it
13734 _data4.parent = undefined;
13735 } else {
13736 var selfAsParent = false;
13737 var ancestor = parent;
13738
13739 while (!ancestor.empty()) {
13740 if (node.same(ancestor)) {
13741 // mark self as parent and remove from data
13742 selfAsParent = true;
13743 _data4.parent = undefined; // remove parent reference
13744 // exit or we loop forever
13745
13746 break;
13747 }
13748
13749 ancestor = ancestor.parent();
13750 }
13751
13752 if (!selfAsParent) {
13753 // connect with children
13754 parent[0]._private.children.push(node);
13755
13756 node._private.parent = parent[0]; // let the core know we have a compound graph
13757
13758 cy_p.hasCompoundNodes = true;
13759 }
13760 } // else
13761
13762 } // if specified parent
13763
13764 } // for each node
13765
13766
13767 if (elements.length > 0) {
13768 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13769
13770 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13771 var _ele3 = restored[_i5];
13772
13773 if (_ele3.isNode()) {
13774 continue;
13775 } // adding an edge invalidates the traversal caches for the parallel edges
13776
13777
13778 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13779
13780
13781 _ele3.source().clearTraversalCache();
13782
13783 _ele3.target().clearTraversalCache();
13784 }
13785
13786 var toUpdateStyle;
13787
13788 if (cy_p.hasCompoundNodes) {
13789 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13790 } else {
13791 toUpdateStyle = restored;
13792 }
13793
13794 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13795
13796 if (notifyRenderer) {
13797 restored.emitAndNotify('add');
13798 } else if (addToPool) {
13799 restored.emit('add');
13800 }
13801 }
13802
13803 return self; // chainability
13804 };
13805
13806 elesfn$u.removed = function () {
13807 var ele = this[0];
13808 return ele && ele._private.removed;
13809 };
13810
13811 elesfn$u.inside = function () {
13812 var ele = this[0];
13813 return ele && !ele._private.removed;
13814 };
13815
13816 elesfn$u.remove = function () {
13817 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13818 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13819 var self = this;
13820 var elesToRemove = [];
13821 var elesToRemoveIds = {};
13822 var cy = self._private.cy; // add connected edges
13823
13824 function addConnectedEdges(node) {
13825 var edges = node._private.edges;
13826
13827 for (var i = 0; i < edges.length; i++) {
13828 add(edges[i]);
13829 }
13830 } // add descendant nodes
13831
13832
13833 function addChildren(node) {
13834 var children = node._private.children;
13835
13836 for (var i = 0; i < children.length; i++) {
13837 add(children[i]);
13838 }
13839 }
13840
13841 function add(ele) {
13842 var alreadyAdded = elesToRemoveIds[ele.id()];
13843
13844 if (removeFromPool && ele.removed() || alreadyAdded) {
13845 return;
13846 } else {
13847 elesToRemoveIds[ele.id()] = true;
13848 }
13849
13850 if (ele.isNode()) {
13851 elesToRemove.push(ele); // nodes are removed last
13852
13853 addConnectedEdges(ele);
13854 addChildren(ele);
13855 } else {
13856 elesToRemove.unshift(ele); // edges are removed first
13857 }
13858 } // make the list of elements to remove
13859 // (may be removing more than specified due to connected edges etc)
13860
13861
13862 for (var i = 0, l = self.length; i < l; i++) {
13863 var ele = self[i];
13864 add(ele);
13865 }
13866
13867 function removeEdgeRef(node, edge) {
13868 var connectedEdges = node._private.edges;
13869 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13870
13871 node.clearTraversalCache();
13872 }
13873
13874 function removeParallelRef(pllEdge) {
13875 // removing an edge invalidates the traversal caches for the parallel edges
13876 pllEdge.clearTraversalCache();
13877 }
13878
13879 var alteredParents = [];
13880 alteredParents.ids = {};
13881
13882 function removeChildRef(parent, ele) {
13883 ele = ele[0];
13884 parent = parent[0];
13885 var children = parent._private.children;
13886 var pid = parent.id();
13887 removeFromArray(children, ele); // remove parent => child ref
13888
13889 ele._private.parent = null; // remove child => parent ref
13890
13891 if (!alteredParents.ids[pid]) {
13892 alteredParents.ids[pid] = true;
13893 alteredParents.push(parent);
13894 }
13895 }
13896
13897 self.dirtyCompoundBoundsCache();
13898
13899 if (removeFromPool) {
13900 cy.removeFromPool(elesToRemove); // remove from core pool
13901 }
13902
13903 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13904 var _ele4 = elesToRemove[_i6];
13905
13906 if (_ele4.isEdge()) {
13907 // remove references to this edge in its connected nodes
13908 var src = _ele4.source()[0];
13909
13910 var tgt = _ele4.target()[0];
13911
13912 removeEdgeRef(src, _ele4);
13913 removeEdgeRef(tgt, _ele4);
13914
13915 var pllEdges = _ele4.parallelEdges();
13916
13917 for (var j = 0; j < pllEdges.length; j++) {
13918 var pllEdge = pllEdges[j];
13919 removeParallelRef(pllEdge);
13920
13921 if (pllEdge.isBundledBezier()) {
13922 pllEdge.dirtyBoundingBoxCache();
13923 }
13924 }
13925 } else {
13926 // remove reference to parent
13927 var parent = _ele4.parent();
13928
13929 if (parent.length !== 0) {
13930 removeChildRef(parent, _ele4);
13931 }
13932 }
13933
13934 if (removeFromPool) {
13935 // mark as removed
13936 _ele4._private.removed = true;
13937 }
13938 } // check to see if we have a compound graph or not
13939
13940
13941 var elesStillInside = cy._private.elements;
13942 cy._private.hasCompoundNodes = false;
13943
13944 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13945 var _ele5 = elesStillInside[_i7];
13946
13947 if (_ele5.isParent()) {
13948 cy._private.hasCompoundNodes = true;
13949 break;
13950 }
13951 }
13952
13953 var removedElements = new Collection(this.cy(), elesToRemove);
13954
13955 if (removedElements.size() > 0) {
13956 // must manually notify since trigger won't do this automatically once removed
13957 if (notifyRenderer) {
13958 removedElements.emitAndNotify('remove');
13959 } else if (removeFromPool) {
13960 removedElements.emit('remove');
13961 }
13962 } // the parents who were modified by the removal need their style updated
13963
13964
13965 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13966 var _ele6 = alteredParents[_i8];
13967
13968 if (!removeFromPool || !_ele6.removed()) {
13969 _ele6.updateStyle();
13970 }
13971 }
13972
13973 return removedElements;
13974 };
13975
13976 elesfn$u.move = function (struct) {
13977 var cy = this._private.cy;
13978 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13979 // (our calls to remove/restore do not remove from the graph or make events)
13980
13981 var notifyRenderer = false;
13982 var modifyPool = false;
13983
13984 var toString = function toString(id) {
13985 return id == null ? id : '' + id;
13986 }; // id must be string
13987
13988
13989 if (struct.source !== undefined || struct.target !== undefined) {
13990 var srcId = toString(struct.source);
13991 var tgtId = toString(struct.target);
13992 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13993 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13994
13995 if (srcExists || tgtExists) {
13996 cy.batch(function () {
13997 // avoid duplicate style updates
13998 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13999
14000 eles.emitAndNotify('moveout');
14001
14002 for (var i = 0; i < eles.length; i++) {
14003 var ele = eles[i];
14004 var _data5 = ele._private.data;
14005
14006 if (ele.isEdge()) {
14007 if (srcExists) {
14008 _data5.source = srcId;
14009 }
14010
14011 if (tgtExists) {
14012 _data5.target = tgtId;
14013 }
14014 }
14015 }
14016
14017 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
14018 });
14019 eles.emitAndNotify('move');
14020 }
14021 } else if (struct.parent !== undefined) {
14022 // move node to new parent
14023 var parentId = toString(struct.parent);
14024 var parentExists = parentId === null || cy.hasElementWithId(parentId);
14025
14026 if (parentExists) {
14027 var pidToAssign = parentId === null ? undefined : parentId;
14028 cy.batch(function () {
14029 // avoid duplicate style updates
14030 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
14031
14032 updated.emitAndNotify('moveout');
14033
14034 for (var i = 0; i < eles.length; i++) {
14035 var ele = eles[i];
14036 var _data6 = ele._private.data;
14037
14038 if (ele.isNode()) {
14039 _data6.parent = pidToAssign;
14040 }
14041 }
14042
14043 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
14044 });
14045 eles.emitAndNotify('move');
14046 }
14047 }
14048
14049 return this;
14050 };
14051
14052 [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) {
14053 extend(elesfn$u, props);
14054 });
14055
14056 var corefn = {
14057 add: function add(opts) {
14058 var elements;
14059 var cy = this; // add the elements
14060
14061 if (elementOrCollection(opts)) {
14062 var eles = opts;
14063
14064 if (eles._private.cy === cy) {
14065 // same instance => just restore
14066 elements = eles.restore();
14067 } else {
14068 // otherwise, copy from json
14069 var jsons = [];
14070
14071 for (var i = 0; i < eles.length; i++) {
14072 var ele = eles[i];
14073 jsons.push(ele.json());
14074 }
14075
14076 elements = new Collection(cy, jsons);
14077 }
14078 } // specify an array of options
14079 else if (array(opts)) {
14080 var _jsons = opts;
14081 elements = new Collection(cy, _jsons);
14082 } // specify via opts.nodes and opts.edges
14083 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
14084 var elesByGroup = opts;
14085 var _jsons2 = [];
14086 var grs = ['nodes', 'edges'];
14087
14088 for (var _i = 0, il = grs.length; _i < il; _i++) {
14089 var group = grs[_i];
14090 var elesArray = elesByGroup[group];
14091
14092 if (array(elesArray)) {
14093 for (var j = 0, jl = elesArray.length; j < jl; j++) {
14094 var json = extend({
14095 group: group
14096 }, elesArray[j]);
14097
14098 _jsons2.push(json);
14099 }
14100 }
14101 }
14102
14103 elements = new Collection(cy, _jsons2);
14104 } // specify options for one element
14105 else {
14106 var _json = opts;
14107 elements = new Element(cy, _json).collection();
14108 }
14109
14110 return elements;
14111 },
14112 remove: function remove(collection) {
14113 if (elementOrCollection(collection)) ; else if (string(collection)) {
14114 var selector = collection;
14115 collection = this.$(selector);
14116 }
14117
14118 return collection.remove();
14119 }
14120 };
14121
14122 /* global Float32Array */
14123
14124 /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14125 function generateCubicBezier(mX1, mY1, mX2, mY2) {
14126 var NEWTON_ITERATIONS = 4,
14127 NEWTON_MIN_SLOPE = 0.001,
14128 SUBDIVISION_PRECISION = 0.0000001,
14129 SUBDIVISION_MAX_ITERATIONS = 10,
14130 kSplineTableSize = 11,
14131 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
14132 float32ArraySupported = typeof Float32Array !== 'undefined';
14133 /* Must contain four arguments. */
14134
14135 if (arguments.length !== 4) {
14136 return false;
14137 }
14138 /* Arguments must be numbers. */
14139
14140
14141 for (var i = 0; i < 4; ++i) {
14142 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
14143 return false;
14144 }
14145 }
14146 /* X values must be in the [0, 1] range. */
14147
14148
14149 mX1 = Math.min(mX1, 1);
14150 mX2 = Math.min(mX2, 1);
14151 mX1 = Math.max(mX1, 0);
14152 mX2 = Math.max(mX2, 0);
14153 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
14154
14155 function A(aA1, aA2) {
14156 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
14157 }
14158
14159 function B(aA1, aA2) {
14160 return 3.0 * aA2 - 6.0 * aA1;
14161 }
14162
14163 function C(aA1) {
14164 return 3.0 * aA1;
14165 }
14166
14167 function calcBezier(aT, aA1, aA2) {
14168 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
14169 }
14170
14171 function getSlope(aT, aA1, aA2) {
14172 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
14173 }
14174
14175 function newtonRaphsonIterate(aX, aGuessT) {
14176 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
14177 var currentSlope = getSlope(aGuessT, mX1, mX2);
14178
14179 if (currentSlope === 0.0) {
14180 return aGuessT;
14181 }
14182
14183 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
14184 aGuessT -= currentX / currentSlope;
14185 }
14186
14187 return aGuessT;
14188 }
14189
14190 function calcSampleValues() {
14191 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
14192 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
14193 }
14194 }
14195
14196 function binarySubdivide(aX, aA, aB) {
14197 var currentX,
14198 currentT,
14199 i = 0;
14200
14201 do {
14202 currentT = aA + (aB - aA) / 2.0;
14203 currentX = calcBezier(currentT, mX1, mX2) - aX;
14204
14205 if (currentX > 0.0) {
14206 aB = currentT;
14207 } else {
14208 aA = currentT;
14209 }
14210 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
14211
14212 return currentT;
14213 }
14214
14215 function getTForX(aX) {
14216 var intervalStart = 0.0,
14217 currentSample = 1,
14218 lastSample = kSplineTableSize - 1;
14219
14220 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
14221 intervalStart += kSampleStepSize;
14222 }
14223
14224 --currentSample;
14225 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
14226 guessForT = intervalStart + dist * kSampleStepSize,
14227 initialSlope = getSlope(guessForT, mX1, mX2);
14228
14229 if (initialSlope >= NEWTON_MIN_SLOPE) {
14230 return newtonRaphsonIterate(aX, guessForT);
14231 } else if (initialSlope === 0.0) {
14232 return guessForT;
14233 } else {
14234 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
14235 }
14236 }
14237
14238 var _precomputed = false;
14239
14240 function precompute() {
14241 _precomputed = true;
14242
14243 if (mX1 !== mY1 || mX2 !== mY2) {
14244 calcSampleValues();
14245 }
14246 }
14247
14248 var f = function f(aX) {
14249 if (!_precomputed) {
14250 precompute();
14251 }
14252
14253 if (mX1 === mY1 && mX2 === mY2) {
14254 return aX;
14255 }
14256
14257 if (aX === 0) {
14258 return 0;
14259 }
14260
14261 if (aX === 1) {
14262 return 1;
14263 }
14264
14265 return calcBezier(getTForX(aX), mY1, mY2);
14266 };
14267
14268 f.getControlPoints = function () {
14269 return [{
14270 x: mX1,
14271 y: mY1
14272 }, {
14273 x: mX2,
14274 y: mY2
14275 }];
14276 };
14277
14278 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
14279
14280 f.toString = function () {
14281 return str;
14282 };
14283
14284 return f;
14285 }
14286
14287 /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14288
14289 /* 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
14290 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
14291 var generateSpringRK4 = function () {
14292 function springAccelerationForState(state) {
14293 return -state.tension * state.x - state.friction * state.v;
14294 }
14295
14296 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
14297 var state = {
14298 x: initialState.x + derivative.dx * dt,
14299 v: initialState.v + derivative.dv * dt,
14300 tension: initialState.tension,
14301 friction: initialState.friction
14302 };
14303 return {
14304 dx: state.v,
14305 dv: springAccelerationForState(state)
14306 };
14307 }
14308
14309 function springIntegrateState(state, dt) {
14310 var a = {
14311 dx: state.v,
14312 dv: springAccelerationForState(state)
14313 },
14314 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
14315 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
14316 d = springEvaluateStateWithDerivative(state, dt, c),
14317 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
14318 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
14319 state.x = state.x + dxdt * dt;
14320 state.v = state.v + dvdt * dt;
14321 return state;
14322 }
14323
14324 return function springRK4Factory(tension, friction, duration) {
14325 var initState = {
14326 x: -1,
14327 v: 0,
14328 tension: null,
14329 friction: null
14330 },
14331 path = [0],
14332 time_lapsed = 0,
14333 tolerance = 1 / 10000,
14334 DT = 16 / 1000,
14335 have_duration,
14336 dt,
14337 last_state;
14338 tension = parseFloat(tension) || 500;
14339 friction = parseFloat(friction) || 20;
14340 duration = duration || null;
14341 initState.tension = tension;
14342 initState.friction = friction;
14343 have_duration = duration !== null;
14344 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
14345
14346 if (have_duration) {
14347 /* Run the simulation without a duration. */
14348 time_lapsed = springRK4Factory(tension, friction);
14349 /* Compute the adjusted time delta. */
14350
14351 dt = time_lapsed / duration * DT;
14352 } else {
14353 dt = DT;
14354 }
14355
14356 for (;;) {
14357 /* Next/step function .*/
14358 last_state = springIntegrateState(last_state || initState, dt);
14359 /* Store the position. */
14360
14361 path.push(1 + last_state.x);
14362 time_lapsed += 16;
14363 /* If the change threshold is reached, break. */
14364
14365 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
14366 break;
14367 }
14368 }
14369 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
14370 computed path and returns a snapshot of the position according to a given percentComplete. */
14371
14372
14373 return !have_duration ? time_lapsed : function (percentComplete) {
14374 return path[percentComplete * (path.length - 1) | 0];
14375 };
14376 };
14377 }();
14378
14379 var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
14380 var bezier = generateCubicBezier(t1, p1, t2, p2);
14381 return function (start, end, percent) {
14382 return start + (end - start) * bezier(percent);
14383 };
14384 };
14385
14386 var easings = {
14387 'linear': function linear(start, end, percent) {
14388 return start + (end - start) * percent;
14389 },
14390 // default easings
14391 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
14392 'ease-in': cubicBezier(0.42, 0, 1, 1),
14393 'ease-out': cubicBezier(0, 0, 0.58, 1),
14394 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
14395 // sine
14396 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
14397 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
14398 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
14399 // quad
14400 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
14401 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
14402 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
14403 // cubic
14404 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
14405 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
14406 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
14407 // quart
14408 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
14409 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
14410 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
14411 // quint
14412 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
14413 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
14414 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
14415 // expo
14416 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
14417 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
14418 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
14419 // circ
14420 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
14421 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
14422 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
14423 // user param easings...
14424 'spring': function spring(tension, friction, duration) {
14425 if (duration === 0) {
14426 // can't get a spring w/ duration 0
14427 return easings.linear; // duration 0 => jump to end so impl doesn't matter
14428 }
14429
14430 var spring = generateSpringRK4(tension, friction, duration);
14431 return function (start, end, percent) {
14432 return start + (end - start) * spring(percent);
14433 };
14434 },
14435 'cubic-bezier': cubicBezier
14436 };
14437
14438 function getEasedValue(type, start, end, percent, easingFn) {
14439 if (percent === 1) {
14440 return end;
14441 }
14442
14443 if (start === end) {
14444 return end;
14445 }
14446
14447 var val = easingFn(start, end, percent);
14448
14449 if (type == null) {
14450 return val;
14451 }
14452
14453 if (type.roundValue || type.color) {
14454 val = Math.round(val);
14455 }
14456
14457 if (type.min !== undefined) {
14458 val = Math.max(val, type.min);
14459 }
14460
14461 if (type.max !== undefined) {
14462 val = Math.min(val, type.max);
14463 }
14464
14465 return val;
14466 }
14467
14468 function getValue(prop, spec) {
14469 if (prop.pfValue != null || prop.value != null) {
14470 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
14471 return prop.pfValue;
14472 } else {
14473 return prop.value;
14474 }
14475 } else {
14476 return prop;
14477 }
14478 }
14479
14480 function ease(startProp, endProp, percent, easingFn, propSpec) {
14481 var type = propSpec != null ? propSpec.type : null;
14482
14483 if (percent < 0) {
14484 percent = 0;
14485 } else if (percent > 1) {
14486 percent = 1;
14487 }
14488
14489 var start = getValue(startProp, propSpec);
14490 var end = getValue(endProp, propSpec);
14491
14492 if (number(start) && number(end)) {
14493 return getEasedValue(type, start, end, percent, easingFn);
14494 } else if (array(start) && array(end)) {
14495 var easedArr = [];
14496
14497 for (var i = 0; i < end.length; i++) {
14498 var si = start[i];
14499 var ei = end[i];
14500
14501 if (si != null && ei != null) {
14502 var val = getEasedValue(type, si, ei, percent, easingFn);
14503 easedArr.push(val);
14504 } else {
14505 easedArr.push(ei);
14506 }
14507 }
14508
14509 return easedArr;
14510 }
14511
14512 return undefined;
14513 }
14514
14515 function step(self, ani, now, isCore) {
14516 var isEles = !isCore;
14517 var _p = self._private;
14518 var ani_p = ani._private;
14519 var pEasing = ani_p.easing;
14520 var startTime = ani_p.startTime;
14521 var cy = isCore ? self : self.cy();
14522 var style = cy.style();
14523
14524 if (!ani_p.easingImpl) {
14525 if (pEasing == null) {
14526 // use default
14527 ani_p.easingImpl = easings['linear'];
14528 } else {
14529 // then define w/ name
14530 var easingVals;
14531
14532 if (string(pEasing)) {
14533 var easingProp = style.parse('transition-timing-function', pEasing);
14534 easingVals = easingProp.value;
14535 } else {
14536 // then assume preparsed array
14537 easingVals = pEasing;
14538 }
14539
14540 var name, args;
14541
14542 if (string(easingVals)) {
14543 name = easingVals;
14544 args = [];
14545 } else {
14546 name = easingVals[1];
14547 args = easingVals.slice(2).map(function (n) {
14548 return +n;
14549 });
14550 }
14551
14552 if (args.length > 0) {
14553 // create with args
14554 if (name === 'spring') {
14555 args.push(ani_p.duration); // need duration to generate spring
14556 }
14557
14558 ani_p.easingImpl = easings[name].apply(null, args);
14559 } else {
14560 // static impl by name
14561 ani_p.easingImpl = easings[name];
14562 }
14563 }
14564 }
14565
14566 var easing = ani_p.easingImpl;
14567 var percent;
14568
14569 if (ani_p.duration === 0) {
14570 percent = 1;
14571 } else {
14572 percent = (now - startTime) / ani_p.duration;
14573 }
14574
14575 if (ani_p.applying) {
14576 percent = ani_p.progress;
14577 }
14578
14579 if (percent < 0) {
14580 percent = 0;
14581 } else if (percent > 1) {
14582 percent = 1;
14583 }
14584
14585 if (ani_p.delay == null) {
14586 // then update
14587 var startPos = ani_p.startPosition;
14588 var endPos = ani_p.position;
14589
14590 if (endPos && isEles && !self.locked()) {
14591 var newPos = {};
14592
14593 if (valid(startPos.x, endPos.x)) {
14594 newPos.x = ease(startPos.x, endPos.x, percent, easing);
14595 }
14596
14597 if (valid(startPos.y, endPos.y)) {
14598 newPos.y = ease(startPos.y, endPos.y, percent, easing);
14599 }
14600
14601 self.position(newPos);
14602 }
14603
14604 var startPan = ani_p.startPan;
14605 var endPan = ani_p.pan;
14606 var pan = _p.pan;
14607 var animatingPan = endPan != null && isCore;
14608
14609 if (animatingPan) {
14610 if (valid(startPan.x, endPan.x)) {
14611 pan.x = ease(startPan.x, endPan.x, percent, easing);
14612 }
14613
14614 if (valid(startPan.y, endPan.y)) {
14615 pan.y = ease(startPan.y, endPan.y, percent, easing);
14616 }
14617
14618 self.emit('pan');
14619 }
14620
14621 var startZoom = ani_p.startZoom;
14622 var endZoom = ani_p.zoom;
14623 var animatingZoom = endZoom != null && isCore;
14624
14625 if (animatingZoom) {
14626 if (valid(startZoom, endZoom)) {
14627 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
14628 }
14629
14630 self.emit('zoom');
14631 }
14632
14633 if (animatingPan || animatingZoom) {
14634 self.emit('viewport');
14635 }
14636
14637 var props = ani_p.style;
14638
14639 if (props && props.length > 0 && isEles) {
14640 for (var i = 0; i < props.length; i++) {
14641 var prop = props[i];
14642 var _name = prop.name;
14643 var end = prop;
14644 var start = ani_p.startStyle[_name];
14645 var propSpec = style.properties[start.name];
14646 var easedVal = ease(start, end, percent, easing, propSpec);
14647 style.overrideBypass(self, _name, easedVal);
14648 } // for props
14649
14650
14651 self.emit('style');
14652 } // if
14653
14654 }
14655
14656 ani_p.progress = percent;
14657 return percent;
14658 }
14659
14660 function valid(start, end) {
14661 if (start == null || end == null) {
14662 return false;
14663 }
14664
14665 if (number(start) && number(end)) {
14666 return true;
14667 } else if (start && end) {
14668 return true;
14669 }
14670
14671 return false;
14672 }
14673
14674 function startAnimation(self, ani, now, isCore) {
14675 var ani_p = ani._private;
14676 ani_p.started = true;
14677 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14678 }
14679
14680 function stepAll(now, cy) {
14681 var eles = cy._private.aniEles;
14682 var doneEles = [];
14683
14684 function stepOne(ele, isCore) {
14685 var _p = ele._private;
14686 var current = _p.animation.current;
14687 var queue = _p.animation.queue;
14688 var ranAnis = false; // if nothing currently animating, get something from the queue
14689
14690 if (current.length === 0) {
14691 var next = queue.shift();
14692
14693 if (next) {
14694 current.push(next);
14695 }
14696 }
14697
14698 var callbacks = function callbacks(_callbacks) {
14699 for (var j = _callbacks.length - 1; j >= 0; j--) {
14700 var cb = _callbacks[j];
14701 cb();
14702 }
14703
14704 _callbacks.splice(0, _callbacks.length);
14705 }; // step and remove if done
14706
14707
14708 for (var i = current.length - 1; i >= 0; i--) {
14709 var ani = current[i];
14710 var ani_p = ani._private;
14711
14712 if (ani_p.stopped) {
14713 current.splice(i, 1);
14714 ani_p.hooked = false;
14715 ani_p.playing = false;
14716 ani_p.started = false;
14717 callbacks(ani_p.frames);
14718 continue;
14719 }
14720
14721 if (!ani_p.playing && !ani_p.applying) {
14722 continue;
14723 } // an apply() while playing shouldn't do anything
14724
14725
14726 if (ani_p.playing && ani_p.applying) {
14727 ani_p.applying = false;
14728 }
14729
14730 if (!ani_p.started) {
14731 startAnimation(ele, ani, now);
14732 }
14733
14734 step(ele, ani, now, isCore);
14735
14736 if (ani_p.applying) {
14737 ani_p.applying = false;
14738 }
14739
14740 callbacks(ani_p.frames);
14741
14742 if (ani_p.step != null) {
14743 ani_p.step(now);
14744 }
14745
14746 if (ani.completed()) {
14747 current.splice(i, 1);
14748 ani_p.hooked = false;
14749 ani_p.playing = false;
14750 ani_p.started = false;
14751 callbacks(ani_p.completes);
14752 }
14753
14754 ranAnis = true;
14755 }
14756
14757 if (!isCore && current.length === 0 && queue.length === 0) {
14758 doneEles.push(ele);
14759 }
14760
14761 return ranAnis;
14762 } // stepElement
14763 // handle all eles
14764
14765
14766 var ranEleAni = false;
14767
14768 for (var e = 0; e < eles.length; e++) {
14769 var ele = eles[e];
14770 var handledThisEle = stepOne(ele);
14771 ranEleAni = ranEleAni || handledThisEle;
14772 } // each element
14773
14774
14775 var ranCoreAni = stepOne(cy, true); // notify renderer
14776
14777 if (ranEleAni || ranCoreAni) {
14778 if (eles.length > 0) {
14779 cy.notify('draw', eles);
14780 } else {
14781 cy.notify('draw');
14782 }
14783 } // remove elements from list of currently animating if its queues are empty
14784
14785
14786 eles.unmerge(doneEles);
14787 cy.emit('step');
14788 } // stepAll
14789
14790 var corefn$1 = {
14791 // pull in animation functions
14792 animate: define$3.animate(),
14793 animation: define$3.animation(),
14794 animated: define$3.animated(),
14795 clearQueue: define$3.clearQueue(),
14796 delay: define$3.delay(),
14797 delayAnimation: define$3.delayAnimation(),
14798 stop: define$3.stop(),
14799 addToAnimationPool: function addToAnimationPool(eles) {
14800 var cy = this;
14801
14802 if (!cy.styleEnabled()) {
14803 return;
14804 } // save cycles when no style used
14805
14806
14807 cy._private.aniEles.merge(eles);
14808 },
14809 stopAnimationLoop: function stopAnimationLoop() {
14810 this._private.animationsRunning = false;
14811 },
14812 startAnimationLoop: function startAnimationLoop() {
14813 var cy = this;
14814 cy._private.animationsRunning = true;
14815
14816 if (!cy.styleEnabled()) {
14817 return;
14818 } // save cycles when no style used
14819 // NB the animation loop will exec in headless environments if style enabled
14820 // and explicit cy.destroy() is necessary to stop the loop
14821
14822
14823 function headlessStep() {
14824 if (!cy._private.animationsRunning) {
14825 return;
14826 }
14827
14828 requestAnimationFrame(function animationStep(now) {
14829 stepAll(now, cy);
14830 headlessStep();
14831 });
14832 }
14833
14834 var renderer = cy.renderer();
14835
14836 if (renderer && renderer.beforeRender) {
14837 // let the renderer schedule animations
14838 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14839 stepAll(now, cy);
14840 }, renderer.beforeRenderPriorities.animations);
14841 } else {
14842 // manage the animation loop ourselves
14843 headlessStep(); // first call
14844 }
14845 }
14846 };
14847
14848 var emitterOptions$1 = {
14849 qualifierCompare: function qualifierCompare(selector1, selector2) {
14850 if (selector1 == null || selector2 == null) {
14851 return selector1 == null && selector2 == null;
14852 } else {
14853 return selector1.sameText(selector2);
14854 }
14855 },
14856 eventMatches: function eventMatches(cy, listener, eventObj) {
14857 var selector = listener.qualifier;
14858
14859 if (selector != null) {
14860 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14861 }
14862
14863 return true;
14864 },
14865 addEventFields: function addEventFields(cy, evt) {
14866 evt.cy = cy;
14867 evt.target = cy;
14868 },
14869 callbackContext: function callbackContext(cy, listener, eventObj) {
14870 return listener.qualifier != null ? eventObj.target : cy;
14871 }
14872 };
14873
14874 var argSelector$1 = function argSelector(arg) {
14875 if (string(arg)) {
14876 return new Selector(arg);
14877 } else {
14878 return arg;
14879 }
14880 };
14881
14882 var elesfn$v = {
14883 createEmitter: function createEmitter() {
14884 var _p = this._private;
14885
14886 if (!_p.emitter) {
14887 _p.emitter = new Emitter(emitterOptions$1, this);
14888 }
14889
14890 return this;
14891 },
14892 emitter: function emitter() {
14893 return this._private.emitter;
14894 },
14895 on: function on(events, selector, callback) {
14896 this.emitter().on(events, argSelector$1(selector), callback);
14897 return this;
14898 },
14899 removeListener: function removeListener(events, selector, callback) {
14900 this.emitter().removeListener(events, argSelector$1(selector), callback);
14901 return this;
14902 },
14903 removeAllListeners: function removeAllListeners() {
14904 this.emitter().removeAllListeners();
14905 return this;
14906 },
14907 one: function one(events, selector, callback) {
14908 this.emitter().one(events, argSelector$1(selector), callback);
14909 return this;
14910 },
14911 once: function once(events, selector, callback) {
14912 this.emitter().one(events, argSelector$1(selector), callback);
14913 return this;
14914 },
14915 emit: function emit(events, extraParams) {
14916 this.emitter().emit(events, extraParams);
14917 return this;
14918 },
14919 emitAndNotify: function emitAndNotify(event, eles) {
14920 this.emit(event);
14921 this.notify(event, eles);
14922 return this;
14923 }
14924 };
14925 define$3.eventAliasesOn(elesfn$v);
14926
14927 var corefn$2 = {
14928 png: function png(options) {
14929 var renderer = this._private.renderer;
14930 options = options || {};
14931 return renderer.png(options);
14932 },
14933 jpg: function jpg(options) {
14934 var renderer = this._private.renderer;
14935 options = options || {};
14936 options.bg = options.bg || '#fff';
14937 return renderer.jpg(options);
14938 }
14939 };
14940 corefn$2.jpeg = corefn$2.jpg;
14941
14942 var corefn$3 = {
14943 layout: function layout(options) {
14944 var cy = this;
14945
14946 if (options == null) {
14947 error('Layout options must be specified to make a layout');
14948 return;
14949 }
14950
14951 if (options.name == null) {
14952 error('A `name` must be specified to make a layout');
14953 return;
14954 }
14955
14956 var name = options.name;
14957 var Layout = cy.extension('layout', name);
14958
14959 if (Layout == null) {
14960 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14961 return;
14962 }
14963
14964 var eles;
14965
14966 if (string(options.eles)) {
14967 eles = cy.$(options.eles);
14968 } else {
14969 eles = options.eles != null ? options.eles : cy.$();
14970 }
14971
14972 var layout = new Layout(extend({}, options, {
14973 cy: cy,
14974 eles: eles
14975 }));
14976 return layout;
14977 }
14978 };
14979 corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14980
14981 var corefn$4 = {
14982 notify: function notify(eventName, eventEles) {
14983 var _p = this._private;
14984
14985 if (this.batching()) {
14986 _p.batchNotifications = _p.batchNotifications || {};
14987 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14988
14989 if (eventEles != null) {
14990 eles.merge(eventEles);
14991 }
14992
14993 return; // notifications are disabled during batching
14994 }
14995
14996 if (!_p.notificationsEnabled) {
14997 return;
14998 } // exit on disabled
14999
15000
15001 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
15002
15003 if (this.destroyed() || !renderer) {
15004 return;
15005 }
15006
15007 renderer.notify(eventName, eventEles);
15008 },
15009 notifications: function notifications(bool) {
15010 var p = this._private;
15011
15012 if (bool === undefined) {
15013 return p.notificationsEnabled;
15014 } else {
15015 p.notificationsEnabled = bool ? true : false;
15016 }
15017
15018 return this;
15019 },
15020 noNotifications: function noNotifications(callback) {
15021 this.notifications(false);
15022 callback();
15023 this.notifications(true);
15024 },
15025 batching: function batching() {
15026 return this._private.batchCount > 0;
15027 },
15028 startBatch: function startBatch() {
15029 var _p = this._private;
15030
15031 if (_p.batchCount == null) {
15032 _p.batchCount = 0;
15033 }
15034
15035 if (_p.batchCount === 0) {
15036 _p.batchStyleEles = this.collection();
15037 _p.batchNotifications = {};
15038 }
15039
15040 _p.batchCount++;
15041 return this;
15042 },
15043 endBatch: function endBatch() {
15044 var _p = this._private;
15045
15046 if (_p.batchCount === 0) {
15047 return this;
15048 }
15049
15050 _p.batchCount--;
15051
15052 if (_p.batchCount === 0) {
15053 // update style for dirty eles
15054 _p.batchStyleEles.updateStyle();
15055
15056 var renderer = this.renderer(); // notify the renderer of queued eles and event types
15057
15058 Object.keys(_p.batchNotifications).forEach(function (eventName) {
15059 var eles = _p.batchNotifications[eventName];
15060
15061 if (eles.empty()) {
15062 renderer.notify(eventName);
15063 } else {
15064 renderer.notify(eventName, eles);
15065 }
15066 });
15067 }
15068
15069 return this;
15070 },
15071 batch: function batch(callback) {
15072 this.startBatch();
15073 callback();
15074 this.endBatch();
15075 return this;
15076 },
15077 // for backwards compatibility
15078 batchData: function batchData(map) {
15079 var cy = this;
15080 return this.batch(function () {
15081 var ids = Object.keys(map);
15082
15083 for (var i = 0; i < ids.length; i++) {
15084 var id = ids[i];
15085 var data = map[id];
15086 var ele = cy.getElementById(id);
15087 ele.data(data);
15088 }
15089 });
15090 }
15091 };
15092
15093 var rendererDefaults = defaults({
15094 hideEdgesOnViewport: false,
15095 textureOnViewport: false,
15096 motionBlur: false,
15097 motionBlurOpacity: 0.05,
15098 pixelRatio: undefined,
15099 desktopTapThreshold: 4,
15100 touchTapThreshold: 8,
15101 wheelSensitivity: 1,
15102 debug: false,
15103 showFps: false
15104 });
15105 var corefn$5 = {
15106 renderTo: function renderTo(context, zoom, pan, pxRatio) {
15107 var r = this._private.renderer;
15108 r.renderTo(context, zoom, pan, pxRatio);
15109 return this;
15110 },
15111 renderer: function renderer() {
15112 return this._private.renderer;
15113 },
15114 forceRender: function forceRender() {
15115 this.notify('draw');
15116 return this;
15117 },
15118 resize: function resize() {
15119 this.invalidateSize();
15120 this.emitAndNotify('resize');
15121 return this;
15122 },
15123 initRenderer: function initRenderer(options) {
15124 var cy = this;
15125 var RendererProto = cy.extension('renderer', options.name);
15126
15127 if (RendererProto == null) {
15128 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
15129 return;
15130 }
15131
15132 if (options.wheelSensitivity !== undefined) {
15133 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.");
15134 }
15135
15136 var rOpts = rendererDefaults(options);
15137 rOpts.cy = cy;
15138 cy._private.renderer = new RendererProto(rOpts);
15139 this.notify('init');
15140 },
15141 destroyRenderer: function destroyRenderer() {
15142 var cy = this;
15143 cy.notify('destroy'); // destroy the renderer
15144
15145 var domEle = cy.container();
15146
15147 if (domEle) {
15148 domEle._cyreg = null;
15149
15150 while (domEle.childNodes.length > 0) {
15151 domEle.removeChild(domEle.childNodes[0]);
15152 }
15153 }
15154
15155 cy._private.renderer = null; // to be extra safe, remove the ref
15156
15157 cy.mutableElements().forEach(function (ele) {
15158 var _p = ele._private;
15159 _p.rscratch = {};
15160 _p.rstyle = {};
15161 _p.animation.current = [];
15162 _p.animation.queue = [];
15163 });
15164 },
15165 onRender: function onRender(fn) {
15166 return this.on('render', fn);
15167 },
15168 offRender: function offRender(fn) {
15169 return this.off('render', fn);
15170 }
15171 };
15172 corefn$5.invalidateDimensions = corefn$5.resize;
15173
15174 var corefn$6 = {
15175 // get a collection
15176 // - empty collection on no args
15177 // - collection of elements in the graph on selector arg
15178 // - guarantee a returned collection when elements or collection specified
15179 collection: function collection(eles, opts) {
15180 if (string(eles)) {
15181 return this.$(eles);
15182 } else if (elementOrCollection(eles)) {
15183 return eles.collection();
15184 } else if (array(eles)) {
15185 return new Collection(this, eles, opts);
15186 }
15187
15188 return new Collection(this);
15189 },
15190 nodes: function nodes(selector) {
15191 var nodes = this.$(function (ele) {
15192 return ele.isNode();
15193 });
15194
15195 if (selector) {
15196 return nodes.filter(selector);
15197 }
15198
15199 return nodes;
15200 },
15201 edges: function edges(selector) {
15202 var edges = this.$(function (ele) {
15203 return ele.isEdge();
15204 });
15205
15206 if (selector) {
15207 return edges.filter(selector);
15208 }
15209
15210 return edges;
15211 },
15212 // search the graph like jQuery
15213 $: function $(selector) {
15214 var eles = this._private.elements;
15215
15216 if (selector) {
15217 return eles.filter(selector);
15218 } else {
15219 return eles.spawnSelf();
15220 }
15221 },
15222 mutableElements: function mutableElements() {
15223 return this._private.elements;
15224 }
15225 }; // aliases
15226
15227 corefn$6.elements = corefn$6.filter = corefn$6.$;
15228
15229 var styfn = {}; // keys for style blocks, e.g. ttfftt
15230
15231 var TRUE = 't';
15232 var FALSE = 'f'; // (potentially expensive calculation)
15233 // apply the style to the element based on
15234 // - its bypass
15235 // - what selectors match it
15236
15237 styfn.apply = function (eles) {
15238 var self = this;
15239 var _p = self._private;
15240 var cy = _p.cy;
15241 var updatedEles = cy.collection();
15242
15243 for (var ie = 0; ie < eles.length; ie++) {
15244 var ele = eles[ie];
15245 var cxtMeta = self.getContextMeta(ele);
15246
15247 if (cxtMeta.empty) {
15248 continue;
15249 }
15250
15251 var cxtStyle = self.getContextStyle(cxtMeta);
15252 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
15253
15254 if (ele._private.appliedInitStyle) {
15255 self.updateTransitions(ele, app.diffProps);
15256 } else {
15257 ele._private.appliedInitStyle = true;
15258 }
15259
15260 var hintsDiff = self.updateStyleHints(ele);
15261
15262 if (hintsDiff) {
15263 updatedEles.push(ele);
15264 }
15265 } // for elements
15266
15267
15268 return updatedEles;
15269 };
15270
15271 styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
15272 var self = this;
15273 var cache = self._private.propDiffs = self._private.propDiffs || {};
15274 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
15275 var cachedVal = cache[dualCxtKey];
15276
15277 if (cachedVal) {
15278 return cachedVal;
15279 }
15280
15281 var diffProps = [];
15282 var addedProp = {};
15283
15284 for (var i = 0; i < self.length; i++) {
15285 var cxt = self[i];
15286 var oldHasCxt = oldCxtKey[i] === TRUE;
15287 var newHasCxt = newCxtKey[i] === TRUE;
15288 var cxtHasDiffed = oldHasCxt !== newHasCxt;
15289 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
15290
15291 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
15292 var props = void 0;
15293
15294 if (cxtHasDiffed && cxtHasMappedProps) {
15295 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
15296 } else if (cxtHasDiffed) {
15297 props = cxt.properties; // need to check them all
15298 } else if (cxtHasMappedProps) {
15299 props = cxt.mappedProperties; // only need to check mapped
15300 }
15301
15302 for (var j = 0; j < props.length; j++) {
15303 var prop = props[j];
15304 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
15305 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
15306 // is cached)
15307
15308 var laterCxtOverrides = false;
15309
15310 for (var k = i + 1; k < self.length; k++) {
15311 var laterCxt = self[k];
15312 var hasLaterCxt = newCxtKey[k] === TRUE;
15313
15314 if (!hasLaterCxt) {
15315 continue;
15316 } // can't override unless the context is active
15317
15318
15319 laterCxtOverrides = laterCxt.properties[prop.name] != null;
15320
15321 if (laterCxtOverrides) {
15322 break;
15323 } // exit early as long as one later context overrides
15324
15325 }
15326
15327 if (!addedProp[name] && !laterCxtOverrides) {
15328 addedProp[name] = true;
15329 diffProps.push(name);
15330 }
15331 } // for props
15332
15333 } // if
15334
15335 } // for contexts
15336
15337
15338 cache[dualCxtKey] = diffProps;
15339 return diffProps;
15340 };
15341
15342 styfn.getContextMeta = function (ele) {
15343 var self = this;
15344 var cxtKey = '';
15345 var diffProps;
15346 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
15347
15348 for (var i = 0; i < self.length; i++) {
15349 var context = self[i];
15350 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
15351
15352 if (contextSelectorMatches) {
15353 cxtKey += TRUE;
15354 } else {
15355 cxtKey += FALSE;
15356 }
15357 } // for context
15358
15359
15360 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
15361 ele._private.styleCxtKey = cxtKey;
15362 return {
15363 key: cxtKey,
15364 diffPropNames: diffProps,
15365 empty: diffProps.length === 0
15366 };
15367 }; // gets a computed ele style object based on matched contexts
15368
15369
15370 styfn.getContextStyle = function (cxtMeta) {
15371 var cxtKey = cxtMeta.key;
15372 var self = this;
15373 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
15374
15375 if (cxtStyles[cxtKey]) {
15376 return cxtStyles[cxtKey];
15377 }
15378
15379 var style = {
15380 _private: {
15381 key: cxtKey
15382 }
15383 };
15384
15385 for (var i = 0; i < self.length; i++) {
15386 var cxt = self[i];
15387 var hasCxt = cxtKey[i] === TRUE;
15388
15389 if (!hasCxt) {
15390 continue;
15391 }
15392
15393 for (var j = 0; j < cxt.properties.length; j++) {
15394 var prop = cxt.properties[j];
15395 style[prop.name] = prop;
15396 }
15397 }
15398
15399 cxtStyles[cxtKey] = style;
15400 return style;
15401 };
15402
15403 styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
15404 var self = this;
15405 var diffProps = cxtMeta.diffPropNames;
15406 var retDiffProps = {};
15407 var types = self.types;
15408
15409 for (var i = 0; i < diffProps.length; i++) {
15410 var diffPropName = diffProps[i];
15411 var cxtProp = cxtStyle[diffPropName];
15412 var eleProp = ele.pstyle(diffPropName);
15413
15414 if (!cxtProp) {
15415 // no context prop means delete
15416 if (!eleProp) {
15417 continue; // no existing prop means nothing needs to be removed
15418 // nb affects initial application on mapped values like control-point-distances
15419 } else if (eleProp.bypass) {
15420 cxtProp = {
15421 name: diffPropName,
15422 deleteBypassed: true
15423 };
15424 } else {
15425 cxtProp = {
15426 name: diffPropName,
15427 "delete": true
15428 };
15429 }
15430 } // save cycles when the context prop doesn't need to be applied
15431
15432
15433 if (eleProp === cxtProp) {
15434 continue;
15435 } // save cycles when a mapped context prop doesn't need to be applied
15436
15437
15438 if (cxtProp.mapped === types.fn // context prop is function mapper
15439 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
15440 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
15441 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
15442 ) {
15443 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
15444 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
15445
15446 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
15447
15448 if (fnValue === mapping.prevFnValue) {
15449 continue;
15450 }
15451 }
15452
15453 var retDiffProp = retDiffProps[diffPropName] = {
15454 prev: eleProp
15455 };
15456 self.applyParsedProperty(ele, cxtProp);
15457 retDiffProp.next = ele.pstyle(diffPropName);
15458
15459 if (retDiffProp.next && retDiffProp.next.bypass) {
15460 retDiffProp.next = retDiffProp.next.bypassed;
15461 }
15462 }
15463
15464 return {
15465 diffProps: retDiffProps
15466 };
15467 };
15468
15469 styfn.updateStyleHints = function (ele) {
15470 var _p = ele._private;
15471 var self = this;
15472 var propNames = self.propertyGroupNames;
15473 var propGrKeys = self.propertyGroupKeys;
15474
15475 var propHash = function propHash(ele, propNames, seedKey) {
15476 return self.getPropertiesHash(ele, propNames, seedKey);
15477 };
15478
15479 var oldStyleKey = _p.styleKey;
15480
15481 if (ele.removed()) {
15482 return false;
15483 }
15484
15485 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
15486 // but lazily -- only use non-default prop values to reduce the number of hashes
15487 //
15488
15489 var overriddenStyles = ele._private.style;
15490 propNames = Object.keys(overriddenStyles);
15491
15492 for (var i = 0; i < propGrKeys.length; i++) {
15493 var grKey = propGrKeys[i];
15494 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15495 }
15496
15497 var updateGrKey1 = function updateGrKey1(val, grKey) {
15498 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
15499 };
15500
15501 var updateGrKey2 = function updateGrKey2(val, grKey) {
15502 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
15503 };
15504
15505 var updateGrKey = function updateGrKey(val, grKey) {
15506 updateGrKey1(val, grKey);
15507 updateGrKey2(val, grKey);
15508 };
15509
15510 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
15511 for (var j = 0; j < strVal.length; j++) {
15512 var ch = strVal.charCodeAt(j);
15513 updateGrKey1(ch, grKey);
15514 updateGrKey2(ch, grKey);
15515 }
15516 }; // - hashing works on 32 bit ints b/c we use bitwise ops
15517 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
15518 // - raise up small numbers so more significant digits are seen by hashing
15519 // - make small numbers larger than a normal value to avoid collisions
15520 // - works in practice and it's relatively cheap
15521
15522
15523 var N = 2000000000;
15524
15525 var cleanNum = function cleanNum(val) {
15526 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
15527 };
15528
15529 for (var _i = 0; _i < propNames.length; _i++) {
15530 var name = propNames[_i];
15531 var parsedProp = overriddenStyles[name];
15532
15533 if (parsedProp == null) {
15534 continue;
15535 }
15536
15537 var propInfo = this.properties[name];
15538 var type = propInfo.type;
15539 var _grKey = propInfo.groupKey;
15540 var normalizedNumberVal = void 0;
15541
15542 if (propInfo.hashOverride != null) {
15543 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
15544 } else if (parsedProp.pfValue != null) {
15545 normalizedNumberVal = parsedProp.pfValue;
15546 } // might not be a number if it allows enums
15547
15548
15549 var numberVal = propInfo.enums == null ? parsedProp.value : null;
15550 var haveNormNum = normalizedNumberVal != null;
15551 var haveUnitedNum = numberVal != null;
15552 var haveNum = haveNormNum || haveUnitedNum;
15553 var units = parsedProp.units; // numbers are cheaper to hash than strings
15554 // 1 hash op vs n hash ops (for length n string)
15555
15556 if (type.number && haveNum && !type.multiple) {
15557 var v = haveNormNum ? normalizedNumberVal : numberVal;
15558 updateGrKey(cleanNum(v), _grKey);
15559
15560 if (!haveNormNum && units != null) {
15561 updateGrKeyWStr(units, _grKey);
15562 }
15563 } else {
15564 updateGrKeyWStr(parsedProp.strValue, _grKey);
15565 }
15566 } // overall style key
15567 //
15568
15569
15570 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15571
15572 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
15573 var _grKey2 = propGrKeys[_i2];
15574 var grHash = _p.styleKeys[_grKey2];
15575 hash[0] = hashInt(grHash[0], hash[0]);
15576 hash[1] = hashIntAlt(grHash[1], hash[1]);
15577 }
15578
15579 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
15580 //
15581
15582 var sk = _p.styleKeys;
15583 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
15584 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
15585 _p.labelKey = combineHashesArray(labelKeys);
15586 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
15587
15588 if (!isNode) {
15589 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
15590 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
15591 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
15592 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
15593 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
15594 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
15595 } // node
15596 //
15597
15598
15599 if (isNode) {
15600 var _p$styleKeys = _p.styleKeys,
15601 nodeBody = _p$styleKeys.nodeBody,
15602 nodeBorder = _p$styleKeys.nodeBorder,
15603 backgroundImage = _p$styleKeys.backgroundImage,
15604 compound = _p$styleKeys.compound,
15605 pie = _p$styleKeys.pie;
15606 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
15607 return k != null;
15608 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
15609 _p.nodeKey = combineHashesArray(nodeKeys);
15610 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
15611 }
15612
15613 return oldStyleKey !== _p.styleKey;
15614 };
15615
15616 styfn.clearStyleHints = function (ele) {
15617 var _p = ele._private;
15618 _p.styleCxtKey = '';
15619 _p.styleKeys = {};
15620 _p.styleKey = null;
15621 _p.labelKey = null;
15622 _p.labelStyleKey = null;
15623 _p.sourceLabelKey = null;
15624 _p.sourceLabelStyleKey = null;
15625 _p.targetLabelKey = null;
15626 _p.targetLabelStyleKey = null;
15627 _p.nodeKey = null;
15628 _p.hasPie = null;
15629 }; // apply a property to the style (for internal use)
15630 // returns whether application was successful
15631 //
15632 // now, this function flattens the property, and here's how:
15633 //
15634 // for parsedProp:{ bypass: true, deleteBypass: true }
15635 // no property is generated, instead the bypass property in the
15636 // element's style is replaced by what's pointed to by the `bypassed`
15637 // field in the bypass property (i.e. restoring the property the
15638 // bypass was overriding)
15639 //
15640 // for parsedProp:{ mapped: truthy }
15641 // the generated flattenedProp:{ mapping: prop }
15642 //
15643 // for parsedProp:{ bypass: true }
15644 // the generated flattenedProp:{ bypassed: parsedProp }
15645
15646
15647 styfn.applyParsedProperty = function (ele, parsedProp) {
15648 var self = this;
15649 var prop = parsedProp;
15650 var style = ele._private.style;
15651 var flatProp;
15652 var types = self.types;
15653 var type = self.properties[prop.name].type;
15654 var propIsBypass = prop.bypass;
15655 var origProp = style[prop.name];
15656 var origPropIsBypass = origProp && origProp.bypass;
15657 var _p = ele._private;
15658 var flatPropMapping = 'mapping';
15659
15660 var getVal = function getVal(p) {
15661 if (p == null) {
15662 return null;
15663 } else if (p.pfValue != null) {
15664 return p.pfValue;
15665 } else {
15666 return p.value;
15667 }
15668 };
15669
15670 var checkTriggers = function checkTriggers() {
15671 var fromVal = getVal(origProp);
15672 var toVal = getVal(prop);
15673 self.checkTriggers(ele, prop.name, fromVal, toVal);
15674 };
15675
15676 if (prop && prop.name.substr(0, 3) === 'pie') {
15677 warn('The pie style properties are deprecated. Create charts using background images instead.');
15678 } // edge sanity checks to prevent the client from making serious mistakes
15679
15680
15681 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
15682 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
15683 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15684 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15685 }
15686
15687 if (prop["delete"]) {
15688 // delete the property and use the default value on falsey value
15689 style[prop.name] = undefined;
15690 checkTriggers();
15691 return true;
15692 }
15693
15694 if (prop.deleteBypassed) {
15695 // delete the property that the
15696 if (!origProp) {
15697 checkTriggers();
15698 return true; // can't delete if no prop
15699 } else if (origProp.bypass) {
15700 // delete bypassed
15701 origProp.bypassed = undefined;
15702 checkTriggers();
15703 return true;
15704 } else {
15705 return false; // we're unsuccessful deleting the bypassed
15706 }
15707 } // check if we need to delete the current bypass
15708
15709
15710 if (prop.deleteBypass) {
15711 // then this property is just here to indicate we need to delete
15712 if (!origProp) {
15713 checkTriggers();
15714 return true; // property is already not defined
15715 } else if (origProp.bypass) {
15716 // then replace the bypass property with the original
15717 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15718 style[prop.name] = origProp.bypassed;
15719 checkTriggers();
15720 return true;
15721 } else {
15722 return false; // we're unsuccessful deleting the bypass
15723 }
15724 }
15725
15726 var printMappingErr = function printMappingErr() {
15727 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');
15728 }; // put the property in the style objects
15729
15730
15731 switch (prop.mapped) {
15732 // flatten the property if mapped
15733 case types.mapData:
15734 {
15735 // flatten the field (e.g. data.foo.bar)
15736 var fields = prop.field.split('.');
15737 var fieldVal = _p.data;
15738
15739 for (var i = 0; i < fields.length && fieldVal; i++) {
15740 var field = fields[i];
15741 fieldVal = fieldVal[field];
15742 }
15743
15744 if (fieldVal == null) {
15745 printMappingErr();
15746 return false;
15747 }
15748
15749 var percent;
15750
15751 if (!number(fieldVal)) {
15752 // then don't apply and fall back on the existing style
15753 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15754 return false;
15755 } else {
15756 var fieldWidth = prop.fieldMax - prop.fieldMin;
15757
15758 if (fieldWidth === 0) {
15759 // safety check -- not strictly necessary as no props of zero range should be passed here
15760 percent = 0;
15761 } else {
15762 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15763 }
15764 } // make sure to bound percent value
15765
15766
15767 if (percent < 0) {
15768 percent = 0;
15769 } else if (percent > 1) {
15770 percent = 1;
15771 }
15772
15773 if (type.color) {
15774 var r1 = prop.valueMin[0];
15775 var r2 = prop.valueMax[0];
15776 var g1 = prop.valueMin[1];
15777 var g2 = prop.valueMax[1];
15778 var b1 = prop.valueMin[2];
15779 var b2 = prop.valueMax[2];
15780 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15781 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15782 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)];
15783 flatProp = {
15784 // colours are simple, so just create the flat property instead of expensive string parsing
15785 bypass: prop.bypass,
15786 // we're a bypass if the mapping property is a bypass
15787 name: prop.name,
15788 value: clr,
15789 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15790 };
15791 } else if (type.number) {
15792 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15793 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15794 } else {
15795 return false; // can only map to colours and numbers
15796 }
15797
15798 if (!flatProp) {
15799 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15800 printMappingErr();
15801 return false;
15802 }
15803
15804 flatProp.mapping = prop; // keep a reference to the mapping
15805
15806 prop = flatProp; // the flattened (mapped) property is the one we want
15807
15808 break;
15809 }
15810 // direct mapping
15811
15812 case types.data:
15813 {
15814 // flatten the field (e.g. data.foo.bar)
15815 var _fields = prop.field.split('.');
15816
15817 var _fieldVal = _p.data;
15818
15819 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15820 var _field = _fields[_i3];
15821 _fieldVal = _fieldVal[_field];
15822 }
15823
15824 if (_fieldVal != null) {
15825 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15826 }
15827
15828 if (!flatProp) {
15829 // if we can't flatten the property, then don't apply and fall back on the existing style
15830 printMappingErr();
15831 return false;
15832 }
15833
15834 flatProp.mapping = prop; // keep a reference to the mapping
15835
15836 prop = flatProp; // the flattened (mapped) property is the one we want
15837
15838 break;
15839 }
15840
15841 case types.fn:
15842 {
15843 var fn = prop.value;
15844 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15845
15846 prop.prevFnValue = fnRetVal;
15847
15848 if (fnRetVal == null) {
15849 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15850 return false;
15851 }
15852
15853 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15854
15855 if (!flatProp) {
15856 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15857 return false;
15858 }
15859
15860 flatProp.mapping = copy(prop); // keep a reference to the mapping
15861
15862 prop = flatProp; // the flattened (mapped) property is the one we want
15863
15864 break;
15865 }
15866
15867 case undefined:
15868 break;
15869 // just set the property
15870
15871 default:
15872 return false;
15873 // not a valid mapping
15874 } // if the property is a bypass property, then link the resultant property to the original one
15875
15876
15877 if (propIsBypass) {
15878 if (origPropIsBypass) {
15879 // then this bypass overrides the existing one
15880 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15881 } else {
15882 // then link the orig prop to the new bypass
15883 prop.bypassed = origProp;
15884 }
15885
15886 style[prop.name] = prop; // and set
15887 } else {
15888 // prop is not bypass
15889 if (origPropIsBypass) {
15890 // then keep the orig prop (since it's a bypass) and link to the new prop
15891 origProp.bypassed = prop;
15892 } else {
15893 // then just replace the old prop with the new one
15894 style[prop.name] = prop;
15895 }
15896 }
15897
15898 checkTriggers();
15899 return true;
15900 };
15901
15902 styfn.cleanElements = function (eles, keepBypasses) {
15903 for (var i = 0; i < eles.length; i++) {
15904 var ele = eles[i];
15905 this.clearStyleHints(ele);
15906 ele.dirtyCompoundBoundsCache();
15907 ele.dirtyBoundingBoxCache();
15908
15909 if (!keepBypasses) {
15910 ele._private.style = {};
15911 } else {
15912 var style = ele._private.style;
15913 var propNames = Object.keys(style);
15914
15915 for (var j = 0; j < propNames.length; j++) {
15916 var propName = propNames[j];
15917 var eleProp = style[propName];
15918
15919 if (eleProp != null) {
15920 if (eleProp.bypass) {
15921 eleProp.bypassed = null;
15922 } else {
15923 style[propName] = null;
15924 }
15925 }
15926 }
15927 }
15928 }
15929 }; // updates the visual style for all elements (useful for manual style modification after init)
15930
15931
15932 styfn.update = function () {
15933 var cy = this._private.cy;
15934 var eles = cy.mutableElements();
15935 eles.updateStyle();
15936 }; // diffProps : { name => { prev, next } }
15937
15938
15939 styfn.updateTransitions = function (ele, diffProps) {
15940 var self = this;
15941 var _p = ele._private;
15942 var props = ele.pstyle('transition-property').value;
15943 var duration = ele.pstyle('transition-duration').pfValue;
15944 var delay = ele.pstyle('transition-delay').pfValue;
15945
15946 if (props.length > 0 && duration > 0) {
15947 var style = {}; // build up the style to animate towards
15948
15949 var anyPrev = false;
15950
15951 for (var i = 0; i < props.length; i++) {
15952 var prop = props[i];
15953 var styProp = ele.pstyle(prop);
15954 var diffProp = diffProps[prop];
15955
15956 if (!diffProp) {
15957 continue;
15958 }
15959
15960 var prevProp = diffProp.prev;
15961 var fromProp = prevProp;
15962 var toProp = diffProp.next != null ? diffProp.next : styProp;
15963 var diff = false;
15964 var initVal = void 0;
15965 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15966
15967 if (!fromProp) {
15968 continue;
15969 } // consider px values
15970
15971
15972 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15973 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15974
15975 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15976 } else if (number(fromProp.value) && number(toProp.value)) {
15977 diff = toProp.value - fromProp.value; // nonzero is truthy
15978
15979 initVal = fromProp.value + initDt * diff; // consider colour values
15980 } else if (array(fromProp.value) && array(toProp.value)) {
15981 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15982 initVal = fromProp.strValue;
15983 } // the previous value is good for an animation only if it's different
15984
15985
15986 if (diff) {
15987 style[prop] = toProp.strValue; // to val
15988
15989 this.applyBypass(ele, prop, initVal); // from val
15990
15991 anyPrev = true;
15992 }
15993 } // end if props allow ani
15994 // can't transition if there's nothing previous to transition from
15995
15996
15997 if (!anyPrev) {
15998 return;
15999 }
16000
16001 _p.transitioning = true;
16002 new Promise$1(function (resolve) {
16003 if (delay > 0) {
16004 ele.delayAnimation(delay).play().promise().then(resolve);
16005 } else {
16006 resolve();
16007 }
16008 }).then(function () {
16009 return ele.animation({
16010 style: style,
16011 duration: duration,
16012 easing: ele.pstyle('transition-timing-function').value,
16013 queue: false
16014 }).play().promise();
16015 }).then(function () {
16016 // if( !isBypass ){
16017 self.removeBypasses(ele, props);
16018 ele.emitAndNotify('style'); // }
16019
16020 _p.transitioning = false;
16021 });
16022 } else if (_p.transitioning) {
16023 this.removeBypasses(ele, props);
16024 ele.emitAndNotify('style');
16025 _p.transitioning = false;
16026 }
16027 };
16028
16029 styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
16030 var prop = this.properties[name];
16031 var triggerCheck = getTrigger(prop);
16032
16033 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
16034 onTrigger(prop);
16035 }
16036 };
16037
16038 styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
16039 var _this = this;
16040
16041 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
16042 return prop.triggersZOrder;
16043 }, function () {
16044 _this._private.cy.notify('zorder', ele);
16045 });
16046 };
16047
16048 styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
16049 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
16050 return prop.triggersBounds;
16051 }, function (prop) {
16052 ele.dirtyCompoundBoundsCache();
16053 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
16054 // then dirty the pll edge bb cache as well
16055
16056 if ( // only for beziers -- so performance of other edges isn't affected
16057 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
16058 ele.parallelEdges().forEach(function (pllEdge) {
16059 if (pllEdge.isBundledBezier()) {
16060 pllEdge.dirtyBoundingBoxCache();
16061 }
16062 });
16063 }
16064 });
16065 };
16066
16067 styfn.checkTriggers = function (ele, name, fromValue, toValue) {
16068 ele.dirtyStyleCache();
16069 this.checkZOrderTrigger(ele, name, fromValue, toValue);
16070 this.checkBoundsTrigger(ele, name, fromValue, toValue);
16071 };
16072
16073 var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
16074 // returns true iff application was successful for at least 1 specified property
16075
16076 styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
16077 var self = this;
16078 var props = [];
16079 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
16080
16081 if (name === '*' || name === '**') {
16082 // apply to all property names
16083 if (value !== undefined) {
16084 for (var i = 0; i < self.properties.length; i++) {
16085 var prop = self.properties[i];
16086 var _name = prop.name;
16087 var parsedProp = this.parse(_name, value, true);
16088
16089 if (parsedProp) {
16090 props.push(parsedProp);
16091 }
16092 }
16093 }
16094 } else if (string(name)) {
16095 // then parse the single property
16096 var _parsedProp = this.parse(name, value, true);
16097
16098 if (_parsedProp) {
16099 props.push(_parsedProp);
16100 }
16101 } else if (plainObject(name)) {
16102 // then parse each property
16103 var specifiedProps = name;
16104 updateTransitions = value;
16105 var names = Object.keys(specifiedProps);
16106
16107 for (var _i = 0; _i < names.length; _i++) {
16108 var _name2 = names[_i];
16109 var _value = specifiedProps[_name2];
16110
16111 if (_value === undefined) {
16112 // try camel case name too
16113 _value = specifiedProps[dash2camel(_name2)];
16114 }
16115
16116 if (_value !== undefined) {
16117 var _parsedProp2 = this.parse(_name2, _value, true);
16118
16119 if (_parsedProp2) {
16120 props.push(_parsedProp2);
16121 }
16122 }
16123 }
16124 } else {
16125 // can't do anything without well defined properties
16126 return false;
16127 } // we've failed if there are no valid properties
16128
16129
16130 if (props.length === 0) {
16131 return false;
16132 } // now, apply the bypass properties on the elements
16133
16134
16135 var ret = false; // return true if at least one succesful bypass applied
16136
16137 for (var _i2 = 0; _i2 < eles.length; _i2++) {
16138 // for each ele
16139 var ele = eles[_i2];
16140 var diffProps = {};
16141 var diffProp = void 0;
16142
16143 for (var j = 0; j < props.length; j++) {
16144 // for each prop
16145 var _prop = props[j];
16146
16147 if (updateTransitions) {
16148 var prevProp = ele.pstyle(_prop.name);
16149 diffProp = diffProps[_prop.name] = {
16150 prev: prevProp
16151 };
16152 }
16153
16154 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
16155
16156 if (updateTransitions) {
16157 diffProp.next = ele.pstyle(_prop.name);
16158 }
16159 } // for props
16160
16161
16162 if (ret) {
16163 this.updateStyleHints(ele);
16164 }
16165
16166 if (updateTransitions) {
16167 this.updateTransitions(ele, diffProps, isBypass);
16168 }
16169 } // for eles
16170
16171
16172 return ret;
16173 }; // only useful in specific cases like animation
16174
16175
16176 styfn$1.overrideBypass = function (eles, name, value) {
16177 name = camel2dash(name);
16178
16179 for (var i = 0; i < eles.length; i++) {
16180 var ele = eles[i];
16181 var prop = ele._private.style[name];
16182 var type = this.properties[name].type;
16183 var isColor = type.color;
16184 var isMulti = type.mutiple;
16185 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
16186
16187 if (!prop || !prop.bypass) {
16188 // need a bypass if one doesn't exist
16189 this.applyBypass(ele, name, value);
16190 } else {
16191 prop.value = value;
16192
16193 if (prop.pfValue != null) {
16194 prop.pfValue = value;
16195 }
16196
16197 if (isColor) {
16198 prop.strValue = 'rgb(' + value.join(',') + ')';
16199 } else if (isMulti) {
16200 prop.strValue = value.join(' ');
16201 } else {
16202 prop.strValue = '' + value;
16203 }
16204
16205 this.updateStyleHints(ele);
16206 }
16207
16208 this.checkTriggers(ele, name, oldValue, value);
16209 }
16210 };
16211
16212 styfn$1.removeAllBypasses = function (eles, updateTransitions) {
16213 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
16214 };
16215
16216 styfn$1.removeBypasses = function (eles, props, updateTransitions) {
16217 var isBypass = true;
16218
16219 for (var j = 0; j < eles.length; j++) {
16220 var ele = eles[j];
16221 var diffProps = {};
16222
16223 for (var i = 0; i < props.length; i++) {
16224 var name = props[i];
16225 var prop = this.properties[name];
16226 var prevProp = ele.pstyle(prop.name);
16227
16228 if (!prevProp || !prevProp.bypass) {
16229 // if a bypass doesn't exist for the prop, nothing needs to be removed
16230 continue;
16231 }
16232
16233 var value = ''; // empty => remove bypass
16234
16235 var parsedProp = this.parse(name, value, true);
16236 var diffProp = diffProps[prop.name] = {
16237 prev: prevProp
16238 };
16239 this.applyParsedProperty(ele, parsedProp);
16240 diffProp.next = ele.pstyle(prop.name);
16241 } // for props
16242
16243
16244 this.updateStyleHints(ele);
16245
16246 if (updateTransitions) {
16247 this.updateTransitions(ele, diffProps, isBypass);
16248 }
16249 } // for eles
16250
16251 };
16252
16253 var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
16254
16255 styfn$2.getEmSizeInPixels = function () {
16256 var px = this.containerCss('font-size');
16257
16258 if (px != null) {
16259 return parseFloat(px);
16260 } else {
16261 return 1; // for headless
16262 }
16263 }; // gets css property from the core container
16264
16265
16266 styfn$2.containerCss = function (propName) {
16267 var cy = this._private.cy;
16268 var domElement = cy.container();
16269
16270 if (window$1 && domElement && window$1.getComputedStyle) {
16271 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
16272 }
16273 };
16274
16275 var styfn$3 = {}; // gets the rendered style for an element
16276
16277 styfn$3.getRenderedStyle = function (ele, prop) {
16278 if (prop) {
16279 return this.getStylePropertyValue(ele, prop, true);
16280 } else {
16281 return this.getRawStyle(ele, true);
16282 }
16283 }; // gets the raw style for an element
16284
16285
16286 styfn$3.getRawStyle = function (ele, isRenderedVal) {
16287 var self = this;
16288 ele = ele[0]; // insure it's an element
16289
16290 if (ele) {
16291 var rstyle = {};
16292
16293 for (var i = 0; i < self.properties.length; i++) {
16294 var prop = self.properties[i];
16295 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
16296
16297 if (val != null) {
16298 rstyle[prop.name] = val;
16299 rstyle[dash2camel(prop.name)] = val;
16300 }
16301 }
16302
16303 return rstyle;
16304 }
16305 };
16306
16307 styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
16308 var pstyle = ele.pstyle(property)[subproperty][index];
16309 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
16310 };
16311
16312 styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
16313 var self = this;
16314 ele = ele[0]; // insure it's an element
16315
16316 if (ele) {
16317 var prop = self.properties[propName];
16318
16319 if (prop.alias) {
16320 prop = prop.pointsTo;
16321 }
16322
16323 var type = prop.type;
16324 var styleProp = ele.pstyle(prop.name);
16325
16326 if (styleProp) {
16327 var value = styleProp.value,
16328 units = styleProp.units,
16329 strValue = styleProp.strValue;
16330
16331 if (isRenderedVal && type.number && value != null && number(value)) {
16332 var zoom = ele.cy().zoom();
16333
16334 var getRenderedValue = function getRenderedValue(val) {
16335 return val * zoom;
16336 };
16337
16338 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
16339 return getRenderedValue(val) + units;
16340 };
16341
16342 var isArrayValue = array(value);
16343 var haveUnits = isArrayValue ? units.every(function (u) {
16344 return u != null;
16345 }) : units != null;
16346
16347 if (haveUnits) {
16348 if (isArrayValue) {
16349 return value.map(function (v, i) {
16350 return getValueStringWithUnits(v, units[i]);
16351 }).join(' ');
16352 } else {
16353 return getValueStringWithUnits(value, units);
16354 }
16355 } else {
16356 if (isArrayValue) {
16357 return value.map(function (v) {
16358 return string(v) ? v : '' + getRenderedValue(v);
16359 }).join(' ');
16360 } else {
16361 return '' + getRenderedValue(value);
16362 }
16363 }
16364 } else if (strValue != null) {
16365 return strValue;
16366 }
16367 }
16368
16369 return null;
16370 }
16371 };
16372
16373 styfn$3.getAnimationStartStyle = function (ele, aniProps) {
16374 var rstyle = {};
16375
16376 for (var i = 0; i < aniProps.length; i++) {
16377 var aniProp = aniProps[i];
16378 var name = aniProp.name;
16379 var styleProp = ele.pstyle(name);
16380
16381 if (styleProp !== undefined) {
16382 // then make a prop of it
16383 if (plainObject(styleProp)) {
16384 styleProp = this.parse(name, styleProp.strValue);
16385 } else {
16386 styleProp = this.parse(name, styleProp);
16387 }
16388 }
16389
16390 if (styleProp) {
16391 rstyle[name] = styleProp;
16392 }
16393 }
16394
16395 return rstyle;
16396 };
16397
16398 styfn$3.getPropsList = function (propsObj) {
16399 var self = this;
16400 var rstyle = [];
16401 var style = propsObj;
16402 var props = self.properties;
16403
16404 if (style) {
16405 var names = Object.keys(style);
16406
16407 for (var i = 0; i < names.length; i++) {
16408 var name = names[i];
16409 var val = style[name];
16410 var prop = props[name] || props[camel2dash(name)];
16411 var styleProp = this.parse(prop.name, val);
16412
16413 if (styleProp) {
16414 rstyle.push(styleProp);
16415 }
16416 }
16417 }
16418
16419 return rstyle;
16420 };
16421
16422 styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
16423 var hash = seed.slice();
16424 var name, val, strVal, chVal;
16425 var i, j;
16426
16427 for (i = 0; i < propNames.length; i++) {
16428 name = propNames[i];
16429 val = ele.pstyle(name, false);
16430
16431 if (val == null) {
16432 continue;
16433 } else if (val.pfValue != null) {
16434 hash[0] = hashInt(chVal, hash[0]);
16435 hash[1] = hashIntAlt(chVal, hash[1]);
16436 } else {
16437 strVal = val.strValue;
16438
16439 for (j = 0; j < strVal.length; j++) {
16440 chVal = strVal.charCodeAt(j);
16441 hash[0] = hashInt(chVal, hash[0]);
16442 hash[1] = hashIntAlt(chVal, hash[1]);
16443 }
16444 }
16445 }
16446
16447 return hash;
16448 };
16449
16450 styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
16451
16452 var styfn$4 = {};
16453
16454 styfn$4.appendFromJson = function (json) {
16455 var style = this;
16456
16457 for (var i = 0; i < json.length; i++) {
16458 var context = json[i];
16459 var selector = context.selector;
16460 var props = context.style || context.css;
16461 var names = Object.keys(props);
16462 style.selector(selector); // apply selector
16463
16464 for (var j = 0; j < names.length; j++) {
16465 var name = names[j];
16466 var value = props[name];
16467 style.css(name, value); // apply property
16468 }
16469 }
16470
16471 return style;
16472 }; // accessible cy.style() function
16473
16474
16475 styfn$4.fromJson = function (json) {
16476 var style = this;
16477 style.resetToDefault();
16478 style.appendFromJson(json);
16479 return style;
16480 }; // get json from cy.style() api
16481
16482
16483 styfn$4.json = function () {
16484 var json = [];
16485
16486 for (var i = this.defaultLength; i < this.length; i++) {
16487 var cxt = this[i];
16488 var selector = cxt.selector;
16489 var props = cxt.properties;
16490 var css = {};
16491
16492 for (var j = 0; j < props.length; j++) {
16493 var prop = props[j];
16494 css[prop.name] = prop.strValue;
16495 }
16496
16497 json.push({
16498 selector: !selector ? 'core' : selector.toString(),
16499 style: css
16500 });
16501 }
16502
16503 return json;
16504 };
16505
16506 var styfn$5 = {};
16507
16508 styfn$5.appendFromString = function (string) {
16509 var self = this;
16510 var style = this;
16511 var remaining = '' + string;
16512 var selAndBlockStr;
16513 var blockRem;
16514 var propAndValStr; // remove comments from the style string
16515
16516 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
16517
16518 function removeSelAndBlockFromRemaining() {
16519 // remove the parsed selector and block from the remaining text to parse
16520 if (remaining.length > selAndBlockStr.length) {
16521 remaining = remaining.substr(selAndBlockStr.length);
16522 } else {
16523 remaining = '';
16524 }
16525 }
16526
16527 function removePropAndValFromRem() {
16528 // remove the parsed property and value from the remaining block text to parse
16529 if (blockRem.length > propAndValStr.length) {
16530 blockRem = blockRem.substr(propAndValStr.length);
16531 } else {
16532 blockRem = '';
16533 }
16534 }
16535
16536 for (;;) {
16537 var nothingLeftToParse = remaining.match(/^\s*$/);
16538
16539 if (nothingLeftToParse) {
16540 break;
16541 }
16542
16543 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
16544
16545 if (!selAndBlock) {
16546 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
16547 break;
16548 }
16549
16550 selAndBlockStr = selAndBlock[0]; // parse the selector
16551
16552 var selectorStr = selAndBlock[1];
16553
16554 if (selectorStr !== 'core') {
16555 var selector = new Selector(selectorStr);
16556
16557 if (selector.invalid) {
16558 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
16559
16560 removeSelAndBlockFromRemaining();
16561 continue;
16562 }
16563 } // parse the block of properties and values
16564
16565
16566 var blockStr = selAndBlock[2];
16567 var invalidBlock = false;
16568 blockRem = blockStr;
16569 var props = [];
16570
16571 for (;;) {
16572 var _nothingLeftToParse = blockRem.match(/^\s*$/);
16573
16574 if (_nothingLeftToParse) {
16575 break;
16576 }
16577
16578 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
16579
16580 if (!propAndVal) {
16581 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
16582 invalidBlock = true;
16583 break;
16584 }
16585
16586 propAndValStr = propAndVal[0];
16587 var propStr = propAndVal[1];
16588 var valStr = propAndVal[2];
16589 var prop = self.properties[propStr];
16590
16591 if (!prop) {
16592 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
16593
16594 removePropAndValFromRem();
16595 continue;
16596 }
16597
16598 var parsedProp = style.parse(propStr, valStr);
16599
16600 if (!parsedProp) {
16601 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
16602
16603 removePropAndValFromRem();
16604 continue;
16605 }
16606
16607 props.push({
16608 name: propStr,
16609 val: valStr
16610 });
16611 removePropAndValFromRem();
16612 }
16613
16614 if (invalidBlock) {
16615 removeSelAndBlockFromRemaining();
16616 break;
16617 } // put the parsed block in the style
16618
16619
16620 style.selector(selectorStr);
16621
16622 for (var i = 0; i < props.length; i++) {
16623 var _prop = props[i];
16624 style.css(_prop.name, _prop.val);
16625 }
16626
16627 removeSelAndBlockFromRemaining();
16628 }
16629
16630 return style;
16631 };
16632
16633 styfn$5.fromString = function (string) {
16634 var style = this;
16635 style.resetToDefault();
16636 style.appendFromString(string);
16637 return style;
16638 };
16639
16640 var styfn$6 = {};
16641
16642 (function () {
16643 var number = number$1;
16644 var rgba = rgbaNoBackRefs;
16645 var hsla = hslaNoBackRefs;
16646 var hex3$1 = hex3;
16647 var hex6$1 = hex6;
16648
16649 var data = function data(prefix) {
16650 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16651 };
16652
16653 var mapData = function mapData(prefix) {
16654 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16655 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16656 };
16657
16658 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
16659
16660 styfn$6.types = {
16661 time: {
16662 number: true,
16663 min: 0,
16664 units: 's|ms',
16665 implicitUnits: 'ms'
16666 },
16667 percent: {
16668 number: true,
16669 min: 0,
16670 max: 100,
16671 units: '%',
16672 implicitUnits: '%'
16673 },
16674 percentages: {
16675 number: true,
16676 min: 0,
16677 max: 100,
16678 units: '%',
16679 implicitUnits: '%',
16680 multiple: true
16681 },
16682 zeroOneNumber: {
16683 number: true,
16684 min: 0,
16685 max: 1,
16686 unitless: true
16687 },
16688 zeroOneNumbers: {
16689 number: true,
16690 min: 0,
16691 max: 1,
16692 unitless: true,
16693 multiple: true
16694 },
16695 nOneOneNumber: {
16696 number: true,
16697 min: -1,
16698 max: 1,
16699 unitless: true
16700 },
16701 nonNegativeInt: {
16702 number: true,
16703 min: 0,
16704 integer: true,
16705 unitless: true
16706 },
16707 position: {
16708 enums: ['parent', 'origin']
16709 },
16710 nodeSize: {
16711 number: true,
16712 min: 0,
16713 enums: ['label']
16714 },
16715 number: {
16716 number: true,
16717 unitless: true
16718 },
16719 numbers: {
16720 number: true,
16721 unitless: true,
16722 multiple: true
16723 },
16724 positiveNumber: {
16725 number: true,
16726 unitless: true,
16727 min: 0,
16728 strictMin: true
16729 },
16730 size: {
16731 number: true,
16732 min: 0
16733 },
16734 bidirectionalSize: {
16735 number: true
16736 },
16737 // allows negative
16738 bidirectionalSizeMaybePercent: {
16739 number: true,
16740 allowPercent: true
16741 },
16742 // allows negative
16743 bidirectionalSizes: {
16744 number: true,
16745 multiple: true
16746 },
16747 // allows negative
16748 sizeMaybePercent: {
16749 number: true,
16750 min: 0,
16751 allowPercent: true
16752 },
16753 axisDirection: {
16754 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16755 },
16756 paddingRelativeTo: {
16757 enums: ['width', 'height', 'average', 'min', 'max']
16758 },
16759 bgWH: {
16760 number: true,
16761 min: 0,
16762 allowPercent: true,
16763 enums: ['auto'],
16764 multiple: true
16765 },
16766 bgPos: {
16767 number: true,
16768 allowPercent: true,
16769 multiple: true
16770 },
16771 bgRelativeTo: {
16772 enums: ['inner', 'include-padding'],
16773 multiple: true
16774 },
16775 bgRepeat: {
16776 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16777 multiple: true
16778 },
16779 bgFit: {
16780 enums: ['none', 'contain', 'cover'],
16781 multiple: true
16782 },
16783 bgCrossOrigin: {
16784 enums: ['anonymous', 'use-credentials'],
16785 multiple: true
16786 },
16787 bgClip: {
16788 enums: ['none', 'node'],
16789 multiple: true
16790 },
16791 bgContainment: {
16792 enums: ['inside', 'over'],
16793 multiple: true
16794 },
16795 color: {
16796 color: true
16797 },
16798 colors: {
16799 color: true,
16800 multiple: true
16801 },
16802 fill: {
16803 enums: ['solid', 'linear-gradient', 'radial-gradient']
16804 },
16805 bool: {
16806 enums: ['yes', 'no']
16807 },
16808 bools: {
16809 enums: ['yes', 'no'],
16810 multiple: true
16811 },
16812 lineStyle: {
16813 enums: ['solid', 'dotted', 'dashed']
16814 },
16815 lineCap: {
16816 enums: ['butt', 'round', 'square']
16817 },
16818 borderStyle: {
16819 enums: ['solid', 'dotted', 'dashed', 'double']
16820 },
16821 curveStyle: {
16822 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16823 },
16824 fontFamily: {
16825 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16826 },
16827 fontStyle: {
16828 enums: ['italic', 'normal', 'oblique']
16829 },
16830 fontWeight: {
16831 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16832 },
16833 textDecoration: {
16834 enums: ['none', 'underline', 'overline', 'line-through']
16835 },
16836 textTransform: {
16837 enums: ['none', 'uppercase', 'lowercase']
16838 },
16839 textWrap: {
16840 enums: ['none', 'wrap', 'ellipsis']
16841 },
16842 textOverflowWrap: {
16843 enums: ['whitespace', 'anywhere']
16844 },
16845 textBackgroundShape: {
16846 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16847 },
16848 nodeShape: {
16849 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']
16850 },
16851 compoundIncludeLabels: {
16852 enums: ['include', 'exclude']
16853 },
16854 arrowShape: {
16855 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16856 },
16857 arrowFill: {
16858 enums: ['filled', 'hollow']
16859 },
16860 display: {
16861 enums: ['element', 'none']
16862 },
16863 visibility: {
16864 enums: ['hidden', 'visible']
16865 },
16866 zCompoundDepth: {
16867 enums: ['bottom', 'orphan', 'auto', 'top']
16868 },
16869 zIndexCompare: {
16870 enums: ['auto', 'manual']
16871 },
16872 valign: {
16873 enums: ['top', 'center', 'bottom']
16874 },
16875 halign: {
16876 enums: ['left', 'center', 'right']
16877 },
16878 justification: {
16879 enums: ['left', 'center', 'right', 'auto']
16880 },
16881 text: {
16882 string: true
16883 },
16884 data: {
16885 mapping: true,
16886 regex: data('data')
16887 },
16888 layoutData: {
16889 mapping: true,
16890 regex: data('layoutData')
16891 },
16892 scratch: {
16893 mapping: true,
16894 regex: data('scratch')
16895 },
16896 mapData: {
16897 mapping: true,
16898 regex: mapData('mapData')
16899 },
16900 mapLayoutData: {
16901 mapping: true,
16902 regex: mapData('mapLayoutData')
16903 },
16904 mapScratch: {
16905 mapping: true,
16906 regex: mapData('mapScratch')
16907 },
16908 fn: {
16909 mapping: true,
16910 fn: true
16911 },
16912 url: {
16913 regexes: urlRegexes,
16914 singleRegexMatchValue: true
16915 },
16916 urls: {
16917 regexes: urlRegexes,
16918 singleRegexMatchValue: true,
16919 multiple: true
16920 },
16921 propList: {
16922 propList: true
16923 },
16924 angle: {
16925 number: true,
16926 units: 'deg|rad',
16927 implicitUnits: 'rad'
16928 },
16929 textRotation: {
16930 number: true,
16931 units: 'deg|rad',
16932 implicitUnits: 'rad',
16933 enums: ['none', 'autorotate']
16934 },
16935 polygonPointList: {
16936 number: true,
16937 multiple: true,
16938 evenMultiple: true,
16939 min: -1,
16940 max: 1,
16941 unitless: true
16942 },
16943 edgeDistances: {
16944 enums: ['intersection', 'node-position']
16945 },
16946 edgeEndpoint: {
16947 number: true,
16948 multiple: true,
16949 units: '%|px|em|deg|rad',
16950 implicitUnits: 'px',
16951 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16952 singleEnum: true,
16953 validate: function validate(valArr, unitsArr) {
16954 switch (valArr.length) {
16955 case 2:
16956 // can be % or px only
16957 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16958
16959 case 1:
16960 // can be enum, deg, or rad only
16961 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16962
16963 default:
16964 return false;
16965 }
16966 }
16967 },
16968 easing: {
16969 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16970 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']
16971 },
16972 gradientDirection: {
16973 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']
16974 },
16975 boundsExpansion: {
16976 number: true,
16977 multiple: true,
16978 min: 0,
16979 validate: function validate(valArr) {
16980 var length = valArr.length;
16981 return length === 1 || length === 2 || length === 4;
16982 }
16983 }
16984 };
16985 var diff = {
16986 zeroNonZero: function zeroNonZero(val1, val2) {
16987 if ((val1 == null || val2 == null) && val1 !== val2) {
16988 return true; // null cases could represent any value
16989 }
16990
16991 if (val1 == 0 && val2 != 0) {
16992 return true;
16993 } else if (val1 != 0 && val2 == 0) {
16994 return true;
16995 } else {
16996 return false;
16997 }
16998 },
16999 any: function any(val1, val2) {
17000 return val1 != val2;
17001 },
17002 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
17003 var empty1 = emptyString(str1);
17004 var empty2 = emptyString(str2);
17005 return empty1 && !empty2 || !empty1 && empty2;
17006 }
17007 }; // define visual style properties
17008 //
17009 // - n.b. adding a new group of props may require updates to updateStyleHints()
17010 // - adding new props to an existing group gets handled automatically
17011
17012 var t = styfn$6.types;
17013 var mainLabel = [{
17014 name: 'label',
17015 type: t.text,
17016 triggersBounds: diff.any,
17017 triggersZOrder: diff.emptyNonEmpty
17018 }, {
17019 name: 'text-rotation',
17020 type: t.textRotation,
17021 triggersBounds: diff.any
17022 }, {
17023 name: 'text-margin-x',
17024 type: t.bidirectionalSize,
17025 triggersBounds: diff.any
17026 }, {
17027 name: 'text-margin-y',
17028 type: t.bidirectionalSize,
17029 triggersBounds: diff.any
17030 }];
17031 var sourceLabel = [{
17032 name: 'source-label',
17033 type: t.text,
17034 triggersBounds: diff.any
17035 }, {
17036 name: 'source-text-rotation',
17037 type: t.textRotation,
17038 triggersBounds: diff.any
17039 }, {
17040 name: 'source-text-margin-x',
17041 type: t.bidirectionalSize,
17042 triggersBounds: diff.any
17043 }, {
17044 name: 'source-text-margin-y',
17045 type: t.bidirectionalSize,
17046 triggersBounds: diff.any
17047 }, {
17048 name: 'source-text-offset',
17049 type: t.size,
17050 triggersBounds: diff.any
17051 }];
17052 var targetLabel = [{
17053 name: 'target-label',
17054 type: t.text,
17055 triggersBounds: diff.any
17056 }, {
17057 name: 'target-text-rotation',
17058 type: t.textRotation,
17059 triggersBounds: diff.any
17060 }, {
17061 name: 'target-text-margin-x',
17062 type: t.bidirectionalSize,
17063 triggersBounds: diff.any
17064 }, {
17065 name: 'target-text-margin-y',
17066 type: t.bidirectionalSize,
17067 triggersBounds: diff.any
17068 }, {
17069 name: 'target-text-offset',
17070 type: t.size,
17071 triggersBounds: diff.any
17072 }];
17073 var labelDimensions = [{
17074 name: 'font-family',
17075 type: t.fontFamily,
17076 triggersBounds: diff.any
17077 }, {
17078 name: 'font-style',
17079 type: t.fontStyle,
17080 triggersBounds: diff.any
17081 }, {
17082 name: 'font-weight',
17083 type: t.fontWeight,
17084 triggersBounds: diff.any
17085 }, {
17086 name: 'font-size',
17087 type: t.size,
17088 triggersBounds: diff.any
17089 }, {
17090 name: 'text-transform',
17091 type: t.textTransform,
17092 triggersBounds: diff.any
17093 }, {
17094 name: 'text-wrap',
17095 type: t.textWrap,
17096 triggersBounds: diff.any
17097 }, {
17098 name: 'text-overflow-wrap',
17099 type: t.textOverflowWrap,
17100 triggersBounds: diff.any
17101 }, {
17102 name: 'text-max-width',
17103 type: t.size,
17104 triggersBounds: diff.any
17105 }, {
17106 name: 'text-outline-width',
17107 type: t.size,
17108 triggersBounds: diff.any
17109 }, {
17110 name: 'line-height',
17111 type: t.positiveNumber,
17112 triggersBounds: diff.any
17113 }];
17114 var commonLabel = [{
17115 name: 'text-valign',
17116 type: t.valign,
17117 triggersBounds: diff.any
17118 }, {
17119 name: 'text-halign',
17120 type: t.halign,
17121 triggersBounds: diff.any
17122 }, {
17123 name: 'color',
17124 type: t.color
17125 }, {
17126 name: 'text-outline-color',
17127 type: t.color
17128 }, {
17129 name: 'text-outline-opacity',
17130 type: t.zeroOneNumber
17131 }, {
17132 name: 'text-background-color',
17133 type: t.color
17134 }, {
17135 name: 'text-background-opacity',
17136 type: t.zeroOneNumber
17137 }, {
17138 name: 'text-background-padding',
17139 type: t.size,
17140 triggersBounds: diff.any
17141 }, {
17142 name: 'text-border-opacity',
17143 type: t.zeroOneNumber
17144 }, {
17145 name: 'text-border-color',
17146 type: t.color
17147 }, {
17148 name: 'text-border-width',
17149 type: t.size,
17150 triggersBounds: diff.any
17151 }, {
17152 name: 'text-border-style',
17153 type: t.borderStyle,
17154 triggersBounds: diff.any
17155 }, {
17156 name: 'text-background-shape',
17157 type: t.textBackgroundShape,
17158 triggersBounds: diff.any
17159 }, {
17160 name: 'text-justification',
17161 type: t.justification
17162 }];
17163 var behavior = [{
17164 name: 'events',
17165 type: t.bool
17166 }, {
17167 name: 'text-events',
17168 type: t.bool
17169 }];
17170 var visibility = [{
17171 name: 'display',
17172 type: t.display,
17173 triggersZOrder: diff.any,
17174 triggersBounds: diff.any,
17175 triggersBoundsOfParallelBeziers: true
17176 }, {
17177 name: 'visibility',
17178 type: t.visibility,
17179 triggersZOrder: diff.any
17180 }, {
17181 name: 'opacity',
17182 type: t.zeroOneNumber,
17183 triggersZOrder: diff.zeroNonZero
17184 }, {
17185 name: 'text-opacity',
17186 type: t.zeroOneNumber
17187 }, {
17188 name: 'min-zoomed-font-size',
17189 type: t.size
17190 }, {
17191 name: 'z-compound-depth',
17192 type: t.zCompoundDepth,
17193 triggersZOrder: diff.any
17194 }, {
17195 name: 'z-index-compare',
17196 type: t.zIndexCompare,
17197 triggersZOrder: diff.any
17198 }, {
17199 name: 'z-index',
17200 type: t.nonNegativeInt,
17201 triggersZOrder: diff.any
17202 }];
17203 var overlay = [{
17204 name: 'overlay-padding',
17205 type: t.size,
17206 triggersBounds: diff.any
17207 }, {
17208 name: 'overlay-color',
17209 type: t.color
17210 }, {
17211 name: 'overlay-opacity',
17212 type: t.zeroOneNumber,
17213 triggersBounds: diff.zeroNonZero
17214 }];
17215 var transition = [{
17216 name: 'transition-property',
17217 type: t.propList
17218 }, {
17219 name: 'transition-duration',
17220 type: t.time
17221 }, {
17222 name: 'transition-delay',
17223 type: t.time
17224 }, {
17225 name: 'transition-timing-function',
17226 type: t.easing
17227 }];
17228
17229 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
17230 if (parsedProp.value === 'label') {
17231 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
17232 } else {
17233 return parsedProp.pfValue;
17234 }
17235 };
17236
17237 var nodeBody = [{
17238 name: 'height',
17239 type: t.nodeSize,
17240 triggersBounds: diff.any,
17241 hashOverride: nodeSizeHashOverride
17242 }, {
17243 name: 'width',
17244 type: t.nodeSize,
17245 triggersBounds: diff.any,
17246 hashOverride: nodeSizeHashOverride
17247 }, {
17248 name: 'shape',
17249 type: t.nodeShape,
17250 triggersBounds: diff.any
17251 }, {
17252 name: 'shape-polygon-points',
17253 type: t.polygonPointList,
17254 triggersBounds: diff.any
17255 }, {
17256 name: 'background-color',
17257 type: t.color
17258 }, {
17259 name: 'background-fill',
17260 type: t.fill
17261 }, {
17262 name: 'background-opacity',
17263 type: t.zeroOneNumber
17264 }, {
17265 name: 'background-blacken',
17266 type: t.nOneOneNumber
17267 }, {
17268 name: 'background-gradient-stop-colors',
17269 type: t.colors
17270 }, {
17271 name: 'background-gradient-stop-positions',
17272 type: t.percentages
17273 }, {
17274 name: 'background-gradient-direction',
17275 type: t.gradientDirection
17276 }, {
17277 name: 'padding',
17278 type: t.sizeMaybePercent,
17279 triggersBounds: diff.any
17280 }, {
17281 name: 'padding-relative-to',
17282 type: t.paddingRelativeTo,
17283 triggersBounds: diff.any
17284 }, {
17285 name: 'bounds-expansion',
17286 type: t.boundsExpansion,
17287 triggersBounds: diff.any
17288 }];
17289 var nodeBorder = [{
17290 name: 'border-color',
17291 type: t.color
17292 }, {
17293 name: 'border-opacity',
17294 type: t.zeroOneNumber
17295 }, {
17296 name: 'border-width',
17297 type: t.size,
17298 triggersBounds: diff.any
17299 }, {
17300 name: 'border-style',
17301 type: t.borderStyle
17302 }];
17303 var backgroundImage = [{
17304 name: 'background-image',
17305 type: t.urls
17306 }, {
17307 name: 'background-image-crossorigin',
17308 type: t.bgCrossOrigin
17309 }, {
17310 name: 'background-image-opacity',
17311 type: t.zeroOneNumbers
17312 }, {
17313 name: 'background-image-containment',
17314 type: t.bgContainment
17315 }, {
17316 name: 'background-image-smoothing',
17317 type: t.bools
17318 }, {
17319 name: 'background-position-x',
17320 type: t.bgPos
17321 }, {
17322 name: 'background-position-y',
17323 type: t.bgPos
17324 }, {
17325 name: 'background-width-relative-to',
17326 type: t.bgRelativeTo
17327 }, {
17328 name: 'background-height-relative-to',
17329 type: t.bgRelativeTo
17330 }, {
17331 name: 'background-repeat',
17332 type: t.bgRepeat
17333 }, {
17334 name: 'background-fit',
17335 type: t.bgFit
17336 }, {
17337 name: 'background-clip',
17338 type: t.bgClip
17339 }, {
17340 name: 'background-width',
17341 type: t.bgWH
17342 }, {
17343 name: 'background-height',
17344 type: t.bgWH
17345 }, {
17346 name: 'background-offset-x',
17347 type: t.bgPos
17348 }, {
17349 name: 'background-offset-y',
17350 type: t.bgPos
17351 }];
17352 var compound = [{
17353 name: 'position',
17354 type: t.position,
17355 triggersBounds: diff.any
17356 }, {
17357 name: 'compound-sizing-wrt-labels',
17358 type: t.compoundIncludeLabels,
17359 triggersBounds: diff.any
17360 }, {
17361 name: 'min-width',
17362 type: t.size,
17363 triggersBounds: diff.any
17364 }, {
17365 name: 'min-width-bias-left',
17366 type: t.sizeMaybePercent,
17367 triggersBounds: diff.any
17368 }, {
17369 name: 'min-width-bias-right',
17370 type: t.sizeMaybePercent,
17371 triggersBounds: diff.any
17372 }, {
17373 name: 'min-height',
17374 type: t.size,
17375 triggersBounds: diff.any
17376 }, {
17377 name: 'min-height-bias-top',
17378 type: t.sizeMaybePercent,
17379 triggersBounds: diff.any
17380 }, {
17381 name: 'min-height-bias-bottom',
17382 type: t.sizeMaybePercent,
17383 triggersBounds: diff.any
17384 }];
17385 var edgeLine = [{
17386 name: 'line-style',
17387 type: t.lineStyle
17388 }, {
17389 name: 'line-color',
17390 type: t.color
17391 }, {
17392 name: 'line-fill',
17393 type: t.fill
17394 }, {
17395 name: 'line-cap',
17396 type: t.lineCap
17397 }, {
17398 name: 'line-opacity',
17399 type: t.zeroOneNumber
17400 }, {
17401 name: 'line-dash-pattern',
17402 type: t.numbers
17403 }, {
17404 name: 'line-dash-offset',
17405 type: t.number
17406 }, {
17407 name: 'line-gradient-stop-colors',
17408 type: t.colors
17409 }, {
17410 name: 'line-gradient-stop-positions',
17411 type: t.percentages
17412 }, {
17413 name: 'curve-style',
17414 type: t.curveStyle,
17415 triggersBounds: diff.any,
17416 triggersBoundsOfParallelBeziers: true
17417 }, {
17418 name: 'haystack-radius',
17419 type: t.zeroOneNumber,
17420 triggersBounds: diff.any
17421 }, {
17422 name: 'source-endpoint',
17423 type: t.edgeEndpoint,
17424 triggersBounds: diff.any
17425 }, {
17426 name: 'target-endpoint',
17427 type: t.edgeEndpoint,
17428 triggersBounds: diff.any
17429 }, {
17430 name: 'control-point-step-size',
17431 type: t.size,
17432 triggersBounds: diff.any
17433 }, {
17434 name: 'control-point-distances',
17435 type: t.bidirectionalSizes,
17436 triggersBounds: diff.any
17437 }, {
17438 name: 'control-point-weights',
17439 type: t.numbers,
17440 triggersBounds: diff.any
17441 }, {
17442 name: 'segment-distances',
17443 type: t.bidirectionalSizes,
17444 triggersBounds: diff.any
17445 }, {
17446 name: 'segment-weights',
17447 type: t.numbers,
17448 triggersBounds: diff.any
17449 }, {
17450 name: 'taxi-turn',
17451 type: t.bidirectionalSizeMaybePercent,
17452 triggersBounds: diff.any
17453 }, {
17454 name: 'taxi-turn-min-distance',
17455 type: t.size,
17456 triggersBounds: diff.any
17457 }, {
17458 name: 'taxi-direction',
17459 type: t.axisDirection,
17460 triggersBounds: diff.any
17461 }, {
17462 name: 'edge-distances',
17463 type: t.edgeDistances,
17464 triggersBounds: diff.any
17465 }, {
17466 name: 'arrow-scale',
17467 type: t.positiveNumber,
17468 triggersBounds: diff.any
17469 }, {
17470 name: 'loop-direction',
17471 type: t.angle,
17472 triggersBounds: diff.any
17473 }, {
17474 name: 'loop-sweep',
17475 type: t.angle,
17476 triggersBounds: diff.any
17477 }, {
17478 name: 'source-distance-from-node',
17479 type: t.size,
17480 triggersBounds: diff.any
17481 }, {
17482 name: 'target-distance-from-node',
17483 type: t.size,
17484 triggersBounds: diff.any
17485 }];
17486 var ghost = [{
17487 name: 'ghost',
17488 type: t.bool,
17489 triggersBounds: diff.any
17490 }, {
17491 name: 'ghost-offset-x',
17492 type: t.bidirectionalSize,
17493 triggersBounds: diff.any
17494 }, {
17495 name: 'ghost-offset-y',
17496 type: t.bidirectionalSize,
17497 triggersBounds: diff.any
17498 }, {
17499 name: 'ghost-opacity',
17500 type: t.zeroOneNumber
17501 }];
17502 var core = [{
17503 name: 'selection-box-color',
17504 type: t.color
17505 }, {
17506 name: 'selection-box-opacity',
17507 type: t.zeroOneNumber
17508 }, {
17509 name: 'selection-box-border-color',
17510 type: t.color
17511 }, {
17512 name: 'selection-box-border-width',
17513 type: t.size
17514 }, {
17515 name: 'active-bg-color',
17516 type: t.color
17517 }, {
17518 name: 'active-bg-opacity',
17519 type: t.zeroOneNumber
17520 }, {
17521 name: 'active-bg-size',
17522 type: t.size
17523 }, {
17524 name: 'outside-texture-bg-color',
17525 type: t.color
17526 }, {
17527 name: 'outside-texture-bg-opacity',
17528 type: t.zeroOneNumber
17529 }]; // pie backgrounds for nodes
17530
17531 var pie = [];
17532 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
17533
17534 pie.push({
17535 name: 'pie-size',
17536 type: t.sizeMaybePercent
17537 });
17538
17539 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17540 pie.push({
17541 name: 'pie-' + i + '-background-color',
17542 type: t.color
17543 });
17544 pie.push({
17545 name: 'pie-' + i + '-background-size',
17546 type: t.percent
17547 });
17548 pie.push({
17549 name: 'pie-' + i + '-background-opacity',
17550 type: t.zeroOneNumber
17551 });
17552 } // edge arrows
17553
17554
17555 var edgeArrow = [];
17556 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
17557 [{
17558 name: 'arrow-shape',
17559 type: t.arrowShape,
17560 triggersBounds: diff.any
17561 }, {
17562 name: 'arrow-color',
17563 type: t.color
17564 }, {
17565 name: 'arrow-fill',
17566 type: t.arrowFill
17567 }].forEach(function (prop) {
17568 arrowPrefixes.forEach(function (prefix) {
17569 var name = prefix + '-' + prop.name;
17570 var type = prop.type,
17571 triggersBounds = prop.triggersBounds;
17572 edgeArrow.push({
17573 name: name,
17574 type: type,
17575 triggersBounds: triggersBounds
17576 });
17577 });
17578 }, {});
17579 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
17580 var propGroups = styfn$6.propertyGroups = {
17581 // common to all eles
17582 behavior: behavior,
17583 transition: transition,
17584 visibility: visibility,
17585 overlay: overlay,
17586 ghost: ghost,
17587 // labels
17588 commonLabel: commonLabel,
17589 labelDimensions: labelDimensions,
17590 mainLabel: mainLabel,
17591 sourceLabel: sourceLabel,
17592 targetLabel: targetLabel,
17593 // node props
17594 nodeBody: nodeBody,
17595 nodeBorder: nodeBorder,
17596 backgroundImage: backgroundImage,
17597 pie: pie,
17598 compound: compound,
17599 // edge props
17600 edgeLine: edgeLine,
17601 edgeArrow: edgeArrow,
17602 core: core
17603 };
17604 var propGroupNames = styfn$6.propertyGroupNames = {};
17605 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
17606 propGroupKeys.forEach(function (key) {
17607 propGroupNames[key] = propGroups[key].map(function (prop) {
17608 return prop.name;
17609 });
17610 propGroups[key].forEach(function (prop) {
17611 return prop.groupKey = key;
17612 });
17613 }); // define aliases
17614
17615 var aliases = styfn$6.aliases = [{
17616 name: 'content',
17617 pointsTo: 'label'
17618 }, {
17619 name: 'control-point-distance',
17620 pointsTo: 'control-point-distances'
17621 }, {
17622 name: 'control-point-weight',
17623 pointsTo: 'control-point-weights'
17624 }, {
17625 name: 'edge-text-rotation',
17626 pointsTo: 'text-rotation'
17627 }, {
17628 name: 'padding-left',
17629 pointsTo: 'padding'
17630 }, {
17631 name: 'padding-right',
17632 pointsTo: 'padding'
17633 }, {
17634 name: 'padding-top',
17635 pointsTo: 'padding'
17636 }, {
17637 name: 'padding-bottom',
17638 pointsTo: 'padding'
17639 }]; // list of property names
17640
17641 styfn$6.propertyNames = props.map(function (p) {
17642 return p.name;
17643 }); // allow access of properties by name ( e.g. style.properties.height )
17644
17645 for (var _i = 0; _i < props.length; _i++) {
17646 var prop = props[_i];
17647 props[prop.name] = prop; // allow lookup by name
17648 } // map aliases
17649
17650
17651 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17652 var alias = aliases[_i2];
17653 var pointsToProp = props[alias.pointsTo];
17654 var aliasProp = {
17655 name: alias.name,
17656 alias: true,
17657 pointsTo: pointsToProp
17658 }; // add alias prop for parsing
17659
17660 props.push(aliasProp);
17661 props[alias.name] = aliasProp; // allow lookup by name
17662 }
17663 })();
17664
17665 styfn$6.getDefaultProperty = function (name) {
17666 return this.getDefaultProperties()[name];
17667 };
17668
17669 styfn$6.getDefaultProperties = function () {
17670 var _p = this._private;
17671
17672 if (_p.defaultProperties != null) {
17673 return _p.defaultProperties;
17674 }
17675
17676 var rawProps = extend({
17677 // core props
17678 'selection-box-color': '#ddd',
17679 'selection-box-opacity': 0.65,
17680 'selection-box-border-color': '#aaa',
17681 'selection-box-border-width': 1,
17682 'active-bg-color': 'black',
17683 'active-bg-opacity': 0.15,
17684 'active-bg-size': 30,
17685 'outside-texture-bg-color': '#000',
17686 'outside-texture-bg-opacity': 0.125,
17687 // common node/edge props
17688 'events': 'yes',
17689 'text-events': 'no',
17690 'text-valign': 'top',
17691 'text-halign': 'center',
17692 'text-justification': 'auto',
17693 'line-height': 1,
17694 'color': '#000',
17695 'text-outline-color': '#000',
17696 'text-outline-width': 0,
17697 'text-outline-opacity': 1,
17698 'text-opacity': 1,
17699 'text-decoration': 'none',
17700 'text-transform': 'none',
17701 'text-wrap': 'none',
17702 'text-overflow-wrap': 'whitespace',
17703 'text-max-width': 9999,
17704 'text-background-color': '#000',
17705 'text-background-opacity': 0,
17706 'text-background-shape': 'rectangle',
17707 'text-background-padding': 0,
17708 'text-border-opacity': 0,
17709 'text-border-width': 0,
17710 'text-border-style': 'solid',
17711 'text-border-color': '#000',
17712 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17713 'font-style': 'normal',
17714 'font-weight': 'normal',
17715 'font-size': 16,
17716 'min-zoomed-font-size': 0,
17717 'text-rotation': 'none',
17718 'source-text-rotation': 'none',
17719 'target-text-rotation': 'none',
17720 'visibility': 'visible',
17721 'display': 'element',
17722 'opacity': 1,
17723 'z-compound-depth': 'auto',
17724 'z-index-compare': 'auto',
17725 'z-index': 0,
17726 'label': '',
17727 'text-margin-x': 0,
17728 'text-margin-y': 0,
17729 'source-label': '',
17730 'source-text-offset': 0,
17731 'source-text-margin-x': 0,
17732 'source-text-margin-y': 0,
17733 'target-label': '',
17734 'target-text-offset': 0,
17735 'target-text-margin-x': 0,
17736 'target-text-margin-y': 0,
17737 'overlay-opacity': 0,
17738 'overlay-color': '#000',
17739 'overlay-padding': 10,
17740 'transition-property': 'none',
17741 'transition-duration': 0,
17742 'transition-delay': 0,
17743 'transition-timing-function': 'linear',
17744 // node props
17745 'background-blacken': 0,
17746 'background-color': '#999',
17747 'background-fill': 'solid',
17748 'background-opacity': 1,
17749 'background-image': 'none',
17750 'background-image-crossorigin': 'anonymous',
17751 'background-image-opacity': 1,
17752 'background-image-containment': 'inside',
17753 'background-image-smoothing': 'yes',
17754 'background-position-x': '50%',
17755 'background-position-y': '50%',
17756 'background-offset-x': 0,
17757 'background-offset-y': 0,
17758 'background-width-relative-to': 'include-padding',
17759 'background-height-relative-to': 'include-padding',
17760 'background-repeat': 'no-repeat',
17761 'background-fit': 'none',
17762 'background-clip': 'node',
17763 'background-width': 'auto',
17764 'background-height': 'auto',
17765 'border-color': '#000',
17766 'border-opacity': 1,
17767 'border-width': 0,
17768 'border-style': 'solid',
17769 'height': 30,
17770 'width': 30,
17771 'shape': 'ellipse',
17772 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17773 'bounds-expansion': 0,
17774 // node gradient
17775 'background-gradient-direction': 'to-bottom',
17776 'background-gradient-stop-colors': '#999',
17777 'background-gradient-stop-positions': '0%',
17778 // ghost props
17779 'ghost': 'no',
17780 'ghost-offset-y': 0,
17781 'ghost-offset-x': 0,
17782 'ghost-opacity': 0,
17783 // compound props
17784 'padding': 0,
17785 'padding-relative-to': 'width',
17786 'position': 'origin',
17787 'compound-sizing-wrt-labels': 'include',
17788 'min-width': 0,
17789 'min-width-bias-left': 0,
17790 'min-width-bias-right': 0,
17791 'min-height': 0,
17792 'min-height-bias-top': 0,
17793 'min-height-bias-bottom': 0
17794 }, {
17795 // node pie bg
17796 'pie-size': '100%'
17797 }, [{
17798 name: 'pie-{{i}}-background-color',
17799 value: 'black'
17800 }, {
17801 name: 'pie-{{i}}-background-size',
17802 value: '0%'
17803 }, {
17804 name: 'pie-{{i}}-background-opacity',
17805 value: 1
17806 }].reduce(function (css, prop) {
17807 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17808 var name = prop.name.replace('{{i}}', i);
17809 var val = prop.value;
17810 css[name] = val;
17811 }
17812
17813 return css;
17814 }, {}), {
17815 // edge props
17816 'line-style': 'solid',
17817 'line-color': '#999',
17818 'line-fill': 'solid',
17819 'line-cap': 'butt',
17820 'line-opacity': 1,
17821 'line-gradient-stop-colors': '#999',
17822 'line-gradient-stop-positions': '0%',
17823 'control-point-step-size': 40,
17824 'control-point-weights': 0.5,
17825 'segment-weights': 0.5,
17826 'segment-distances': 20,
17827 'taxi-turn': '50%',
17828 'taxi-turn-min-distance': 10,
17829 'taxi-direction': 'auto',
17830 'edge-distances': 'intersection',
17831 'curve-style': 'haystack',
17832 'haystack-radius': 0,
17833 'arrow-scale': 1,
17834 'loop-direction': '-45deg',
17835 'loop-sweep': '-90deg',
17836 'source-distance-from-node': 0,
17837 'target-distance-from-node': 0,
17838 'source-endpoint': 'outside-to-node',
17839 'target-endpoint': 'outside-to-node',
17840 'line-dash-pattern': [6, 3],
17841 'line-dash-offset': 0
17842 }, [{
17843 name: 'arrow-shape',
17844 value: 'none'
17845 }, {
17846 name: 'arrow-color',
17847 value: '#999'
17848 }, {
17849 name: 'arrow-fill',
17850 value: 'filled'
17851 }].reduce(function (css, prop) {
17852 styfn$6.arrowPrefixes.forEach(function (prefix) {
17853 var name = prefix + '-' + prop.name;
17854 var val = prop.value;
17855 css[name] = val;
17856 });
17857 return css;
17858 }, {}));
17859 var parsedProps = {};
17860
17861 for (var i = 0; i < this.properties.length; i++) {
17862 var prop = this.properties[i];
17863
17864 if (prop.pointsTo) {
17865 continue;
17866 }
17867
17868 var name = prop.name;
17869 var val = rawProps[name];
17870 var parsedProp = this.parse(name, val);
17871 parsedProps[name] = parsedProp;
17872 }
17873
17874 _p.defaultProperties = parsedProps;
17875 return _p.defaultProperties;
17876 };
17877
17878 styfn$6.addDefaultStylesheet = function () {
17879 this.selector(':parent').css({
17880 'shape': 'rectangle',
17881 'padding': 10,
17882 'background-color': '#eee',
17883 'border-color': '#ccc',
17884 'border-width': 1
17885 }).selector('edge').css({
17886 'width': 3
17887 }).selector(':loop').css({
17888 'curve-style': 'bezier'
17889 }).selector('edge:compound').css({
17890 'curve-style': 'bezier',
17891 'source-endpoint': 'outside-to-line',
17892 'target-endpoint': 'outside-to-line'
17893 }).selector(':selected').css({
17894 'background-color': '#0169D9',
17895 'line-color': '#0169D9',
17896 'source-arrow-color': '#0169D9',
17897 'target-arrow-color': '#0169D9',
17898 'mid-source-arrow-color': '#0169D9',
17899 'mid-target-arrow-color': '#0169D9'
17900 }).selector(':parent:selected').css({
17901 'background-color': '#CCE1F9',
17902 'border-color': '#aec8e5'
17903 }).selector(':active').css({
17904 'overlay-color': 'black',
17905 'overlay-padding': 10,
17906 'overlay-opacity': 0.25
17907 });
17908 this.defaultLength = this.length;
17909 };
17910
17911 var styfn$7 = {}; // a caching layer for property parsing
17912
17913 styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17914 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17915
17916 if (fn(value)) {
17917 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17918 }
17919
17920 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17921 var bypassKey = propIsBypass ? 't' : 'f';
17922 var valueKey = '' + value;
17923 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17924 var propCache = self.propCache = self.propCache || [];
17925 var ret;
17926
17927 if (!(ret = propCache[argHash])) {
17928 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17929 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17930 // - mappings can't be shared b/c mappings are per-element
17931
17932
17933 if (propIsBypass || propIsFlat === 'mapping') {
17934 // need a copy since props are mutated later in their lifecycles
17935 ret = copy(ret);
17936
17937 if (ret) {
17938 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17939 }
17940 }
17941
17942 return ret;
17943 };
17944
17945 styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17946 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17947
17948 if (!prop && value != null) {
17949 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17950 }
17951
17952 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17953 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17954 }
17955
17956 return prop;
17957 }; // parse a property; return null on invalid; return parsed property otherwise
17958 // fields :
17959 // - name : the name of the property
17960 // - value : the parsed, native-typed value of the property
17961 // - strValue : a string value that represents the property value in valid css
17962 // - bypass : true iff the property is a bypass property
17963
17964
17965 styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17966 var self = this;
17967 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17968
17969 var property = self.properties[name];
17970 var passedValue = value;
17971 var types = self.types;
17972
17973 if (!property) {
17974 return null;
17975 } // return null on property of unknown name
17976
17977
17978 if (value === undefined) {
17979 return null;
17980 } // can't assign undefined
17981 // the property may be an alias
17982
17983
17984 if (property.alias) {
17985 property = property.pointsTo;
17986 name = property.name;
17987 }
17988
17989 var valueIsString = string(value);
17990
17991 if (valueIsString) {
17992 // trim the value to make parsing easier
17993 value = value.trim();
17994 }
17995
17996 var type = property.type;
17997
17998 if (!type) {
17999 return null;
18000 } // no type, no luck
18001 // check if bypass is null or empty string (i.e. indication to delete bypass property)
18002
18003
18004 if (propIsBypass && (value === '' || value === null)) {
18005 return {
18006 name: name,
18007 value: value,
18008 bypass: true,
18009 deleteBypass: true
18010 };
18011 } // check if value is a function used as a mapper
18012
18013
18014 if (fn(value)) {
18015 return {
18016 name: name,
18017 value: value,
18018 strValue: 'fn',
18019 mapped: types.fn,
18020 bypass: propIsBypass
18021 };
18022 } // check if value is mapped
18023
18024
18025 var data, mapData;
18026
18027 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))) {
18028 if (propIsBypass) {
18029 return false;
18030 } // mappers not allowed in bypass
18031
18032
18033 var mapped = types.data;
18034 return {
18035 name: name,
18036 value: data,
18037 strValue: '' + value,
18038 mapped: mapped,
18039 field: data[1],
18040 bypass: propIsBypass
18041 };
18042 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
18043 if (propIsBypass) {
18044 return false;
18045 } // mappers not allowed in bypass
18046
18047
18048 if (type.multiple) {
18049 return false;
18050 } // impossible to map to num
18051
18052
18053 var _mapped = types.mapData; // we can map only if the type is a colour or a number
18054
18055 if (!(type.color || type.number)) {
18056 return false;
18057 }
18058
18059 var valueMin = this.parse(name, mapData[4]); // parse to validate
18060
18061 if (!valueMin || valueMin.mapped) {
18062 return false;
18063 } // can't be invalid or mapped
18064
18065
18066 var valueMax = this.parse(name, mapData[5]); // parse to validate
18067
18068 if (!valueMax || valueMax.mapped) {
18069 return false;
18070 } // can't be invalid or mapped
18071 // check if valueMin and valueMax are the same
18072
18073
18074 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
18075 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
18076 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
18077 } else if (type.color) {
18078 var c1 = valueMin.value;
18079 var c2 = valueMax.value;
18080 var same = c1[0] === c2[0] // red
18081 && c1[1] === c2[1] // green
18082 && c1[2] === c2[2] // blue
18083 && ( // optional alpha
18084 c1[3] === c2[3] // same alpha outright
18085 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
18086 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
18087 );
18088
18089 if (same) {
18090 return false;
18091 } // can't make a mapper without a range
18092
18093 }
18094
18095 return {
18096 name: name,
18097 value: mapData,
18098 strValue: '' + value,
18099 mapped: _mapped,
18100 field: mapData[1],
18101 fieldMin: parseFloat(mapData[2]),
18102 // min & max are numeric
18103 fieldMax: parseFloat(mapData[3]),
18104 valueMin: valueMin.value,
18105 valueMax: valueMax.value,
18106 bypass: propIsBypass
18107 };
18108 }
18109
18110 if (type.multiple && propIsFlat !== 'multiple') {
18111 var vals;
18112
18113 if (valueIsString) {
18114 vals = value.split(/\s+/);
18115 } else if (array(value)) {
18116 vals = value;
18117 } else {
18118 vals = [value];
18119 }
18120
18121 if (type.evenMultiple && vals.length % 2 !== 0) {
18122 return null;
18123 }
18124
18125 var valArr = [];
18126 var unitsArr = [];
18127 var pfValArr = [];
18128 var strVal = '';
18129 var hasEnum = false;
18130
18131 for (var i = 0; i < vals.length; i++) {
18132 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
18133 hasEnum = hasEnum || string(p.value);
18134 valArr.push(p.value);
18135 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
18136 unitsArr.push(p.units);
18137 strVal += (i > 0 ? ' ' : '') + p.strValue;
18138 }
18139
18140 if (type.validate && !type.validate(valArr, unitsArr)) {
18141 return null;
18142 }
18143
18144 if (type.singleEnum && hasEnum) {
18145 if (valArr.length === 1 && string(valArr[0])) {
18146 return {
18147 name: name,
18148 value: valArr[0],
18149 strValue: valArr[0],
18150 bypass: propIsBypass
18151 };
18152 } else {
18153 return null;
18154 }
18155 }
18156
18157 return {
18158 name: name,
18159 value: valArr,
18160 pfValue: pfValArr,
18161 strValue: strVal,
18162 bypass: propIsBypass,
18163 units: unitsArr
18164 };
18165 } // several types also allow enums
18166
18167
18168 var checkEnums = function checkEnums() {
18169 for (var _i = 0; _i < type.enums.length; _i++) {
18170 var en = type.enums[_i];
18171
18172 if (en === value) {
18173 return {
18174 name: name,
18175 value: value,
18176 strValue: '' + value,
18177 bypass: propIsBypass
18178 };
18179 }
18180 }
18181
18182 return null;
18183 }; // check the type and return the appropriate object
18184
18185
18186 if (type.number) {
18187 var units;
18188 var implicitUnits = 'px'; // not set => px
18189
18190 if (type.units) {
18191 // use specified units if set
18192 units = type.units;
18193 }
18194
18195 if (type.implicitUnits) {
18196 implicitUnits = type.implicitUnits;
18197 }
18198
18199 if (!type.unitless) {
18200 if (valueIsString) {
18201 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
18202
18203 if (units) {
18204 unitsRegex = units;
18205 } // only allow explicit units if so set
18206
18207
18208 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
18209
18210 if (match) {
18211 value = match[1];
18212 units = match[2] || implicitUnits;
18213 }
18214 } else if (!units || type.implicitUnits) {
18215 units = implicitUnits; // implicitly px if unspecified
18216 }
18217 }
18218
18219 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
18220
18221 if (isNaN(value) && type.enums === undefined) {
18222 return null;
18223 } // check if this number type also accepts special keywords in place of numbers
18224 // (i.e. `left`, `auto`, etc)
18225
18226
18227 if (isNaN(value) && type.enums !== undefined) {
18228 value = passedValue;
18229 return checkEnums();
18230 } // check if value must be an integer
18231
18232
18233 if (type.integer && !integer(value)) {
18234 return null;
18235 } // check value is within range
18236
18237
18238 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
18239 return null;
18240 }
18241
18242 var ret = {
18243 name: name,
18244 value: value,
18245 strValue: '' + value + (units ? units : ''),
18246 units: units,
18247 bypass: propIsBypass
18248 }; // normalise value in pixels
18249
18250 if (type.unitless || units !== 'px' && units !== 'em') {
18251 ret.pfValue = value;
18252 } else {
18253 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
18254 } // normalise value in ms
18255
18256
18257 if (units === 'ms' || units === 's') {
18258 ret.pfValue = units === 'ms' ? value : 1000 * value;
18259 } // normalise value in rad
18260
18261
18262 if (units === 'deg' || units === 'rad') {
18263 ret.pfValue = units === 'rad' ? value : deg2rad(value);
18264 } // normalize value in %
18265
18266
18267 if (units === '%') {
18268 ret.pfValue = value / 100;
18269 }
18270
18271 return ret;
18272 } else if (type.propList) {
18273 var props = [];
18274 var propsStr = '' + value;
18275
18276 if (propsStr === 'none') ; else {
18277 // go over each prop
18278 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
18279
18280 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
18281 var propName = propsSplit[_i2].trim();
18282
18283 if (self.properties[propName]) {
18284 props.push(propName);
18285 } else {
18286 warn('`' + propName + '` is not a valid property name');
18287 }
18288 }
18289
18290 if (props.length === 0) {
18291 return null;
18292 }
18293 }
18294
18295 return {
18296 name: name,
18297 value: props,
18298 strValue: props.length === 0 ? 'none' : props.join(' '),
18299 bypass: propIsBypass
18300 };
18301 } else if (type.color) {
18302 var tuple = color2tuple(value);
18303
18304 if (!tuple) {
18305 return null;
18306 }
18307
18308 return {
18309 name: name,
18310 value: tuple,
18311 pfValue: tuple,
18312 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
18313 // n.b. no spaces b/c of multiple support
18314 bypass: propIsBypass
18315 };
18316 } else if (type.regex || type.regexes) {
18317 // first check enums
18318 if (type.enums) {
18319 var enumProp = checkEnums();
18320
18321 if (enumProp) {
18322 return enumProp;
18323 }
18324 }
18325
18326 var regexes = type.regexes ? type.regexes : [type.regex];
18327
18328 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
18329 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
18330
18331 var m = regex.exec(value);
18332
18333 if (m) {
18334 // regex matches
18335 return {
18336 name: name,
18337 value: type.singleRegexMatchValue ? m[1] : m,
18338 strValue: '' + value,
18339 bypass: propIsBypass
18340 };
18341 }
18342 }
18343
18344 return null; // didn't match any
18345 } else if (type.string) {
18346 // just return
18347 return {
18348 name: name,
18349 value: '' + value,
18350 strValue: '' + value,
18351 bypass: propIsBypass
18352 };
18353 } else if (type.enums) {
18354 // check enums last because it's a combo type in others
18355 return checkEnums();
18356 } else {
18357 return null; // not a type we can handle
18358 }
18359 };
18360
18361 var Style = function Style(cy) {
18362 if (!(this instanceof Style)) {
18363 return new Style(cy);
18364 }
18365
18366 if (!core(cy)) {
18367 error('A style must have a core reference');
18368 return;
18369 }
18370
18371 this._private = {
18372 cy: cy,
18373 coreStyle: {}
18374 };
18375 this.length = 0;
18376 this.resetToDefault();
18377 };
18378
18379 var styfn$8 = Style.prototype;
18380
18381 styfn$8.instanceString = function () {
18382 return 'style';
18383 }; // remove all contexts
18384
18385
18386 styfn$8.clear = function () {
18387 var _p = this._private;
18388 var cy = _p.cy;
18389 var eles = cy.elements();
18390
18391 for (var i = 0; i < this.length; i++) {
18392 this[i] = undefined;
18393 }
18394
18395 this.length = 0;
18396 _p.contextStyles = {};
18397 _p.propDiffs = {};
18398 this.cleanElements(eles, true);
18399 eles.forEach(function (ele) {
18400 var ele_p = ele[0]._private;
18401 ele_p.styleDirty = true;
18402 ele_p.appliedInitStyle = false;
18403 });
18404 return this; // chaining
18405 };
18406
18407 styfn$8.resetToDefault = function () {
18408 this.clear();
18409 this.addDefaultStylesheet();
18410 return this;
18411 }; // builds a style object for the 'core' selector
18412
18413
18414 styfn$8.core = function (propName) {
18415 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
18416 }; // create a new context from the specified selector string and switch to that context
18417
18418
18419 styfn$8.selector = function (selectorStr) {
18420 // 'core' is a special case and does not need a selector
18421 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
18422 var i = this.length++; // new context means new index
18423
18424 this[i] = {
18425 selector: selector,
18426 properties: [],
18427 mappedProperties: [],
18428 index: i
18429 };
18430 return this; // chaining
18431 }; // add one or many css rules to the current context
18432
18433
18434 styfn$8.css = function () {
18435 var self = this;
18436 var args = arguments;
18437
18438 if (args.length === 1) {
18439 var map = args[0];
18440
18441 for (var i = 0; i < self.properties.length; i++) {
18442 var prop = self.properties[i];
18443 var mapVal = map[prop.name];
18444
18445 if (mapVal === undefined) {
18446 mapVal = map[dash2camel(prop.name)];
18447 }
18448
18449 if (mapVal !== undefined) {
18450 this.cssRule(prop.name, mapVal);
18451 }
18452 }
18453 } else if (args.length === 2) {
18454 this.cssRule(args[0], args[1]);
18455 } // do nothing if args are invalid
18456
18457
18458 return this; // chaining
18459 };
18460
18461 styfn$8.style = styfn$8.css; // add a single css rule to the current context
18462
18463 styfn$8.cssRule = function (name, value) {
18464 // name-value pair
18465 var property = this.parse(name, value); // add property to current context if valid
18466
18467 if (property) {
18468 var i = this.length - 1;
18469 this[i].properties.push(property);
18470 this[i].properties[property.name] = property; // allow access by name as well
18471
18472 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
18473 this._private.hasPie = true;
18474 }
18475
18476 if (property.mapped) {
18477 this[i].mappedProperties.push(property);
18478 } // add to core style if necessary
18479
18480
18481 var currentSelectorIsCore = !this[i].selector;
18482
18483 if (currentSelectorIsCore) {
18484 this._private.coreStyle[property.name] = property;
18485 }
18486 }
18487
18488 return this; // chaining
18489 };
18490
18491 styfn$8.append = function (style) {
18492 if (stylesheet(style)) {
18493 style.appendToStyle(this);
18494 } else if (array(style)) {
18495 this.appendFromJson(style);
18496 } else if (string(style)) {
18497 this.appendFromString(style);
18498 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
18499
18500
18501 return this;
18502 }; // static function
18503
18504
18505 Style.fromJson = function (cy, json) {
18506 var style = new Style(cy);
18507 style.fromJson(json);
18508 return style;
18509 };
18510
18511 Style.fromString = function (cy, string) {
18512 return new Style(cy).fromString(string);
18513 };
18514
18515 [styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
18516 extend(styfn$8, props);
18517 });
18518 Style.types = styfn$8.types;
18519 Style.properties = styfn$8.properties;
18520 Style.propertyGroups = styfn$8.propertyGroups;
18521 Style.propertyGroupNames = styfn$8.propertyGroupNames;
18522 Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
18523
18524 var corefn$7 = {
18525 style: function style(newStyle) {
18526 if (newStyle) {
18527 var s = this.setStyle(newStyle);
18528 s.update();
18529 }
18530
18531 return this._private.style;
18532 },
18533 setStyle: function setStyle(style) {
18534 var _p = this._private;
18535
18536 if (stylesheet(style)) {
18537 _p.style = style.generateStyle(this);
18538 } else if (array(style)) {
18539 _p.style = Style.fromJson(this, style);
18540 } else if (string(style)) {
18541 _p.style = Style.fromString(this, style);
18542 } else {
18543 _p.style = Style(this);
18544 }
18545
18546 return _p.style;
18547 },
18548 // e.g. cy.data() changed => recalc ele mappers
18549 updateStyle: function updateStyle() {
18550 this.mutableElements().updateStyle(); // just send to all eles
18551 }
18552 };
18553
18554 var defaultSelectionType = 'single';
18555 var corefn$8 = {
18556 autolock: function autolock(bool) {
18557 if (bool !== undefined) {
18558 this._private.autolock = bool ? true : false;
18559 } else {
18560 return this._private.autolock;
18561 }
18562
18563 return this; // chaining
18564 },
18565 autoungrabify: function autoungrabify(bool) {
18566 if (bool !== undefined) {
18567 this._private.autoungrabify = bool ? true : false;
18568 } else {
18569 return this._private.autoungrabify;
18570 }
18571
18572 return this; // chaining
18573 },
18574 autounselectify: function autounselectify(bool) {
18575 if (bool !== undefined) {
18576 this._private.autounselectify = bool ? true : false;
18577 } else {
18578 return this._private.autounselectify;
18579 }
18580
18581 return this; // chaining
18582 },
18583 selectionType: function selectionType(selType) {
18584 var _p = this._private;
18585
18586 if (_p.selectionType == null) {
18587 _p.selectionType = defaultSelectionType;
18588 }
18589
18590 if (selType !== undefined) {
18591 if (selType === 'additive' || selType === 'single') {
18592 _p.selectionType = selType;
18593 }
18594 } else {
18595 return _p.selectionType;
18596 }
18597
18598 return this;
18599 },
18600 panningEnabled: function panningEnabled(bool) {
18601 if (bool !== undefined) {
18602 this._private.panningEnabled = bool ? true : false;
18603 } else {
18604 return this._private.panningEnabled;
18605 }
18606
18607 return this; // chaining
18608 },
18609 userPanningEnabled: function userPanningEnabled(bool) {
18610 if (bool !== undefined) {
18611 this._private.userPanningEnabled = bool ? true : false;
18612 } else {
18613 return this._private.userPanningEnabled;
18614 }
18615
18616 return this; // chaining
18617 },
18618 zoomingEnabled: function zoomingEnabled(bool) {
18619 if (bool !== undefined) {
18620 this._private.zoomingEnabled = bool ? true : false;
18621 } else {
18622 return this._private.zoomingEnabled;
18623 }
18624
18625 return this; // chaining
18626 },
18627 userZoomingEnabled: function userZoomingEnabled(bool) {
18628 if (bool !== undefined) {
18629 this._private.userZoomingEnabled = bool ? true : false;
18630 } else {
18631 return this._private.userZoomingEnabled;
18632 }
18633
18634 return this; // chaining
18635 },
18636 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18637 if (bool !== undefined) {
18638 this._private.boxSelectionEnabled = bool ? true : false;
18639 } else {
18640 return this._private.boxSelectionEnabled;
18641 }
18642
18643 return this; // chaining
18644 },
18645 pan: function pan() {
18646 var args = arguments;
18647 var pan = this._private.pan;
18648 var dim, val, dims, x, y;
18649
18650 switch (args.length) {
18651 case 0:
18652 // .pan()
18653 return pan;
18654
18655 case 1:
18656 if (string(args[0])) {
18657 // .pan('x')
18658 dim = args[0];
18659 return pan[dim];
18660 } else if (plainObject(args[0])) {
18661 // .pan({ x: 0, y: 100 })
18662 if (!this._private.panningEnabled) {
18663 return this;
18664 }
18665
18666 dims = args[0];
18667 x = dims.x;
18668 y = dims.y;
18669
18670 if (number(x)) {
18671 pan.x = x;
18672 }
18673
18674 if (number(y)) {
18675 pan.y = y;
18676 }
18677
18678 this.emit('pan viewport');
18679 }
18680
18681 break;
18682
18683 case 2:
18684 // .pan('x', 100)
18685 if (!this._private.panningEnabled) {
18686 return this;
18687 }
18688
18689 dim = args[0];
18690 val = args[1];
18691
18692 if ((dim === 'x' || dim === 'y') && number(val)) {
18693 pan[dim] = val;
18694 }
18695
18696 this.emit('pan viewport');
18697 break;
18698 // invalid
18699 }
18700
18701 this.notify('viewport');
18702 return this; // chaining
18703 },
18704 panBy: function panBy(arg0, arg1) {
18705 var args = arguments;
18706 var pan = this._private.pan;
18707 var dim, val, dims, x, y;
18708
18709 if (!this._private.panningEnabled) {
18710 return this;
18711 }
18712
18713 switch (args.length) {
18714 case 1:
18715 if (plainObject(arg0)) {
18716 // .panBy({ x: 0, y: 100 })
18717 dims = args[0];
18718 x = dims.x;
18719 y = dims.y;
18720
18721 if (number(x)) {
18722 pan.x += x;
18723 }
18724
18725 if (number(y)) {
18726 pan.y += y;
18727 }
18728
18729 this.emit('pan viewport');
18730 }
18731
18732 break;
18733
18734 case 2:
18735 // .panBy('x', 100)
18736 dim = arg0;
18737 val = arg1;
18738
18739 if ((dim === 'x' || dim === 'y') && number(val)) {
18740 pan[dim] += val;
18741 }
18742
18743 this.emit('pan viewport');
18744 break;
18745 // invalid
18746 }
18747
18748 this.notify('viewport');
18749 return this; // chaining
18750 },
18751 fit: function fit(elements, padding) {
18752 var viewportState = this.getFitViewport(elements, padding);
18753
18754 if (viewportState) {
18755 var _p = this._private;
18756 _p.zoom = viewportState.zoom;
18757 _p.pan = viewportState.pan;
18758 this.emit('pan zoom viewport');
18759 this.notify('viewport');
18760 }
18761
18762 return this; // chaining
18763 },
18764 getFitViewport: function getFitViewport(elements, padding) {
18765 if (number(elements) && padding === undefined) {
18766 // elements is optional
18767 padding = elements;
18768 elements = undefined;
18769 }
18770
18771 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18772 return;
18773 }
18774
18775 var bb;
18776
18777 if (string(elements)) {
18778 var sel = elements;
18779 elements = this.$(sel);
18780 } else if (boundingBox(elements)) {
18781 // assume bb
18782 var bbe = elements;
18783 bb = {
18784 x1: bbe.x1,
18785 y1: bbe.y1,
18786 x2: bbe.x2,
18787 y2: bbe.y2
18788 };
18789 bb.w = bb.x2 - bb.x1;
18790 bb.h = bb.y2 - bb.y1;
18791 } else if (!elementOrCollection(elements)) {
18792 elements = this.mutableElements();
18793 }
18794
18795 if (elementOrCollection(elements) && elements.empty()) {
18796 return;
18797 } // can't fit to nothing
18798
18799
18800 bb = bb || elements.boundingBox();
18801 var w = this.width();
18802 var h = this.height();
18803 var zoom;
18804 padding = number(padding) ? padding : 0;
18805
18806 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18807 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18808
18809 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18810 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18811 var pan = {
18812 // now pan to middle
18813 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18814 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18815 };
18816 return {
18817 zoom: zoom,
18818 pan: pan
18819 };
18820 }
18821
18822 return;
18823 },
18824 zoomRange: function zoomRange(min, max) {
18825 var _p = this._private;
18826
18827 if (max == null) {
18828 var opts = min;
18829 min = opts.min;
18830 max = opts.max;
18831 }
18832
18833 if (number(min) && number(max) && min <= max) {
18834 _p.minZoom = min;
18835 _p.maxZoom = max;
18836 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18837 _p.minZoom = min;
18838 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18839 _p.maxZoom = max;
18840 }
18841
18842 return this;
18843 },
18844 minZoom: function minZoom(zoom) {
18845 if (zoom === undefined) {
18846 return this._private.minZoom;
18847 } else {
18848 return this.zoomRange({
18849 min: zoom
18850 });
18851 }
18852 },
18853 maxZoom: function maxZoom(zoom) {
18854 if (zoom === undefined) {
18855 return this._private.maxZoom;
18856 } else {
18857 return this.zoomRange({
18858 max: zoom
18859 });
18860 }
18861 },
18862 getZoomedViewport: function getZoomedViewport(params) {
18863 var _p = this._private;
18864 var currentPan = _p.pan;
18865 var currentZoom = _p.zoom;
18866 var pos; // in rendered px
18867
18868 var zoom;
18869 var bail = false;
18870
18871 if (!_p.zoomingEnabled) {
18872 // zooming disabled
18873 bail = true;
18874 }
18875
18876 if (number(params)) {
18877 // then set the zoom
18878 zoom = params;
18879 } else if (plainObject(params)) {
18880 // then zoom about a point
18881 zoom = params.level;
18882
18883 if (params.position != null) {
18884 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18885 } else if (params.renderedPosition != null) {
18886 pos = params.renderedPosition;
18887 }
18888
18889 if (pos != null && !_p.panningEnabled) {
18890 // panning disabled
18891 bail = true;
18892 }
18893 } // crop zoom
18894
18895
18896 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18897 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18898
18899 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18900 return null;
18901 }
18902
18903 if (pos != null) {
18904 // set zoom about position
18905 var pan1 = currentPan;
18906 var zoom1 = currentZoom;
18907 var zoom2 = zoom;
18908 var pan2 = {
18909 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18910 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18911 };
18912 return {
18913 zoomed: true,
18914 panned: true,
18915 zoom: zoom2,
18916 pan: pan2
18917 };
18918 } else {
18919 // just set the zoom
18920 return {
18921 zoomed: true,
18922 panned: false,
18923 zoom: zoom,
18924 pan: currentPan
18925 };
18926 }
18927 },
18928 zoom: function zoom(params) {
18929 if (params === undefined) {
18930 // get
18931 return this._private.zoom;
18932 } else {
18933 // set
18934 var vp = this.getZoomedViewport(params);
18935 var _p = this._private;
18936
18937 if (vp == null || !vp.zoomed) {
18938 return this;
18939 }
18940
18941 _p.zoom = vp.zoom;
18942
18943 if (vp.panned) {
18944 _p.pan.x = vp.pan.x;
18945 _p.pan.y = vp.pan.y;
18946 }
18947
18948 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18949 this.notify('viewport');
18950 return this; // chaining
18951 }
18952 },
18953 viewport: function viewport(opts) {
18954 var _p = this._private;
18955 var zoomDefd = true;
18956 var panDefd = true;
18957 var events = []; // to trigger
18958
18959 var zoomFailed = false;
18960 var panFailed = false;
18961
18962 if (!opts) {
18963 return this;
18964 }
18965
18966 if (!number(opts.zoom)) {
18967 zoomDefd = false;
18968 }
18969
18970 if (!plainObject(opts.pan)) {
18971 panDefd = false;
18972 }
18973
18974 if (!zoomDefd && !panDefd) {
18975 return this;
18976 }
18977
18978 if (zoomDefd) {
18979 var z = opts.zoom;
18980
18981 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18982 zoomFailed = true;
18983 } else {
18984 _p.zoom = z;
18985 events.push('zoom');
18986 }
18987 }
18988
18989 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18990 var p = opts.pan;
18991
18992 if (number(p.x)) {
18993 _p.pan.x = p.x;
18994 panFailed = false;
18995 }
18996
18997 if (number(p.y)) {
18998 _p.pan.y = p.y;
18999 panFailed = false;
19000 }
19001
19002 if (!panFailed) {
19003 events.push('pan');
19004 }
19005 }
19006
19007 if (events.length > 0) {
19008 events.push('viewport');
19009 this.emit(events.join(' '));
19010 this.notify('viewport');
19011 }
19012
19013 return this; // chaining
19014 },
19015 center: function center(elements) {
19016 var pan = this.getCenterPan(elements);
19017
19018 if (pan) {
19019 this._private.pan = pan;
19020 this.emit('pan viewport');
19021 this.notify('viewport');
19022 }
19023
19024 return this; // chaining
19025 },
19026 getCenterPan: function getCenterPan(elements, zoom) {
19027 if (!this._private.panningEnabled) {
19028 return;
19029 }
19030
19031 if (string(elements)) {
19032 var selector = elements;
19033 elements = this.mutableElements().filter(selector);
19034 } else if (!elementOrCollection(elements)) {
19035 elements = this.mutableElements();
19036 }
19037
19038 if (elements.length === 0) {
19039 return;
19040 } // can't centre pan to nothing
19041
19042
19043 var bb = elements.boundingBox();
19044 var w = this.width();
19045 var h = this.height();
19046 zoom = zoom === undefined ? this._private.zoom : zoom;
19047 var pan = {
19048 // middle
19049 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
19050 y: (h - zoom * (bb.y1 + bb.y2)) / 2
19051 };
19052 return pan;
19053 },
19054 reset: function reset() {
19055 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
19056 return this;
19057 }
19058
19059 this.viewport({
19060 pan: {
19061 x: 0,
19062 y: 0
19063 },
19064 zoom: 1
19065 });
19066 return this; // chaining
19067 },
19068 invalidateSize: function invalidateSize() {
19069 this._private.sizeCache = null;
19070 },
19071 size: function size() {
19072 var _p = this._private;
19073 var container = _p.container;
19074 return _p.sizeCache = _p.sizeCache || (container ? function () {
19075 var style = window$1.getComputedStyle(container);
19076
19077 var val = function val(name) {
19078 return parseFloat(style.getPropertyValue(name));
19079 };
19080
19081 return {
19082 width: container.clientWidth - val('padding-left') - val('padding-right'),
19083 height: container.clientHeight - val('padding-top') - val('padding-bottom')
19084 };
19085 }() : {
19086 // fallback if no container (not 0 b/c can be used for dividing etc)
19087 width: 1,
19088 height: 1
19089 });
19090 },
19091 width: function width() {
19092 return this.size().width;
19093 },
19094 height: function height() {
19095 return this.size().height;
19096 },
19097 extent: function extent() {
19098 var pan = this._private.pan;
19099 var zoom = this._private.zoom;
19100 var rb = this.renderedExtent();
19101 var b = {
19102 x1: (rb.x1 - pan.x) / zoom,
19103 x2: (rb.x2 - pan.x) / zoom,
19104 y1: (rb.y1 - pan.y) / zoom,
19105 y2: (rb.y2 - pan.y) / zoom
19106 };
19107 b.w = b.x2 - b.x1;
19108 b.h = b.y2 - b.y1;
19109 return b;
19110 },
19111 renderedExtent: function renderedExtent() {
19112 var width = this.width();
19113 var height = this.height();
19114 return {
19115 x1: 0,
19116 y1: 0,
19117 x2: width,
19118 y2: height,
19119 w: width,
19120 h: height
19121 };
19122 }
19123 }; // aliases
19124
19125 corefn$8.centre = corefn$8.center; // backwards compatibility
19126
19127 corefn$8.autolockNodes = corefn$8.autolock;
19128 corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
19129
19130 var fn$6 = {
19131 data: define$3.data({
19132 field: 'data',
19133 bindingEvent: 'data',
19134 allowBinding: true,
19135 allowSetting: true,
19136 settingEvent: 'data',
19137 settingTriggersEvent: true,
19138 triggerFnName: 'trigger',
19139 allowGetting: true,
19140 updateStyle: true
19141 }),
19142 removeData: define$3.removeData({
19143 field: 'data',
19144 event: 'data',
19145 triggerFnName: 'trigger',
19146 triggerEvent: true,
19147 updateStyle: true
19148 }),
19149 scratch: define$3.data({
19150 field: 'scratch',
19151 bindingEvent: 'scratch',
19152 allowBinding: true,
19153 allowSetting: true,
19154 settingEvent: 'scratch',
19155 settingTriggersEvent: true,
19156 triggerFnName: 'trigger',
19157 allowGetting: true,
19158 updateStyle: true
19159 }),
19160 removeScratch: define$3.removeData({
19161 field: 'scratch',
19162 event: 'scratch',
19163 triggerFnName: 'trigger',
19164 triggerEvent: true,
19165 updateStyle: true
19166 })
19167 }; // aliases
19168
19169 fn$6.attr = fn$6.data;
19170 fn$6.removeAttr = fn$6.removeData;
19171
19172 var Core = function Core(opts) {
19173 var cy = this;
19174 opts = extend({}, opts);
19175 var container = opts.container; // allow for passing a wrapped jquery object
19176 // e.g. cytoscape({ container: $('#cy') })
19177
19178 if (container && !htmlElement(container) && htmlElement(container[0])) {
19179 container = container[0];
19180 }
19181
19182 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
19183
19184 reg = reg || {};
19185
19186 if (reg && reg.cy) {
19187 reg.cy.destroy();
19188 reg = {}; // old instance => replace reg completely
19189 }
19190
19191 var readies = reg.readies = reg.readies || [];
19192
19193 if (container) {
19194 container._cyreg = reg;
19195 } // make sure container assoc'd reg points to this cy
19196
19197
19198 reg.cy = cy;
19199 var head = window$1 !== undefined && container !== undefined && !opts.headless;
19200 var options = opts;
19201 options.layout = extend({
19202 name: head ? 'grid' : 'null'
19203 }, options.layout);
19204 options.renderer = extend({
19205 name: head ? 'canvas' : 'null'
19206 }, options.renderer);
19207
19208 var defVal = function defVal(def, val, altVal) {
19209 if (val !== undefined) {
19210 return val;
19211 } else if (altVal !== undefined) {
19212 return altVal;
19213 } else {
19214 return def;
19215 }
19216 };
19217
19218 var _p = this._private = {
19219 container: container,
19220 // html dom ele container
19221 ready: false,
19222 // whether ready has been triggered
19223 options: options,
19224 // cached options
19225 elements: new Collection(this),
19226 // elements in the graph
19227 listeners: [],
19228 // list of listeners
19229 aniEles: new Collection(this),
19230 // elements being animated
19231 data: options.data || {},
19232 // data for the core
19233 scratch: {},
19234 // scratch object for core
19235 layout: null,
19236 renderer: null,
19237 destroyed: false,
19238 // whether destroy was called
19239 notificationsEnabled: true,
19240 // whether notifications are sent to the renderer
19241 minZoom: 1e-50,
19242 maxZoom: 1e50,
19243 zoomingEnabled: defVal(true, options.zoomingEnabled),
19244 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
19245 panningEnabled: defVal(true, options.panningEnabled),
19246 userPanningEnabled: defVal(true, options.userPanningEnabled),
19247 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
19248 autolock: defVal(false, options.autolock, options.autolockNodes),
19249 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
19250 autounselectify: defVal(false, options.autounselectify),
19251 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
19252 zoom: number(options.zoom) ? options.zoom : 1,
19253 pan: {
19254 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
19255 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
19256 },
19257 animation: {
19258 // object for currently-running animations
19259 current: [],
19260 queue: []
19261 },
19262 hasCompoundNodes: false
19263 };
19264
19265 this.createEmitter(); // set selection type
19266
19267 this.selectionType(options.selectionType); // init zoom bounds
19268
19269 this.zoomRange({
19270 min: options.minZoom,
19271 max: options.maxZoom
19272 });
19273
19274 var loadExtData = function loadExtData(extData, next) {
19275 var anyIsPromise = extData.some(promise);
19276
19277 if (anyIsPromise) {
19278 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
19279 } else {
19280 next(extData); // exec synchronously for convenience
19281 }
19282 }; // start with the default stylesheet so we have something before loading an external stylesheet
19283
19284
19285 if (_p.styleEnabled) {
19286 cy.setStyle([]);
19287 } // create the renderer
19288
19289
19290 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
19291
19292 cy.initRenderer(rendererOptions);
19293
19294 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
19295 cy.notifications(false); // remove old elements
19296
19297 var oldEles = cy.mutableElements();
19298
19299 if (oldEles.length > 0) {
19300 oldEles.remove();
19301 }
19302
19303 if (elements != null) {
19304 if (plainObject(elements) || array(elements)) {
19305 cy.add(elements);
19306 }
19307 }
19308
19309 cy.one('layoutready', function (e) {
19310 cy.notifications(true);
19311 cy.emit(e); // we missed this event by turning notifications off, so pass it on
19312
19313 cy.one('load', onload);
19314 cy.emitAndNotify('load');
19315 }).one('layoutstop', function () {
19316 cy.one('done', ondone);
19317 cy.emit('done');
19318 });
19319 var layoutOpts = extend({}, cy._private.options.layout);
19320 layoutOpts.eles = cy.elements();
19321 cy.layout(layoutOpts).run();
19322 };
19323
19324 loadExtData([options.style, options.elements], function (thens) {
19325 var initStyle = thens[0];
19326 var initEles = thens[1]; // init style
19327
19328 if (_p.styleEnabled) {
19329 cy.style().append(initStyle);
19330 } // initial load
19331
19332
19333 setElesAndLayout(initEles, function () {
19334 // onready
19335 cy.startAnimationLoop();
19336 _p.ready = true; // if a ready callback is specified as an option, the bind it
19337
19338 if (fn(options.ready)) {
19339 cy.on('ready', options.ready);
19340 } // bind all the ready handlers registered before creating this instance
19341
19342
19343 for (var i = 0; i < readies.length; i++) {
19344 var fn$1 = readies[i];
19345 cy.on('ready', fn$1);
19346 }
19347
19348 if (reg) {
19349 reg.readies = [];
19350 } // 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
19351
19352
19353 cy.emit('ready');
19354 }, options.done);
19355 });
19356 };
19357
19358 var corefn$9 = Core.prototype; // short alias
19359
19360 extend(corefn$9, {
19361 instanceString: function instanceString() {
19362 return 'core';
19363 },
19364 isReady: function isReady() {
19365 return this._private.ready;
19366 },
19367 destroyed: function destroyed() {
19368 return this._private.destroyed;
19369 },
19370 ready: function ready(fn) {
19371 if (this.isReady()) {
19372 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
19373 } else {
19374 this.on('ready', fn);
19375 }
19376
19377 return this;
19378 },
19379 destroy: function destroy() {
19380 var cy = this;
19381 if (cy.destroyed()) return;
19382 cy.stopAnimationLoop();
19383 cy.destroyRenderer();
19384 this.emit('destroy');
19385 cy._private.destroyed = true;
19386 return cy;
19387 },
19388 hasElementWithId: function hasElementWithId(id) {
19389 return this._private.elements.hasElementWithId(id);
19390 },
19391 getElementById: function getElementById(id) {
19392 return this._private.elements.getElementById(id);
19393 },
19394 hasCompoundNodes: function hasCompoundNodes() {
19395 return this._private.hasCompoundNodes;
19396 },
19397 headless: function headless() {
19398 return this._private.renderer.isHeadless();
19399 },
19400 styleEnabled: function styleEnabled() {
19401 return this._private.styleEnabled;
19402 },
19403 addToPool: function addToPool(eles) {
19404 this._private.elements.merge(eles);
19405
19406 return this; // chaining
19407 },
19408 removeFromPool: function removeFromPool(eles) {
19409 this._private.elements.unmerge(eles);
19410
19411 return this;
19412 },
19413 container: function container() {
19414 return this._private.container || null;
19415 },
19416 mount: function mount(container) {
19417 if (container == null) {
19418 return;
19419 }
19420
19421 var cy = this;
19422 var _p = cy._private;
19423 var options = _p.options;
19424
19425 if (!htmlElement(container) && htmlElement(container[0])) {
19426 container = container[0];
19427 }
19428
19429 cy.stopAnimationLoop();
19430 cy.destroyRenderer();
19431 _p.container = container;
19432 _p.styleEnabled = true;
19433 cy.invalidateSize();
19434 cy.initRenderer(extend({}, options, options.renderer, {
19435 // allow custom renderer name to be re-used, otherwise use canvas
19436 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
19437 }));
19438 cy.startAnimationLoop();
19439 cy.style(options.style);
19440 cy.emit('mount');
19441 return cy;
19442 },
19443 unmount: function unmount() {
19444 var cy = this;
19445 cy.stopAnimationLoop();
19446 cy.destroyRenderer();
19447 cy.initRenderer({
19448 name: 'null'
19449 });
19450 cy.emit('unmount');
19451 return cy;
19452 },
19453 options: function options() {
19454 return copy(this._private.options);
19455 },
19456 json: function json(obj) {
19457 var cy = this;
19458 var _p = cy._private;
19459 var eles = cy.mutableElements();
19460
19461 var getFreshRef = function getFreshRef(ele) {
19462 return cy.getElementById(ele.id());
19463 };
19464
19465 if (plainObject(obj)) {
19466 // set
19467 cy.startBatch();
19468
19469 if (obj.elements) {
19470 var idInJson = {};
19471
19472 var updateEles = function updateEles(jsons, gr) {
19473 var toAdd = [];
19474 var toMod = [];
19475
19476 for (var i = 0; i < jsons.length; i++) {
19477 var json = jsons[i];
19478
19479 if (!json.data.id) {
19480 warn('cy.json() cannot handle elements without an ID attribute');
19481 continue;
19482 }
19483
19484 var id = '' + json.data.id; // id must be string
19485
19486 var ele = cy.getElementById(id);
19487 idInJson[id] = true;
19488
19489 if (ele.length !== 0) {
19490 // existing element should be updated
19491 toMod.push({
19492 ele: ele,
19493 json: json
19494 });
19495 } else {
19496 // otherwise should be added
19497 if (gr) {
19498 json.group = gr;
19499 toAdd.push(json);
19500 } else {
19501 toAdd.push(json);
19502 }
19503 }
19504 }
19505
19506 cy.add(toAdd);
19507
19508 for (var _i = 0; _i < toMod.length; _i++) {
19509 var _toMod$_i = toMod[_i],
19510 _ele = _toMod$_i.ele,
19511 _json = _toMod$_i.json;
19512
19513 _ele.json(_json);
19514 }
19515 };
19516
19517 if (array(obj.elements)) {
19518 // elements: []
19519 updateEles(obj.elements);
19520 } else {
19521 // elements: { nodes: [], edges: [] }
19522 var grs = ['nodes', 'edges'];
19523
19524 for (var i = 0; i < grs.length; i++) {
19525 var gr = grs[i];
19526 var elements = obj.elements[gr];
19527
19528 if (array(elements)) {
19529 updateEles(elements, gr);
19530 }
19531 }
19532 }
19533
19534 var parentsToRemove = cy.collection();
19535 eles.filter(function (ele) {
19536 return !idInJson[ele.id()];
19537 }).forEach(function (ele) {
19538 if (ele.isParent()) {
19539 parentsToRemove.merge(ele);
19540 } else {
19541 ele.remove();
19542 }
19543 }); // so that children are not removed w/parent
19544
19545 parentsToRemove.forEach(function (ele) {
19546 return ele.children().move({
19547 parent: null
19548 });
19549 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
19550
19551 parentsToRemove.forEach(function (ele) {
19552 return getFreshRef(ele).remove();
19553 });
19554 }
19555
19556 if (obj.style) {
19557 cy.style(obj.style);
19558 }
19559
19560 if (obj.zoom != null && obj.zoom !== _p.zoom) {
19561 cy.zoom(obj.zoom);
19562 }
19563
19564 if (obj.pan) {
19565 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
19566 cy.pan(obj.pan);
19567 }
19568 }
19569
19570 if (obj.data) {
19571 cy.data(obj.data);
19572 }
19573
19574 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
19575
19576 for (var _i2 = 0; _i2 < fields.length; _i2++) {
19577 var f = fields[_i2];
19578
19579 if (obj[f] != null) {
19580 cy[f](obj[f]);
19581 }
19582 }
19583
19584 cy.endBatch();
19585 return this; // chaining
19586 } else {
19587 // get
19588 var flat = !!obj;
19589 var json = {};
19590
19591 if (flat) {
19592 json.elements = this.elements().map(function (ele) {
19593 return ele.json();
19594 });
19595 } else {
19596 json.elements = {};
19597 eles.forEach(function (ele) {
19598 var group = ele.group();
19599
19600 if (!json.elements[group]) {
19601 json.elements[group] = [];
19602 }
19603
19604 json.elements[group].push(ele.json());
19605 });
19606 }
19607
19608 if (this._private.styleEnabled) {
19609 json.style = cy.style().json();
19610 }
19611
19612 json.data = copy(cy.data());
19613 var options = _p.options;
19614 json.zoomingEnabled = _p.zoomingEnabled;
19615 json.userZoomingEnabled = _p.userZoomingEnabled;
19616 json.zoom = _p.zoom;
19617 json.minZoom = _p.minZoom;
19618 json.maxZoom = _p.maxZoom;
19619 json.panningEnabled = _p.panningEnabled;
19620 json.userPanningEnabled = _p.userPanningEnabled;
19621 json.pan = copy(_p.pan);
19622 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19623 json.renderer = copy(options.renderer);
19624 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19625 json.textureOnViewport = options.textureOnViewport;
19626 json.wheelSensitivity = options.wheelSensitivity;
19627 json.motionBlur = options.motionBlur;
19628 return json;
19629 }
19630 }
19631 });
19632 corefn$9.$id = corefn$9.getElementById;
19633 [corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
19634 extend(corefn$9, props);
19635 });
19636
19637 /* eslint-disable no-unused-vars */
19638
19639 var defaults$9 = {
19640 fit: true,
19641 // whether to fit the viewport to the graph
19642 directed: false,
19643 // whether the tree is directed downwards (or edges can point in any direction if false)
19644 padding: 30,
19645 // padding on fit
19646 circle: false,
19647 // put depths in concentric circles if true, put depths top down if false
19648 grid: false,
19649 // whether to create an even grid into which the DAG is placed (circle:false only)
19650 spacingFactor: 1.75,
19651 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19652 boundingBox: undefined,
19653 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19654 avoidOverlap: true,
19655 // prevents node overlap, may overflow boundingBox if not enough space
19656 nodeDimensionsIncludeLabels: false,
19657 // Excludes the label when calculating node bounding boxes for the layout algorithm
19658 roots: undefined,
19659 // the roots of the trees
19660 maximal: false,
19661 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
19662 animate: false,
19663 // whether to transition the node positions
19664 animationDuration: 500,
19665 // duration of animation in ms if enabled
19666 animationEasing: undefined,
19667 // easing of animation if enabled,
19668 animateFilter: function animateFilter(node, i) {
19669 return true;
19670 },
19671 // 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
19672 ready: undefined,
19673 // callback on layoutready
19674 stop: undefined,
19675 // callback on layoutstop
19676 transform: function transform(node, position) {
19677 return position;
19678 } // transform a given node position. Useful for changing flow direction in discrete layouts
19679
19680 };
19681 /* eslint-enable */
19682
19683 var getInfo = function getInfo(ele) {
19684 return ele.scratch('breadthfirst');
19685 };
19686
19687 var setInfo = function setInfo(ele, obj) {
19688 return ele.scratch('breadthfirst', obj);
19689 };
19690
19691 function BreadthFirstLayout(options) {
19692 this.options = extend({}, defaults$9, options);
19693 }
19694
19695 BreadthFirstLayout.prototype.run = function () {
19696 var params = this.options;
19697 var options = params;
19698 var cy = params.cy;
19699 var eles = options.eles;
19700 var nodes = eles.nodes().filter(function (n) {
19701 return !n.isParent();
19702 });
19703 var graph = eles;
19704 var directed = options.directed;
19705 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
19706
19707 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19708 x1: 0,
19709 y1: 0,
19710 w: cy.width(),
19711 h: cy.height()
19712 });
19713 var roots;
19714
19715 if (elementOrCollection(options.roots)) {
19716 roots = options.roots;
19717 } else if (array(options.roots)) {
19718 var rootsArray = [];
19719
19720 for (var i = 0; i < options.roots.length; i++) {
19721 var id = options.roots[i];
19722 var ele = cy.getElementById(id);
19723 rootsArray.push(ele);
19724 }
19725
19726 roots = cy.collection(rootsArray);
19727 } else if (string(options.roots)) {
19728 roots = cy.$(options.roots);
19729 } else {
19730 if (directed) {
19731 roots = nodes.roots();
19732 } else {
19733 var components = eles.components();
19734 roots = cy.collection();
19735
19736 var _loop = function _loop(_i) {
19737 var comp = components[_i];
19738 var maxDegree = comp.maxDegree(false);
19739 var compRoots = comp.filter(function (ele) {
19740 return ele.degree(false) === maxDegree;
19741 });
19742 roots = roots.add(compRoots);
19743 };
19744
19745 for (var _i = 0; _i < components.length; _i++) {
19746 _loop(_i);
19747 }
19748 }
19749 }
19750
19751 var depths = [];
19752 var foundByBfs = {};
19753
19754 var addToDepth = function addToDepth(ele, d) {
19755 if (depths[d] == null) {
19756 depths[d] = [];
19757 }
19758
19759 var i = depths[d].length;
19760 depths[d].push(ele);
19761 setInfo(ele, {
19762 index: i,
19763 depth: d
19764 });
19765 };
19766
19767 var changeDepth = function changeDepth(ele, newDepth) {
19768 var _getInfo = getInfo(ele),
19769 depth = _getInfo.depth,
19770 index = _getInfo.index;
19771
19772 depths[depth][index] = null;
19773 addToDepth(ele, newDepth);
19774 }; // find the depths of the nodes
19775
19776
19777 graph.bfs({
19778 roots: roots,
19779 directed: options.directed,
19780 visit: function visit(node, edge, pNode, i, depth) {
19781 var ele = node[0];
19782 var id = ele.id();
19783 addToDepth(ele, depth);
19784 foundByBfs[id] = true;
19785 }
19786 }); // check for nodes not found by bfs
19787
19788 var orphanNodes = [];
19789
19790 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19791 var _ele = nodes[_i2];
19792
19793 if (foundByBfs[_ele.id()]) {
19794 continue;
19795 } else {
19796 orphanNodes.push(_ele);
19797 }
19798 } // assign the nodes a depth and index
19799
19800
19801 var assignDepthsAt = function assignDepthsAt(i) {
19802 var eles = depths[i];
19803
19804 for (var j = 0; j < eles.length; j++) {
19805 var _ele2 = eles[j];
19806
19807 if (_ele2 == null) {
19808 eles.splice(j, 1);
19809 j--;
19810 continue;
19811 }
19812
19813 setInfo(_ele2, {
19814 depth: i,
19815 index: j
19816 });
19817 }
19818 };
19819
19820 var assignDepths = function assignDepths() {
19821 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19822 assignDepthsAt(_i3);
19823 }
19824 };
19825
19826 var adjustMaximally = function adjustMaximally(ele, shifted) {
19827 var eInfo = getInfo(ele);
19828 var incomers = ele.incomers().filter(function (el) {
19829 return el.isNode() && eles.has(el);
19830 });
19831 var maxDepth = -1;
19832 var id = ele.id();
19833
19834 for (var k = 0; k < incomers.length; k++) {
19835 var incmr = incomers[k];
19836 var iInfo = getInfo(incmr);
19837 maxDepth = Math.max(maxDepth, iInfo.depth);
19838 }
19839
19840 if (eInfo.depth <= maxDepth) {
19841 if (shifted[id]) {
19842 return null;
19843 }
19844
19845 changeDepth(ele, maxDepth + 1);
19846 shifted[id] = true;
19847 return true;
19848 }
19849
19850 return false;
19851 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19852
19853
19854 if (directed && maximal) {
19855 var Q = [];
19856 var shifted = {};
19857
19858 var enqueue = function enqueue(n) {
19859 return Q.push(n);
19860 };
19861
19862 var dequeue = function dequeue() {
19863 return Q.shift();
19864 };
19865
19866 nodes.forEach(function (n) {
19867 return Q.push(n);
19868 });
19869
19870 while (Q.length > 0) {
19871 var _ele3 = dequeue();
19872
19873 var didShift = adjustMaximally(_ele3, shifted);
19874
19875 if (didShift) {
19876 _ele3.outgoers().filter(function (el) {
19877 return el.isNode() && eles.has(el);
19878 }).forEach(enqueue);
19879 } else if (didShift === null) {
19880 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19881 break; // exit on failure
19882 }
19883 }
19884 }
19885
19886 assignDepths(); // clear holes
19887 // find min distance we need to leave between nodes
19888
19889 var minDistance = 0;
19890
19891 if (options.avoidOverlap) {
19892 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19893 var n = nodes[_i4];
19894 var nbb = n.layoutDimensions(options);
19895 var w = nbb.w;
19896 var h = nbb.h;
19897 minDistance = Math.max(minDistance, w, h);
19898 }
19899 } // get the weighted percent for an element based on its connectivity to other levels
19900
19901
19902 var cachedWeightedPercent = {};
19903
19904 var getWeightedPercent = function getWeightedPercent(ele) {
19905 if (cachedWeightedPercent[ele.id()]) {
19906 return cachedWeightedPercent[ele.id()];
19907 }
19908
19909 var eleDepth = getInfo(ele).depth;
19910 var neighbors = ele.neighborhood();
19911 var percent = 0;
19912 var samples = 0;
19913
19914 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19915 var neighbor = neighbors[_i5];
19916
19917 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19918 continue;
19919 }
19920
19921 var bf = getInfo(neighbor);
19922
19923 if (bf == null) {
19924 continue;
19925 }
19926
19927 var index = bf.index;
19928 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19929
19930 if (index == null || depth == null) {
19931 continue;
19932 }
19933
19934 var nDepth = depths[depth].length;
19935
19936 if (depth < eleDepth) {
19937 // only get influenced by elements above
19938 percent += index / nDepth;
19939 samples++;
19940 }
19941 }
19942
19943 samples = Math.max(1, samples);
19944 percent = percent / samples;
19945
19946 if (samples === 0) {
19947 // put lone nodes at the start
19948 percent = 0;
19949 }
19950
19951 cachedWeightedPercent[ele.id()] = percent;
19952 return percent;
19953 }; // rearrange the indices in each depth level based on connectivity
19954
19955
19956 var sortFn = function sortFn(a, b) {
19957 var apct = getWeightedPercent(a);
19958 var bpct = getWeightedPercent(b);
19959 var diff = apct - bpct;
19960
19961 if (diff === 0) {
19962 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19963 } else {
19964 return diff;
19965 }
19966 }; // sort each level to make connected nodes closer
19967
19968
19969 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19970 depths[_i6].sort(sortFn);
19971
19972 assignDepthsAt(_i6);
19973 } // assign orphan nodes to a new top-level depth
19974
19975
19976 var orphanDepth = [];
19977
19978 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19979 orphanDepth.push(orphanNodes[_i7]);
19980 }
19981
19982 depths.unshift(orphanDepth);
19983 assignDepths();
19984 var biggestDepthSize = 0;
19985
19986 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19987 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19988 }
19989
19990 var center = {
19991 x: bb.x1 + bb.w / 2,
19992 y: bb.x1 + bb.h / 2
19993 };
19994 var maxDepthSize = depths.reduce(function (max, eles) {
19995 return Math.max(max, eles.length);
19996 }, 0);
19997
19998 var getPosition = function getPosition(ele) {
19999 var _getInfo2 = getInfo(ele),
20000 depth = _getInfo2.depth,
20001 index = _getInfo2.index;
20002
20003 var depthSize = depths[depth].length;
20004 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
20005 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
20006 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
20007 radiusStepSize = Math.max(radiusStepSize, minDistance);
20008
20009 if (!options.circle) {
20010 var epos = {
20011 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
20012 y: (depth + 1) * distanceY
20013 };
20014 return epos;
20015 } else {
20016 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
20017 var theta = 2 * Math.PI / depths[depth].length * index;
20018
20019 if (depth === 0 && depths[0].length === 1) {
20020 radius = 1;
20021 }
20022
20023 return {
20024 x: center.x + radius * Math.cos(theta),
20025 y: center.y + radius * Math.sin(theta)
20026 };
20027 }
20028 };
20029
20030 eles.nodes().layoutPositions(this, options, getPosition);
20031 return this; // chaining
20032 };
20033
20034 var defaults$a = {
20035 fit: true,
20036 // whether to fit the viewport to the graph
20037 padding: 30,
20038 // the padding on fit
20039 boundingBox: undefined,
20040 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20041 avoidOverlap: true,
20042 // prevents node overlap, may overflow boundingBox and radius if not enough space
20043 nodeDimensionsIncludeLabels: false,
20044 // Excludes the label when calculating node bounding boxes for the layout algorithm
20045 spacingFactor: undefined,
20046 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20047 radius: undefined,
20048 // the radius of the circle
20049 startAngle: 3 / 2 * Math.PI,
20050 // where nodes start in radians
20051 sweep: undefined,
20052 // how many radians should be between the first and last node (defaults to full circle)
20053 clockwise: true,
20054 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
20055 sort: undefined,
20056 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20057 animate: false,
20058 // whether to transition the node positions
20059 animationDuration: 500,
20060 // duration of animation in ms if enabled
20061 animationEasing: undefined,
20062 // easing of animation if enabled
20063 animateFilter: function animateFilter(node, i) {
20064 return true;
20065 },
20066 // 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
20067 ready: undefined,
20068 // callback on layoutready
20069 stop: undefined,
20070 // callback on layoutstop
20071 transform: function transform(node, position) {
20072 return position;
20073 } // transform a given node position. Useful for changing flow direction in discrete layouts
20074
20075 };
20076
20077 function CircleLayout(options) {
20078 this.options = extend({}, defaults$a, options);
20079 }
20080
20081 CircleLayout.prototype.run = function () {
20082 var params = this.options;
20083 var options = params;
20084 var cy = params.cy;
20085 var eles = options.eles;
20086 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
20087 var nodes = eles.nodes().not(':parent');
20088
20089 if (options.sort) {
20090 nodes = nodes.sort(options.sort);
20091 }
20092
20093 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20094 x1: 0,
20095 y1: 0,
20096 w: cy.width(),
20097 h: cy.height()
20098 });
20099 var center = {
20100 x: bb.x1 + bb.w / 2,
20101 y: bb.y1 + bb.h / 2
20102 };
20103 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
20104 var dTheta = sweep / Math.max(1, nodes.length - 1);
20105 var r;
20106 var minDistance = 0;
20107
20108 for (var i = 0; i < nodes.length; i++) {
20109 var n = nodes[i];
20110 var nbb = n.layoutDimensions(options);
20111 var w = nbb.w;
20112 var h = nbb.h;
20113 minDistance = Math.max(minDistance, w, h);
20114 }
20115
20116 if (number(options.radius)) {
20117 r = options.radius;
20118 } else if (nodes.length <= 1) {
20119 r = 0;
20120 } else {
20121 r = Math.min(bb.h, bb.w) / 2 - minDistance;
20122 } // calculate the radius
20123
20124
20125 if (nodes.length > 1 && options.avoidOverlap) {
20126 // but only if more than one node (can't overlap)
20127 minDistance *= 1.75; // just to have some nice spacing
20128
20129 var dcos = Math.cos(dTheta) - Math.cos(0);
20130 var dsin = Math.sin(dTheta) - Math.sin(0);
20131 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
20132
20133 r = Math.max(rMin, r);
20134 }
20135
20136 var getPos = function getPos(ele, i) {
20137 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
20138 var rx = r * Math.cos(theta);
20139 var ry = r * Math.sin(theta);
20140 var pos = {
20141 x: center.x + rx,
20142 y: center.y + ry
20143 };
20144 return pos;
20145 };
20146
20147 eles.nodes().layoutPositions(this, options, getPos);
20148 return this; // chaining
20149 };
20150
20151 var defaults$b = {
20152 fit: true,
20153 // whether to fit the viewport to the graph
20154 padding: 30,
20155 // the padding on fit
20156 startAngle: 3 / 2 * Math.PI,
20157 // where nodes start in radians
20158 sweep: undefined,
20159 // how many radians should be between the first and last node (defaults to full circle)
20160 clockwise: true,
20161 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
20162 equidistant: false,
20163 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
20164 minNodeSpacing: 10,
20165 // min spacing between outside of nodes (used for radius adjustment)
20166 boundingBox: undefined,
20167 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20168 avoidOverlap: true,
20169 // prevents node overlap, may overflow boundingBox if not enough space
20170 nodeDimensionsIncludeLabels: false,
20171 // Excludes the label when calculating node bounding boxes for the layout algorithm
20172 height: undefined,
20173 // height of layout area (overrides container height)
20174 width: undefined,
20175 // width of layout area (overrides container width)
20176 spacingFactor: undefined,
20177 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20178 concentric: function concentric(node) {
20179 // returns numeric value for each node, placing higher nodes in levels towards the centre
20180 return node.degree();
20181 },
20182 levelWidth: function levelWidth(nodes) {
20183 // the variation of concentric values in each level
20184 return nodes.maxDegree() / 4;
20185 },
20186 animate: false,
20187 // whether to transition the node positions
20188 animationDuration: 500,
20189 // duration of animation in ms if enabled
20190 animationEasing: undefined,
20191 // easing of animation if enabled
20192 animateFilter: function animateFilter(node, i) {
20193 return true;
20194 },
20195 // 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
20196 ready: undefined,
20197 // callback on layoutready
20198 stop: undefined,
20199 // callback on layoutstop
20200 transform: function transform(node, position) {
20201 return position;
20202 } // transform a given node position. Useful for changing flow direction in discrete layouts
20203
20204 };
20205
20206 function ConcentricLayout(options) {
20207 this.options = extend({}, defaults$b, options);
20208 }
20209
20210 ConcentricLayout.prototype.run = function () {
20211 var params = this.options;
20212 var options = params;
20213 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
20214 var cy = params.cy;
20215 var eles = options.eles;
20216 var nodes = eles.nodes().not(':parent');
20217 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20218 x1: 0,
20219 y1: 0,
20220 w: cy.width(),
20221 h: cy.height()
20222 });
20223 var center = {
20224 x: bb.x1 + bb.w / 2,
20225 y: bb.y1 + bb.h / 2
20226 };
20227 var nodeValues = []; // { node, value }
20228
20229 var maxNodeSize = 0;
20230
20231 for (var i = 0; i < nodes.length; i++) {
20232 var node = nodes[i];
20233 var value = void 0; // calculate the node value
20234
20235 value = options.concentric(node);
20236 nodeValues.push({
20237 value: value,
20238 node: node
20239 }); // for style mapping
20240
20241 node._private.scratch.concentric = value;
20242 } // in case we used the `concentric` in style
20243
20244
20245 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
20246
20247 for (var _i = 0; _i < nodes.length; _i++) {
20248 var _node = nodes[_i];
20249
20250 var nbb = _node.layoutDimensions(options);
20251
20252 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
20253 } // sort node values in descreasing order
20254
20255
20256 nodeValues.sort(function (a, b) {
20257 return b.value - a.value;
20258 });
20259 var levelWidth = options.levelWidth(nodes); // put the values into levels
20260
20261 var levels = [[]];
20262 var currentLevel = levels[0];
20263
20264 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
20265 var val = nodeValues[_i2];
20266
20267 if (currentLevel.length > 0) {
20268 var diff = Math.abs(currentLevel[0].value - val.value);
20269
20270 if (diff >= levelWidth) {
20271 currentLevel = [];
20272 levels.push(currentLevel);
20273 }
20274 }
20275
20276 currentLevel.push(val);
20277 } // create positions from levels
20278
20279
20280 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
20281
20282 if (!options.avoidOverlap) {
20283 // then strictly constrain to bb
20284 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
20285 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
20286 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
20287 minDist = Math.min(minDist, rStep);
20288 } // find the metrics for each level
20289
20290
20291 var r = 0;
20292
20293 for (var _i3 = 0; _i3 < levels.length; _i3++) {
20294 var level = levels[_i3];
20295 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
20296 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
20297
20298 if (level.length > 1 && options.avoidOverlap) {
20299 // but only if more than one node (can't overlap)
20300 var dcos = Math.cos(dTheta) - Math.cos(0);
20301 var dsin = Math.sin(dTheta) - Math.sin(0);
20302 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
20303
20304 r = Math.max(rMin, r);
20305 }
20306
20307 level.r = r;
20308 r += minDist;
20309 }
20310
20311 if (options.equidistant) {
20312 var rDeltaMax = 0;
20313 var _r = 0;
20314
20315 for (var _i4 = 0; _i4 < levels.length; _i4++) {
20316 var _level = levels[_i4];
20317 var rDelta = _level.r - _r;
20318 rDeltaMax = Math.max(rDeltaMax, rDelta);
20319 }
20320
20321 _r = 0;
20322
20323 for (var _i5 = 0; _i5 < levels.length; _i5++) {
20324 var _level2 = levels[_i5];
20325
20326 if (_i5 === 0) {
20327 _r = _level2.r;
20328 }
20329
20330 _level2.r = _r;
20331 _r += rDeltaMax;
20332 }
20333 } // calculate the node positions
20334
20335
20336 var pos = {}; // id => position
20337
20338 for (var _i6 = 0; _i6 < levels.length; _i6++) {
20339 var _level3 = levels[_i6];
20340 var _dTheta = _level3.dTheta;
20341 var _r2 = _level3.r;
20342
20343 for (var j = 0; j < _level3.length; j++) {
20344 var _val = _level3[j];
20345 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
20346 var p = {
20347 x: center.x + _r2 * Math.cos(theta),
20348 y: center.y + _r2 * Math.sin(theta)
20349 };
20350 pos[_val.node.id()] = p;
20351 }
20352 } // position the nodes
20353
20354
20355 eles.nodes().layoutPositions(this, options, function (ele) {
20356 var id = ele.id();
20357 return pos[id];
20358 });
20359 return this; // chaining
20360 };
20361
20362 /*
20363 The CoSE layout was written by Gerardo Huck.
20364 https://www.linkedin.com/in/gerardohuck/
20365
20366 Based on the following article:
20367 http://dl.acm.org/citation.cfm?id=1498047
20368
20369 Modifications tracked on Github.
20370 */
20371 var DEBUG;
20372 /**
20373 * @brief : default layout options
20374 */
20375
20376 var defaults$c = {
20377 // Called on `layoutready`
20378 ready: function ready() {},
20379 // Called on `layoutstop`
20380 stop: function stop() {},
20381 // Whether to animate while running the layout
20382 // true : Animate continuously as the layout is running
20383 // false : Just show the end result
20384 // 'end' : Animate with the end result, from the initial positions to the end positions
20385 animate: true,
20386 // Easing of the animation for animate:'end'
20387 animationEasing: undefined,
20388 // The duration of the animation for animate:'end'
20389 animationDuration: undefined,
20390 // A function that determines whether the node should be animated
20391 // All nodes animated by default on animate enabled
20392 // Non-animated nodes are positioned immediately when the layout starts
20393 animateFilter: function animateFilter(node, i) {
20394 return true;
20395 },
20396 // The layout animates only after this many milliseconds for animate:true
20397 // (prevents flashing on fast runs)
20398 animationThreshold: 250,
20399 // Number of iterations between consecutive screen positions update
20400 refresh: 20,
20401 // Whether to fit the network view after when done
20402 fit: true,
20403 // Padding on fit
20404 padding: 30,
20405 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20406 boundingBox: undefined,
20407 // Excludes the label when calculating node bounding boxes for the layout algorithm
20408 nodeDimensionsIncludeLabels: false,
20409 // Randomize the initial positions of the nodes (true) or use existing positions (false)
20410 randomize: false,
20411 // Extra spacing between components in non-compound graphs
20412 componentSpacing: 40,
20413 // Node repulsion (non overlapping) multiplier
20414 nodeRepulsion: function nodeRepulsion(node) {
20415 return 2048;
20416 },
20417 // Node repulsion (overlapping) multiplier
20418 nodeOverlap: 4,
20419 // Ideal edge (non nested) length
20420 idealEdgeLength: function idealEdgeLength(edge) {
20421 return 32;
20422 },
20423 // Divisor to compute edge forces
20424 edgeElasticity: function edgeElasticity(edge) {
20425 return 32;
20426 },
20427 // Nesting factor (multiplier) to compute ideal edge length for nested edges
20428 nestingFactor: 1.2,
20429 // Gravity force (constant)
20430 gravity: 1,
20431 // Maximum number of iterations to perform
20432 numIter: 1000,
20433 // Initial temperature (maximum node displacement)
20434 initialTemp: 1000,
20435 // Cooling factor (how the temperature is reduced between consecutive iterations
20436 coolingFactor: 0.99,
20437 // Lower temperature threshold (below this point the layout will end)
20438 minTemp: 1.0
20439 };
20440 /**
20441 * @brief : constructor
20442 * @arg options : object containing layout options
20443 */
20444
20445 function CoseLayout(options) {
20446 this.options = extend({}, defaults$c, options);
20447 this.options.layout = this;
20448 }
20449 /**
20450 * @brief : runs the layout
20451 */
20452
20453
20454 CoseLayout.prototype.run = function () {
20455 var options = this.options;
20456 var cy = options.cy;
20457 var layout = this;
20458 layout.stopped = false;
20459
20460 if (options.animate === true || options.animate === false) {
20461 layout.emit({
20462 type: 'layoutstart',
20463 layout: layout
20464 });
20465 } // Set DEBUG - Global variable
20466
20467
20468 if (true === options.debug) {
20469 DEBUG = true;
20470 } else {
20471 DEBUG = false;
20472 } // Initialize layout info
20473
20474
20475 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
20476
20477 if (DEBUG) {
20478 printLayoutInfo(layoutInfo);
20479 } // If required, randomize node positions
20480
20481
20482 if (options.randomize) {
20483 randomizePositions(layoutInfo);
20484 }
20485
20486 var startTime = performanceNow();
20487
20488 var refresh = function refresh() {
20489 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
20490
20491 if (true === options.fit) {
20492 cy.fit(options.padding);
20493 }
20494 };
20495
20496 var mainLoop = function mainLoop(i) {
20497 if (layout.stopped || i >= options.numIter) {
20498 // logDebug("Layout manually stopped. Stopping computation in step " + i);
20499 return false;
20500 } // Do one step in the phisical simulation
20501
20502
20503 step$1(layoutInfo, options); // Update temperature
20504
20505 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
20506
20507 if (layoutInfo.temperature < options.minTemp) {
20508 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
20509 return false;
20510 }
20511
20512 return true;
20513 };
20514
20515 var done = function done() {
20516 if (options.animate === true || options.animate === false) {
20517 refresh(); // Layout has finished
20518
20519 layout.one('layoutstop', options.stop);
20520 layout.emit({
20521 type: 'layoutstop',
20522 layout: layout
20523 });
20524 } else {
20525 var nodes = options.eles.nodes();
20526 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20527 nodes.layoutPositions(layout, options, getScaledPos);
20528 }
20529 };
20530
20531 var i = 0;
20532 var loopRet = true;
20533
20534 if (options.animate === true) {
20535 var frame = function frame() {
20536 var f = 0;
20537
20538 while (loopRet && f < options.refresh) {
20539 loopRet = mainLoop(i);
20540 i++;
20541 f++;
20542 }
20543
20544 if (!loopRet) {
20545 // it's done
20546 separateComponents(layoutInfo, options);
20547 done();
20548 } else {
20549 var now = performanceNow();
20550
20551 if (now - startTime >= options.animationThreshold) {
20552 refresh();
20553 }
20554
20555 requestAnimationFrame(frame);
20556 }
20557 };
20558
20559 frame();
20560 } else {
20561 while (loopRet) {
20562 loopRet = mainLoop(i);
20563 i++;
20564 }
20565
20566 separateComponents(layoutInfo, options);
20567 done();
20568 }
20569
20570 return this; // chaining
20571 };
20572 /**
20573 * @brief : called on continuous layouts to stop them before they finish
20574 */
20575
20576
20577 CoseLayout.prototype.stop = function () {
20578 this.stopped = true;
20579
20580 if (this.thread) {
20581 this.thread.stop();
20582 }
20583
20584 this.emit('layoutstop');
20585 return this; // chaining
20586 };
20587
20588 CoseLayout.prototype.destroy = function () {
20589 if (this.thread) {
20590 this.thread.stop();
20591 }
20592
20593 return this; // chaining
20594 };
20595 /**
20596 * @brief : Creates an object which is contains all the data
20597 * used in the layout process
20598 * @arg cy : cytoscape.js object
20599 * @return : layoutInfo object initialized
20600 */
20601
20602
20603 var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20604 // Shortcut
20605 var edges = options.eles.edges();
20606 var nodes = options.eles.nodes();
20607 var layoutInfo = {
20608 isCompound: cy.hasCompoundNodes(),
20609 layoutNodes: [],
20610 idToIndex: {},
20611 nodeSize: nodes.size(),
20612 graphSet: [],
20613 indexToGraph: [],
20614 layoutEdges: [],
20615 edgeSize: edges.size(),
20616 temperature: options.initialTemp,
20617 clientWidth: cy.width(),
20618 clientHeight: cy.width(),
20619 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
20620 x1: 0,
20621 y1: 0,
20622 w: cy.width(),
20623 h: cy.height()
20624 })
20625 };
20626 var components = options.eles.components();
20627 var id2cmptId = {};
20628
20629 for (var i = 0; i < components.length; i++) {
20630 var component = components[i];
20631
20632 for (var j = 0; j < component.length; j++) {
20633 var node = component[j];
20634 id2cmptId[node.id()] = i;
20635 }
20636 } // Iterate over all nodes, creating layout nodes
20637
20638
20639 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20640 var n = nodes[i];
20641 var nbb = n.layoutDimensions(options);
20642 var tempNode = {};
20643 tempNode.isLocked = n.locked();
20644 tempNode.id = n.data('id');
20645 tempNode.parentId = n.data('parent');
20646 tempNode.cmptId = id2cmptId[n.id()];
20647 tempNode.children = [];
20648 tempNode.positionX = n.position('x');
20649 tempNode.positionY = n.position('y');
20650 tempNode.offsetX = 0;
20651 tempNode.offsetY = 0;
20652 tempNode.height = nbb.w;
20653 tempNode.width = nbb.h;
20654 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20655 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20656 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20657 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20658 tempNode.padLeft = parseFloat(n.style('padding'));
20659 tempNode.padRight = parseFloat(n.style('padding'));
20660 tempNode.padTop = parseFloat(n.style('padding'));
20661 tempNode.padBottom = parseFloat(n.style('padding')); // forces
20662
20663 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
20664
20665 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
20666
20667 layoutInfo.idToIndex[tempNode.id] = i;
20668 } // Inline implementation of a queue, used for traversing the graph in BFS order
20669
20670
20671 var queue = [];
20672 var start = 0; // Points to the start the queue
20673
20674 var end = -1; // Points to the end of the queue
20675
20676 var tempGraph = []; // Second pass to add child information and
20677 // initialize queue for hierarchical traversal
20678
20679 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20680 var n = layoutInfo.layoutNodes[i];
20681 var p_id = n.parentId; // Check if node n has a parent node
20682
20683 if (null != p_id) {
20684 // Add node Id to parent's list of children
20685 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20686 } else {
20687 // If a node doesn't have a parent, then it's in the root graph
20688 queue[++end] = n.id;
20689 tempGraph.push(n.id);
20690 }
20691 } // Add root graph to graphSet
20692
20693
20694 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
20695
20696 while (start <= end) {
20697 // Get the node to visit and remove it from queue
20698 var node_id = queue[start++];
20699 var node_ix = layoutInfo.idToIndex[node_id];
20700 var node = layoutInfo.layoutNodes[node_ix];
20701 var children = node.children;
20702
20703 if (children.length > 0) {
20704 // Add children nodes as a new graph to graph set
20705 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
20706
20707 for (var i = 0; i < children.length; i++) {
20708 queue[++end] = children[i];
20709 }
20710 }
20711 } // Create indexToGraph map
20712
20713
20714 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20715 var graph = layoutInfo.graphSet[i];
20716
20717 for (var j = 0; j < graph.length; j++) {
20718 var index = layoutInfo.idToIndex[graph[j]];
20719 layoutInfo.indexToGraph[index] = i;
20720 }
20721 } // Iterate over all edges, creating Layout Edges
20722
20723
20724 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20725 var e = edges[i];
20726 var tempEdge = {};
20727 tempEdge.id = e.data('id');
20728 tempEdge.sourceId = e.data('source');
20729 tempEdge.targetId = e.data('target'); // Compute ideal length
20730
20731 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20732 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
20733
20734 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20735 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20736 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20737 var targetGraph = layoutInfo.indexToGraph[targetIx];
20738
20739 if (sourceGraph != targetGraph) {
20740 // Find lowest common graph ancestor
20741 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
20742
20743 var lcaGraph = layoutInfo.graphSet[lca];
20744 var depth = 0; // Source depth
20745
20746 var tempNode = layoutInfo.layoutNodes[sourceIx];
20747
20748 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20749 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20750 depth++;
20751 } // Target depth
20752
20753
20754 tempNode = layoutInfo.layoutNodes[targetIx];
20755
20756 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20757 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20758 depth++;
20759 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20760 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20761 // ". Depth: " + depth);
20762 // Update idealLength
20763
20764
20765 idealLength *= depth * options.nestingFactor;
20766 }
20767
20768 tempEdge.idealLength = idealLength;
20769 tempEdge.elasticity = elasticity;
20770 layoutInfo.layoutEdges.push(tempEdge);
20771 } // Finally, return layoutInfo object
20772
20773
20774 return layoutInfo;
20775 };
20776 /**
20777 * @brief : This function finds the index of the lowest common
20778 * graph ancestor between 2 nodes in the subtree
20779 * (from the graph hierarchy induced tree) whose
20780 * root is graphIx
20781 *
20782 * @arg node1: node1's ID
20783 * @arg node2: node2's ID
20784 * @arg layoutInfo: layoutInfo object
20785 *
20786 */
20787
20788
20789 var findLCA = function findLCA(node1, node2, layoutInfo) {
20790 // Find their common ancester, starting from the root graph
20791 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20792
20793 if (2 > res.count) {
20794 // If aux function couldn't find the common ancester,
20795 // then it is the root graph
20796 return 0;
20797 } else {
20798 return res.graph;
20799 }
20800 };
20801 /**
20802 * @brief : Auxiliary function used for LCA computation
20803 *
20804 * @arg node1 : node1's ID
20805 * @arg node2 : node2's ID
20806 * @arg graphIx : subgraph index
20807 * @arg layoutInfo : layoutInfo object
20808 *
20809 * @return : object of the form {count: X, graph: Y}, where:
20810 * X is the number of ancesters (max: 2) found in
20811 * graphIx (and it's subgraphs),
20812 * Y is the graph index of the lowest graph containing
20813 * all X nodes
20814 */
20815
20816
20817 var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20818 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20819
20820 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20821 return {
20822 count: 2,
20823 graph: graphIx
20824 };
20825 } // Make recursive calls for all subgraphs
20826
20827
20828 var c = 0;
20829
20830 for (var i = 0; i < graph.length; i++) {
20831 var nodeId = graph[i];
20832 var nodeIx = layoutInfo.idToIndex[nodeId];
20833 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20834
20835 if (0 === children.length) {
20836 continue;
20837 }
20838
20839 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20840 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20841
20842 if (0 === result.count) {
20843 // Neither node1 nor node2 are present in this subgraph
20844 continue;
20845 } else if (1 === result.count) {
20846 // One of (node1, node2) is present in this subgraph
20847 c++;
20848
20849 if (2 === c) {
20850 // We've already found both nodes, no need to keep searching
20851 break;
20852 }
20853 } else {
20854 // Both nodes are present in this subgraph
20855 return result;
20856 }
20857 }
20858
20859 return {
20860 count: c,
20861 graph: graphIx
20862 };
20863 };
20864 /**
20865 * @brief: printsLayoutInfo into js console
20866 * Only used for debbuging
20867 */
20868
20869
20870 if (false) {
20871 var printLayoutInfo;
20872 }
20873 /**
20874 * @brief : Randomizes the position of all nodes
20875 */
20876
20877
20878 var randomizePositions = function randomizePositions(layoutInfo, cy) {
20879 var width = layoutInfo.clientWidth;
20880 var height = layoutInfo.clientHeight;
20881
20882 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20883 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20884
20885 if (0 === n.children.length && !n.isLocked) {
20886 n.positionX = Math.random() * width;
20887 n.positionY = Math.random() * height;
20888 }
20889 }
20890 };
20891
20892 var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20893 var bb = layoutInfo.boundingBox;
20894 var coseBB = {
20895 x1: Infinity,
20896 x2: -Infinity,
20897 y1: Infinity,
20898 y2: -Infinity
20899 };
20900
20901 if (options.boundingBox) {
20902 nodes.forEach(function (node) {
20903 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20904 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20905 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20906 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20907 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20908 });
20909 coseBB.w = coseBB.x2 - coseBB.x1;
20910 coseBB.h = coseBB.y2 - coseBB.y1;
20911 }
20912
20913 return function (ele, i) {
20914 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20915
20916 if (options.boundingBox) {
20917 // then add extra bounding box constraint
20918 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20919 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20920 return {
20921 x: bb.x1 + pctX * bb.w,
20922 y: bb.y1 + pctY * bb.h
20923 };
20924 } else {
20925 return {
20926 x: lnode.positionX,
20927 y: lnode.positionY
20928 };
20929 }
20930 };
20931 };
20932 /**
20933 * @brief : Updates the positions of nodes in the network
20934 * @arg layoutInfo : LayoutInfo object
20935 * @arg cy : Cytoscape object
20936 * @arg options : Layout options
20937 */
20938
20939
20940 var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20941 // var s = 'Refreshing positions';
20942 // logDebug(s);
20943 var layout = options.layout;
20944 var nodes = options.eles.nodes();
20945 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20946 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20947
20948 if (true !== layoutInfo.ready) {
20949 // s = 'Triggering layoutready';
20950 // logDebug(s);
20951 layoutInfo.ready = true;
20952 layout.one('layoutready', options.ready);
20953 layout.emit({
20954 type: 'layoutready',
20955 layout: this
20956 });
20957 }
20958 };
20959 /**
20960 * @brief : Logs a debug message in JS console, if DEBUG is ON
20961 */
20962 // var logDebug = function(text) {
20963 // if (DEBUG) {
20964 // console.debug(text);
20965 // }
20966 // };
20967
20968 /**
20969 * @brief : Performs one iteration of the physical simulation
20970 * @arg layoutInfo : LayoutInfo object already initialized
20971 * @arg cy : Cytoscape object
20972 * @arg options : Layout options
20973 */
20974
20975
20976 var step$1 = function step(layoutInfo, options, _step) {
20977 // var s = "\n\n###############################";
20978 // s += "\nSTEP: " + step;
20979 // s += "\n###############################\n";
20980 // logDebug(s);
20981 // Calculate node repulsions
20982 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20983
20984 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20985
20986 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20987
20988 propagateForces(layoutInfo); // Update positions based on calculated forces
20989
20990 updatePositions(layoutInfo);
20991 };
20992 /**
20993 * @brief : Computes the node repulsion forces
20994 */
20995
20996
20997 var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20998 // Go through each of the graphs in graphSet
20999 // Nodes only repel each other if they belong to the same graph
21000 // var s = 'calculateNodeForces';
21001 // logDebug(s);
21002 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
21003 var graph = layoutInfo.graphSet[i];
21004 var numNodes = graph.length; // s = "Set: " + graph.toString();
21005 // logDebug(s);
21006 // Now get all the pairs of nodes
21007 // Only get each pair once, (A, B) = (B, A)
21008
21009 for (var j = 0; j < numNodes; j++) {
21010 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
21011
21012 for (var k = j + 1; k < numNodes; k++) {
21013 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
21014 nodeRepulsion(node1, node2, layoutInfo, options);
21015 }
21016 }
21017 }
21018 };
21019
21020 var randomDistance = function randomDistance(max) {
21021 return -max + 2 * max * Math.random();
21022 };
21023 /**
21024 * @brief : Compute the node repulsion forces between a pair of nodes
21025 */
21026
21027
21028 var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
21029 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
21030 var cmptId1 = node1.cmptId;
21031 var cmptId2 = node2.cmptId;
21032
21033 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
21034 return;
21035 } // Get direction of line connecting both node centers
21036
21037
21038 var directionX = node2.positionX - node1.positionX;
21039 var directionY = node2.positionY - node1.positionY;
21040 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
21041 // If both centers are the same, apply a random force
21042
21043 if (0 === directionX && 0 === directionY) {
21044 directionX = randomDistance(maxRandDist);
21045 directionY = randomDistance(maxRandDist);
21046 }
21047
21048 var overlap = nodesOverlap(node1, node2, directionX, directionY);
21049
21050 if (overlap > 0) {
21051 // s += "\nNodes DO overlap.";
21052 // s += "\nOverlap: " + overlap;
21053 // If nodes overlap, repulsion force is proportional
21054 // to the overlap
21055 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
21056
21057 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
21058
21059 var forceX = force * directionX / distance;
21060 var forceY = force * directionY / distance;
21061 } else {
21062 // s += "\nNodes do NOT overlap.";
21063 // If there's no overlap, force is inversely proportional
21064 // to squared distance
21065 // Get clipping points for both nodes
21066 var point1 = findClippingPoint(node1, directionX, directionY);
21067 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
21068
21069 var distanceX = point2.x - point1.x;
21070 var distanceY = point2.y - point1.y;
21071 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
21072 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
21073 // Compute the module and components of the force vector
21074
21075 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
21076 var forceX = force * distanceX / distance;
21077 var forceY = force * distanceY / distance;
21078 } // Apply force
21079
21080
21081 if (!node1.isLocked) {
21082 node1.offsetX -= forceX;
21083 node1.offsetY -= forceY;
21084 }
21085
21086 if (!node2.isLocked) {
21087 node2.offsetX += forceX;
21088 node2.offsetY += forceY;
21089 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
21090 // logDebug(s);
21091
21092
21093 return;
21094 };
21095 /**
21096 * @brief : Determines whether two nodes overlap or not
21097 * @return : Amount of overlapping (0 => no overlap)
21098 */
21099
21100
21101 var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
21102 if (dX > 0) {
21103 var overlapX = node1.maxX - node2.minX;
21104 } else {
21105 var overlapX = node2.maxX - node1.minX;
21106 }
21107
21108 if (dY > 0) {
21109 var overlapY = node1.maxY - node2.minY;
21110 } else {
21111 var overlapY = node2.maxY - node1.minY;
21112 }
21113
21114 if (overlapX >= 0 && overlapY >= 0) {
21115 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
21116 } else {
21117 return 0;
21118 }
21119 };
21120 /**
21121 * @brief : Finds the point in which an edge (direction dX, dY) intersects
21122 * the rectangular bounding box of it's source/target node
21123 */
21124
21125
21126 var findClippingPoint = function findClippingPoint(node, dX, dY) {
21127 // Shorcuts
21128 var X = node.positionX;
21129 var Y = node.positionY;
21130 var H = node.height || 1;
21131 var W = node.width || 1;
21132 var dirSlope = dY / dX;
21133 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
21134 // " . Height: " + H + ", Width: " + W +
21135 // "\nDirection " + dX + ", " + dY;
21136 //
21137 // Compute intersection
21138
21139 var res = {}; // Case: Vertical direction (up)
21140
21141 if (0 === dX && 0 < dY) {
21142 res.x = X; // s += "\nUp direction";
21143
21144 res.y = Y + H / 2;
21145 return res;
21146 } // Case: Vertical direction (down)
21147
21148
21149 if (0 === dX && 0 > dY) {
21150 res.x = X;
21151 res.y = Y + H / 2; // s += "\nDown direction";
21152
21153 return res;
21154 } // Case: Intersects the right border
21155
21156
21157 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
21158 res.x = X + W / 2;
21159 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
21160
21161 return res;
21162 } // Case: Intersects the left border
21163
21164
21165 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
21166 res.x = X - W / 2;
21167 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
21168
21169 return res;
21170 } // Case: Intersects the top border
21171
21172
21173 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
21174 res.x = X + H * dX / 2 / dY;
21175 res.y = Y + H / 2; // s += "\nTop border";
21176
21177 return res;
21178 } // Case: Intersects the bottom border
21179
21180
21181 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
21182 res.x = X - H * dX / 2 / dY;
21183 res.y = Y - H / 2; // s += "\nBottom border";
21184
21185 return res;
21186 } // s += "\nClipping point found at " + res.x + ", " + res.y;
21187 // logDebug(s);
21188
21189
21190 return res;
21191 };
21192 /**
21193 * @brief : Calculates all edge forces
21194 */
21195
21196
21197 var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
21198 // Iterate over all edges
21199 for (var i = 0; i < layoutInfo.edgeSize; i++) {
21200 // Get edge, source & target nodes
21201 var edge = layoutInfo.layoutEdges[i];
21202 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
21203 var source = layoutInfo.layoutNodes[sourceIx];
21204 var targetIx = layoutInfo.idToIndex[edge.targetId];
21205 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
21206
21207 var directionX = target.positionX - source.positionX;
21208 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
21209 // A random force has already been applied as node repulsion
21210
21211 if (0 === directionX && 0 === directionY) {
21212 continue;
21213 } // Get clipping points for both nodes
21214
21215
21216 var point1 = findClippingPoint(source, directionX, directionY);
21217 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
21218 var lx = point2.x - point1.x;
21219 var ly = point2.y - point1.y;
21220 var l = Math.sqrt(lx * lx + ly * ly);
21221 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
21222
21223 if (0 !== l) {
21224 var forceX = force * lx / l;
21225 var forceY = force * ly / l;
21226 } else {
21227 var forceX = 0;
21228 var forceY = 0;
21229 } // Add this force to target and source nodes
21230
21231
21232 if (!source.isLocked) {
21233 source.offsetX += forceX;
21234 source.offsetY += forceY;
21235 }
21236
21237 if (!target.isLocked) {
21238 target.offsetX -= forceX;
21239 target.offsetY -= forceY;
21240 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
21241 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
21242 // logDebug(s);
21243
21244 }
21245 };
21246 /**
21247 * @brief : Computes gravity forces for all nodes
21248 */
21249
21250
21251 var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
21252 var distThreshold = 1; // var s = 'calculateGravityForces';
21253 // logDebug(s);
21254
21255 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
21256 var graph = layoutInfo.graphSet[i];
21257 var numNodes = graph.length; // s = "Set: " + graph.toString();
21258 // logDebug(s);
21259 // Compute graph center
21260
21261 if (0 === i) {
21262 var centerX = layoutInfo.clientHeight / 2;
21263 var centerY = layoutInfo.clientWidth / 2;
21264 } else {
21265 // Get Parent node for this graph, and use its position as center
21266 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
21267 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
21268 var centerX = parent.positionX;
21269 var centerY = parent.positionY;
21270 } // s = "Center found at: " + centerX + ", " + centerY;
21271 // logDebug(s);
21272 // Apply force to all nodes in graph
21273
21274
21275 for (var j = 0; j < numNodes; j++) {
21276 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
21277
21278 if (node.isLocked) {
21279 continue;
21280 }
21281
21282 var dx = centerX - node.positionX;
21283 var dy = centerY - node.positionY;
21284 var d = Math.sqrt(dx * dx + dy * dy);
21285
21286 if (d > distThreshold) {
21287 var fx = options.gravity * dx / d;
21288 var fy = options.gravity * dy / d;
21289 node.offsetX += fx;
21290 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
21291 } // s += ": skypped since it's too close to center";
21292 // logDebug(s);
21293
21294 }
21295 }
21296 };
21297 /**
21298 * @brief : This function propagates the existing offsets from
21299 * parent nodes to its descendents.
21300 * @arg layoutInfo : layoutInfo Object
21301 * @arg cy : cytoscape Object
21302 * @arg options : Layout options
21303 */
21304
21305
21306 var propagateForces = function propagateForces(layoutInfo, options) {
21307 // Inline implementation of a queue, used for traversing the graph in BFS order
21308 var queue = [];
21309 var start = 0; // Points to the start the queue
21310
21311 var end = -1; // Points to the end of the queue
21312 // logDebug('propagateForces');
21313 // Start by visiting the nodes in the root graph
21314
21315 queue.push.apply(queue, layoutInfo.graphSet[0]);
21316 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
21317
21318 while (start <= end) {
21319 // Get the node to visit and remove it from queue
21320 var nodeId = queue[start++];
21321 var nodeIndex = layoutInfo.idToIndex[nodeId];
21322 var node = layoutInfo.layoutNodes[nodeIndex];
21323 var children = node.children; // We only need to process the node if it's compound
21324
21325 if (0 < children.length && !node.isLocked) {
21326 var offX = node.offsetX;
21327 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
21328 // ". OffsetX: " + offX + ". OffsetY: " + offY;
21329 // s += "\n Children: " + children.toString();
21330 // logDebug(s);
21331
21332 for (var i = 0; i < children.length; i++) {
21333 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
21334
21335 childNode.offsetX += offX;
21336 childNode.offsetY += offY; // Add children to queue to be visited
21337
21338 queue[++end] = children[i];
21339 } // Reset parent offsets
21340
21341
21342 node.offsetX = 0;
21343 node.offsetY = 0;
21344 }
21345 }
21346 };
21347 /**
21348 * @brief : Updates the layout model positions, based on
21349 * the accumulated forces
21350 */
21351
21352
21353 var updatePositions = function updatePositions(layoutInfo, options) {
21354 // var s = 'Updating positions';
21355 // logDebug(s);
21356 // Reset boundaries for compound nodes
21357 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21358 var n = layoutInfo.layoutNodes[i];
21359
21360 if (0 < n.children.length) {
21361 // logDebug("Resetting boundaries of compound node: " + n.id);
21362 n.maxX = undefined;
21363 n.minX = undefined;
21364 n.maxY = undefined;
21365 n.minY = undefined;
21366 }
21367 }
21368
21369 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21370 var n = layoutInfo.layoutNodes[i];
21371
21372 if (0 < n.children.length || n.isLocked) {
21373 // No need to set compound or locked node position
21374 // logDebug("Skipping position update of node: " + n.id);
21375 continue;
21376 } // s = "Node: " + n.id + " Previous position: (" +
21377 // n.positionX + ", " + n.positionY + ").";
21378 // Limit displacement in order to improve stability
21379
21380
21381 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
21382 n.positionX += tempForce.x;
21383 n.positionY += tempForce.y;
21384 n.offsetX = 0;
21385 n.offsetY = 0;
21386 n.minX = n.positionX - n.width;
21387 n.maxX = n.positionX + n.width;
21388 n.minY = n.positionY - n.height;
21389 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
21390 // logDebug(s);
21391 // Update ancestry boudaries
21392
21393 updateAncestryBoundaries(n, layoutInfo);
21394 } // Update size, position of compund nodes
21395
21396
21397 for (var i = 0; i < layoutInfo.nodeSize; i++) {
21398 var n = layoutInfo.layoutNodes[i];
21399
21400 if (0 < n.children.length && !n.isLocked) {
21401 n.positionX = (n.maxX + n.minX) / 2;
21402 n.positionY = (n.maxY + n.minY) / 2;
21403 n.width = n.maxX - n.minX;
21404 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
21405 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
21406 // s += "\nWidth: " + n.width + ", Height: " + n.height;
21407 // logDebug(s);
21408 }
21409 }
21410 };
21411 /**
21412 * @brief : Limits a force (forceX, forceY) to be not
21413 * greater (in modulo) than max.
21414 8 Preserves force direction.
21415 */
21416
21417
21418 var limitForce = function limitForce(forceX, forceY, max) {
21419 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
21420 var force = Math.sqrt(forceX * forceX + forceY * forceY);
21421
21422 if (force > max) {
21423 var res = {
21424 x: max * forceX / force,
21425 y: max * forceY / force
21426 };
21427 } else {
21428 var res = {
21429 x: forceX,
21430 y: forceY
21431 };
21432 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
21433 // logDebug(s);
21434
21435
21436 return res;
21437 };
21438 /**
21439 * @brief : Function used for keeping track of compound node
21440 * sizes, since they should bound all their subnodes.
21441 */
21442
21443
21444 var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
21445 // var s = "Propagating new position/size of node " + node.id;
21446 var parentId = node.parentId;
21447
21448 if (null == parentId) {
21449 // If there's no parent, we are done
21450 // s += ". No parent node.";
21451 // logDebug(s);
21452 return;
21453 } // Get Parent Node
21454
21455
21456 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
21457 var flag = false; // MaxX
21458
21459 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
21460 p.maxX = node.maxX + p.padRight;
21461 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
21462 } // MinX
21463
21464
21465 if (null == p.minX || node.minX - p.padLeft < p.minX) {
21466 p.minX = node.minX - p.padLeft;
21467 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
21468 } // MaxY
21469
21470
21471 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
21472 p.maxY = node.maxY + p.padBottom;
21473 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
21474 } // MinY
21475
21476
21477 if (null == p.minY || node.minY - p.padTop < p.minY) {
21478 p.minY = node.minY - p.padTop;
21479 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
21480 } // If updated boundaries, propagate changes upward
21481
21482
21483 if (flag) {
21484 // logDebug(s);
21485 return updateAncestryBoundaries(p, layoutInfo);
21486 } // s += ". No changes in boundaries/position of parent node " + p.id;
21487 // logDebug(s);
21488
21489
21490 return;
21491 };
21492
21493 var separateComponents = function separateComponents(layoutInfo, options) {
21494 var nodes = layoutInfo.layoutNodes;
21495 var components = [];
21496
21497 for (var i = 0; i < nodes.length; i++) {
21498 var node = nodes[i];
21499 var cid = node.cmptId;
21500 var component = components[cid] = components[cid] || [];
21501 component.push(node);
21502 }
21503
21504 var totalA = 0;
21505
21506 for (var i = 0; i < components.length; i++) {
21507 var c = components[i];
21508
21509 if (!c) {
21510 continue;
21511 }
21512
21513 c.x1 = Infinity;
21514 c.x2 = -Infinity;
21515 c.y1 = Infinity;
21516 c.y2 = -Infinity;
21517
21518 for (var j = 0; j < c.length; j++) {
21519 var n = c[j];
21520 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
21521 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
21522 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
21523 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
21524 }
21525
21526 c.w = c.x2 - c.x1;
21527 c.h = c.y2 - c.y1;
21528 totalA += c.w * c.h;
21529 }
21530
21531 components.sort(function (c1, c2) {
21532 return c2.w * c2.h - c1.w * c1.h;
21533 });
21534 var x = 0;
21535 var y = 0;
21536 var usedW = 0;
21537 var rowH = 0;
21538 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
21539
21540 for (var i = 0; i < components.length; i++) {
21541 var c = components[i];
21542
21543 if (!c) {
21544 continue;
21545 }
21546
21547 for (var j = 0; j < c.length; j++) {
21548 var n = c[j];
21549
21550 if (!n.isLocked) {
21551 n.positionX += x - c.x1;
21552 n.positionY += y - c.y1;
21553 }
21554 }
21555
21556 x += c.w + options.componentSpacing;
21557 usedW += c.w + options.componentSpacing;
21558 rowH = Math.max(rowH, c.h);
21559
21560 if (usedW > maxRowW) {
21561 y += rowH + options.componentSpacing;
21562 x = 0;
21563 usedW = 0;
21564 rowH = 0;
21565 }
21566 }
21567 };
21568
21569 var defaults$d = {
21570 fit: true,
21571 // whether to fit the viewport to the graph
21572 padding: 30,
21573 // padding used on fit
21574 boundingBox: undefined,
21575 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21576 avoidOverlap: true,
21577 // prevents node overlap, may overflow boundingBox if not enough space
21578 avoidOverlapPadding: 10,
21579 // extra spacing around nodes when avoidOverlap: true
21580 nodeDimensionsIncludeLabels: false,
21581 // Excludes the label when calculating node bounding boxes for the layout algorithm
21582 spacingFactor: undefined,
21583 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21584 condense: false,
21585 // uses all available space on false, uses minimal space on true
21586 rows: undefined,
21587 // force num of rows in the grid
21588 cols: undefined,
21589 // force num of columns in the grid
21590 position: function position(node) {},
21591 // returns { row, col } for element
21592 sort: undefined,
21593 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21594 animate: false,
21595 // whether to transition the node positions
21596 animationDuration: 500,
21597 // duration of animation in ms if enabled
21598 animationEasing: undefined,
21599 // easing of animation if enabled
21600 animateFilter: function animateFilter(node, i) {
21601 return true;
21602 },
21603 // 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
21604 ready: undefined,
21605 // callback on layoutready
21606 stop: undefined,
21607 // callback on layoutstop
21608 transform: function transform(node, position) {
21609 return position;
21610 } // transform a given node position. Useful for changing flow direction in discrete layouts
21611
21612 };
21613
21614 function GridLayout(options) {
21615 this.options = extend({}, defaults$d, options);
21616 }
21617
21618 GridLayout.prototype.run = function () {
21619 var params = this.options;
21620 var options = params;
21621 var cy = params.cy;
21622 var eles = options.eles;
21623 var nodes = eles.nodes().not(':parent');
21624
21625 if (options.sort) {
21626 nodes = nodes.sort(options.sort);
21627 }
21628
21629 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21630 x1: 0,
21631 y1: 0,
21632 w: cy.width(),
21633 h: cy.height()
21634 });
21635
21636 if (bb.h === 0 || bb.w === 0) {
21637 eles.nodes().layoutPositions(this, options, function (ele) {
21638 return {
21639 x: bb.x1,
21640 y: bb.y1
21641 };
21642 });
21643 } else {
21644 // width/height * splits^2 = cells where splits is number of times to split width
21645 var cells = nodes.size();
21646 var splits = Math.sqrt(cells * bb.h / bb.w);
21647 var rows = Math.round(splits);
21648 var cols = Math.round(bb.w / bb.h * splits);
21649
21650 var small = function small(val) {
21651 if (val == null) {
21652 return Math.min(rows, cols);
21653 } else {
21654 var min = Math.min(rows, cols);
21655
21656 if (min == rows) {
21657 rows = val;
21658 } else {
21659 cols = val;
21660 }
21661 }
21662 };
21663
21664 var large = function large(val) {
21665 if (val == null) {
21666 return Math.max(rows, cols);
21667 } else {
21668 var max = Math.max(rows, cols);
21669
21670 if (max == rows) {
21671 rows = val;
21672 } else {
21673 cols = val;
21674 }
21675 }
21676 };
21677
21678 var oRows = options.rows;
21679 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
21680
21681 if (oRows != null && oCols != null) {
21682 rows = oRows;
21683 cols = oCols;
21684 } else if (oRows != null && oCols == null) {
21685 rows = oRows;
21686 cols = Math.ceil(cells / rows);
21687 } else if (oRows == null && oCols != null) {
21688 cols = oCols;
21689 rows = Math.ceil(cells / cols);
21690 } // otherwise use the automatic values and adjust accordingly
21691 // if rounding was up, see if we can reduce rows or columns
21692 else if (cols * rows > cells) {
21693 var sm = small();
21694 var lg = large(); // reducing the small side takes away the most cells, so try it first
21695
21696 if ((sm - 1) * lg >= cells) {
21697 small(sm - 1);
21698 } else if ((lg - 1) * sm >= cells) {
21699 large(lg - 1);
21700 }
21701 } else {
21702 // if rounding was too low, add rows or columns
21703 while (cols * rows < cells) {
21704 var _sm = small();
21705
21706 var _lg = large(); // try to add to larger side first (adds less in multiplication)
21707
21708
21709 if ((_lg + 1) * _sm >= cells) {
21710 large(_lg + 1);
21711 } else {
21712 small(_sm + 1);
21713 }
21714 }
21715 }
21716
21717 var cellWidth = bb.w / cols;
21718 var cellHeight = bb.h / rows;
21719
21720 if (options.condense) {
21721 cellWidth = 0;
21722 cellHeight = 0;
21723 }
21724
21725 if (options.avoidOverlap) {
21726 for (var i = 0; i < nodes.length; i++) {
21727 var node = nodes[i];
21728 var pos = node._private.position;
21729
21730 if (pos.x == null || pos.y == null) {
21731 // for bb
21732 pos.x = 0;
21733 pos.y = 0;
21734 }
21735
21736 var nbb = node.layoutDimensions(options);
21737 var p = options.avoidOverlapPadding;
21738 var w = nbb.w + p;
21739 var h = nbb.h + p;
21740 cellWidth = Math.max(cellWidth, w);
21741 cellHeight = Math.max(cellHeight, h);
21742 }
21743 }
21744
21745 var cellUsed = {}; // e.g. 'c-0-2' => true
21746
21747 var used = function used(row, col) {
21748 return cellUsed['c-' + row + '-' + col] ? true : false;
21749 };
21750
21751 var use = function use(row, col) {
21752 cellUsed['c-' + row + '-' + col] = true;
21753 }; // to keep track of current cell position
21754
21755
21756 var row = 0;
21757 var col = 0;
21758
21759 var moveToNextCell = function moveToNextCell() {
21760 col++;
21761
21762 if (col >= cols) {
21763 col = 0;
21764 row++;
21765 }
21766 }; // get a cache of all the manual positions
21767
21768
21769 var id2manPos = {};
21770
21771 for (var _i = 0; _i < nodes.length; _i++) {
21772 var _node = nodes[_i];
21773 var rcPos = options.position(_node);
21774
21775 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21776 // must have at least row or col def'd
21777 var _pos = {
21778 row: rcPos.row,
21779 col: rcPos.col
21780 };
21781
21782 if (_pos.col === undefined) {
21783 // find unused col
21784 _pos.col = 0;
21785
21786 while (used(_pos.row, _pos.col)) {
21787 _pos.col++;
21788 }
21789 } else if (_pos.row === undefined) {
21790 // find unused row
21791 _pos.row = 0;
21792
21793 while (used(_pos.row, _pos.col)) {
21794 _pos.row++;
21795 }
21796 }
21797
21798 id2manPos[_node.id()] = _pos;
21799 use(_pos.row, _pos.col);
21800 }
21801 }
21802
21803 var getPos = function getPos(element, i) {
21804 var x, y;
21805
21806 if (element.locked() || element.isParent()) {
21807 return false;
21808 } // see if we have a manual position set
21809
21810
21811 var rcPos = id2manPos[element.id()];
21812
21813 if (rcPos) {
21814 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21815 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21816 } else {
21817 // otherwise set automatically
21818 while (used(row, col)) {
21819 moveToNextCell();
21820 }
21821
21822 x = col * cellWidth + cellWidth / 2 + bb.x1;
21823 y = row * cellHeight + cellHeight / 2 + bb.y1;
21824 use(row, col);
21825 moveToNextCell();
21826 }
21827
21828 return {
21829 x: x,
21830 y: y
21831 };
21832 };
21833
21834 nodes.layoutPositions(this, options, getPos);
21835 }
21836
21837 return this; // chaining
21838 };
21839
21840 var defaults$e = {
21841 ready: function ready() {},
21842 // on layoutready
21843 stop: function stop() {} // on layoutstop
21844
21845 }; // constructor
21846 // options : object containing layout options
21847
21848 function NullLayout(options) {
21849 this.options = extend({}, defaults$e, options);
21850 } // runs the layout
21851
21852
21853 NullLayout.prototype.run = function () {
21854 var options = this.options;
21855 var eles = options.eles; // elements to consider in the layout
21856
21857 var layout = this; // cy is automatically populated for us in the constructor
21858 // (disable eslint for next line as this serves as example layout code to external developers)
21859 // eslint-disable-next-line no-unused-vars
21860
21861 var cy = options.cy;
21862 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21863 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21864
21865 eles.nodes().positions(function () {
21866 return {
21867 x: 0,
21868 y: 0
21869 };
21870 }); // trigger layoutready when each node has had its position set at least once
21871
21872 layout.one('layoutready', options.ready);
21873 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21874
21875 layout.one('layoutstop', options.stop);
21876 layout.emit('layoutstop');
21877 return this; // chaining
21878 }; // called on continuous layouts to stop them before they finish
21879
21880
21881 NullLayout.prototype.stop = function () {
21882 return this; // chaining
21883 };
21884
21885 var defaults$f = {
21886 positions: undefined,
21887 // map of (node id) => (position obj); or function(node){ return somPos; }
21888 zoom: undefined,
21889 // the zoom level to set (prob want fit = false if set)
21890 pan: undefined,
21891 // the pan level to set (prob want fit = false if set)
21892 fit: true,
21893 // whether to fit to viewport
21894 padding: 30,
21895 // padding on fit
21896 animate: false,
21897 // whether to transition the node positions
21898 animationDuration: 500,
21899 // duration of animation in ms if enabled
21900 animationEasing: undefined,
21901 // easing of animation if enabled
21902 animateFilter: function animateFilter(node, i) {
21903 return true;
21904 },
21905 // 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
21906 ready: undefined,
21907 // callback on layoutready
21908 stop: undefined,
21909 // callback on layoutstop
21910 transform: function transform(node, position) {
21911 return position;
21912 } // transform a given node position. Useful for changing flow direction in discrete layouts
21913
21914 };
21915
21916 function PresetLayout(options) {
21917 this.options = extend({}, defaults$f, options);
21918 }
21919
21920 PresetLayout.prototype.run = function () {
21921 var options = this.options;
21922 var eles = options.eles;
21923 var nodes = eles.nodes();
21924 var posIsFn = fn(options.positions);
21925
21926 function getPosition(node) {
21927 if (options.positions == null) {
21928 return copyPosition(node.position());
21929 }
21930
21931 if (posIsFn) {
21932 return options.positions(node);
21933 }
21934
21935 var pos = options.positions[node._private.data.id];
21936
21937 if (pos == null) {
21938 return null;
21939 }
21940
21941 return pos;
21942 }
21943
21944 nodes.layoutPositions(this, options, function (node, i) {
21945 var position = getPosition(node);
21946
21947 if (node.locked() || position == null) {
21948 return false;
21949 }
21950
21951 return position;
21952 });
21953 return this; // chaining
21954 };
21955
21956 var defaults$g = {
21957 fit: true,
21958 // whether to fit to viewport
21959 padding: 30,
21960 // fit padding
21961 boundingBox: undefined,
21962 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21963 animate: false,
21964 // whether to transition the node positions
21965 animationDuration: 500,
21966 // duration of animation in ms if enabled
21967 animationEasing: undefined,
21968 // easing of animation if enabled
21969 animateFilter: function animateFilter(node, i) {
21970 return true;
21971 },
21972 // 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
21973 ready: undefined,
21974 // callback on layoutready
21975 stop: undefined,
21976 // callback on layoutstop
21977 transform: function transform(node, position) {
21978 return position;
21979 } // transform a given node position. Useful for changing flow direction in discrete layouts
21980
21981 };
21982
21983 function RandomLayout(options) {
21984 this.options = extend({}, defaults$g, options);
21985 }
21986
21987 RandomLayout.prototype.run = function () {
21988 var options = this.options;
21989 var cy = options.cy;
21990 var eles = options.eles;
21991 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21992 x1: 0,
21993 y1: 0,
21994 w: cy.width(),
21995 h: cy.height()
21996 });
21997
21998 var getPos = function getPos(node, i) {
21999 return {
22000 x: bb.x1 + Math.round(Math.random() * bb.w),
22001 y: bb.y1 + Math.round(Math.random() * bb.h)
22002 };
22003 };
22004
22005 eles.nodes().layoutPositions(this, options, getPos);
22006 return this; // chaining
22007 };
22008
22009 var layout = [{
22010 name: 'breadthfirst',
22011 impl: BreadthFirstLayout
22012 }, {
22013 name: 'circle',
22014 impl: CircleLayout
22015 }, {
22016 name: 'concentric',
22017 impl: ConcentricLayout
22018 }, {
22019 name: 'cose',
22020 impl: CoseLayout
22021 }, {
22022 name: 'grid',
22023 impl: GridLayout
22024 }, {
22025 name: 'null',
22026 impl: NullLayout
22027 }, {
22028 name: 'preset',
22029 impl: PresetLayout
22030 }, {
22031 name: 'random',
22032 impl: RandomLayout
22033 }];
22034
22035 function NullRenderer(options) {
22036 this.options = options;
22037 this.notifications = 0; // for testing
22038 }
22039
22040 var noop$1 = function noop() {};
22041
22042 var throwImgErr = function throwImgErr() {
22043 throw new Error('A headless instance can not render images');
22044 };
22045
22046 NullRenderer.prototype = {
22047 recalculateRenderedStyle: noop$1,
22048 notify: function notify() {
22049 this.notifications++;
22050 },
22051 init: noop$1,
22052 isHeadless: function isHeadless() {
22053 return true;
22054 },
22055 png: throwImgErr,
22056 jpg: throwImgErr
22057 };
22058
22059 var BRp = {};
22060 BRp.arrowShapeWidth = 0.3;
22061
22062 BRp.registerArrowShapes = function () {
22063 var arrowShapes = this.arrowShapes = {};
22064 var renderer = this; // Contract for arrow shapes:
22065 // 0, 0 is arrow tip
22066 // (0, 1) is direction towards node
22067 // (1, 0) is right
22068 //
22069 // functional api:
22070 // collide: check x, y in shape
22071 // roughCollide: called before collide, no false negatives
22072 // draw: draw
22073 // spacing: dist(arrowTip, nodeBoundary)
22074 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
22075
22076 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
22077 var x1 = translation.x - size / 2 - padding;
22078 var x2 = translation.x + size / 2 + padding;
22079 var y1 = translation.y - size / 2 - padding;
22080 var y2 = translation.y + size / 2 + padding;
22081 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
22082 return inside;
22083 };
22084
22085 var transform = function transform(x, y, size, angle, translation) {
22086 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
22087 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
22088 var xScaled = xRotated * size;
22089 var yScaled = yRotated * size;
22090 var xTranslated = xScaled + translation.x;
22091 var yTranslated = yScaled + translation.y;
22092 return {
22093 x: xTranslated,
22094 y: yTranslated
22095 };
22096 };
22097
22098 var transformPoints = function transformPoints(pts, size, angle, translation) {
22099 var retPts = [];
22100
22101 for (var i = 0; i < pts.length; i += 2) {
22102 var x = pts[i];
22103 var y = pts[i + 1];
22104 retPts.push(transform(x, y, size, angle, translation));
22105 }
22106
22107 return retPts;
22108 };
22109
22110 var pointsToArr = function pointsToArr(pts) {
22111 var ret = [];
22112
22113 for (var i = 0; i < pts.length; i++) {
22114 var p = pts[i];
22115 ret.push(p.x, p.y);
22116 }
22117
22118 return ret;
22119 };
22120
22121 var standardGap = function standardGap(edge) {
22122 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
22123 };
22124
22125 var defineArrowShape = function defineArrowShape(name, defn) {
22126 if (string(defn)) {
22127 defn = arrowShapes[defn];
22128 }
22129
22130 arrowShapes[name] = extend({
22131 name: name,
22132 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
22133 collide: function collide(x, y, size, angle, translation, padding) {
22134 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22135 var inside = pointInsidePolygonPoints(x, y, points);
22136 return inside;
22137 },
22138 roughCollide: bbCollide,
22139 draw: function draw(context, size, angle, translation) {
22140 var points = transformPoints(this.points, size, angle, translation);
22141 renderer.arrowShapeImpl('polygon')(context, points);
22142 },
22143 spacing: function spacing(edge) {
22144 return 0;
22145 },
22146 gap: standardGap
22147 }, defn);
22148 };
22149
22150 defineArrowShape('none', {
22151 collide: falsify,
22152 roughCollide: falsify,
22153 draw: noop,
22154 spacing: zeroify,
22155 gap: zeroify
22156 });
22157 defineArrowShape('triangle', {
22158 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
22159 });
22160 defineArrowShape('arrow', 'triangle');
22161 defineArrowShape('triangle-backcurve', {
22162 points: arrowShapes['triangle'].points,
22163 controlPoint: [0, -0.15],
22164 roughCollide: bbCollide,
22165 draw: function draw(context, size, angle, translation, edgeWidth) {
22166 var ptsTrans = transformPoints(this.points, size, angle, translation);
22167 var ctrlPt = this.controlPoint;
22168 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
22169 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
22170 },
22171 gap: function gap(edge) {
22172 return standardGap(edge) * 0.8;
22173 }
22174 });
22175 defineArrowShape('triangle-tee', {
22176 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
22177 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
22178 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22179 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22180 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
22181 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
22182 return inside;
22183 },
22184 draw: function draw(context, size, angle, translation, edgeWidth) {
22185 var triPts = transformPoints(this.points, size, angle, translation);
22186 var teePts = transformPoints(this.pointsTee, size, angle, translation);
22187 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
22188 }
22189 });
22190 defineArrowShape('circle-triangle', {
22191 radius: 0.15,
22192 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
22193 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22194 var t = translation;
22195 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
22196 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22197 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
22198 },
22199 draw: function draw(context, size, angle, translation, edgeWidth) {
22200 var triPts = transformPoints(this.pointsTr, size, angle, translation);
22201 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
22202 },
22203 spacing: function spacing(edge) {
22204 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
22205 }
22206 });
22207 defineArrowShape('triangle-cross', {
22208 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
22209 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
22210 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
22211 0.15, -0.4],
22212 crossLinePts: function crossLinePts(size, edgeWidth) {
22213 // shift points so that the distance between the cross points matches edge width
22214 var p = this.baseCrossLinePts.slice();
22215 var shiftFactor = edgeWidth / size;
22216 var y0 = 3;
22217 var y1 = 5;
22218 p[y0] = p[y0] - shiftFactor;
22219 p[y1] = p[y1] - shiftFactor;
22220 return p;
22221 },
22222 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22223 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
22224 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
22225 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
22226 return inside;
22227 },
22228 draw: function draw(context, size, angle, translation, edgeWidth) {
22229 var triPts = transformPoints(this.points, size, angle, translation);
22230 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
22231 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
22232 }
22233 });
22234 defineArrowShape('vee', {
22235 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
22236 gap: function gap(edge) {
22237 return standardGap(edge) * 0.525;
22238 }
22239 });
22240 defineArrowShape('circle', {
22241 radius: 0.15,
22242 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
22243 var t = translation;
22244 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
22245 return inside;
22246 },
22247 draw: function draw(context, size, angle, translation, edgeWidth) {
22248 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
22249 },
22250 spacing: function spacing(edge) {
22251 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
22252 }
22253 });
22254 defineArrowShape('tee', {
22255 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
22256 spacing: function spacing(edge) {
22257 return 1;
22258 },
22259 gap: function gap(edge) {
22260 return 1;
22261 }
22262 });
22263 defineArrowShape('square', {
22264 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
22265 });
22266 defineArrowShape('diamond', {
22267 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
22268 gap: function gap(edge) {
22269 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
22270 }
22271 });
22272 defineArrowShape('chevron', {
22273 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
22274 gap: function gap(edge) {
22275 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
22276 }
22277 });
22278 };
22279
22280 var BRp$1 = {}; // Project mouse
22281
22282 BRp$1.projectIntoViewport = function (clientX, clientY) {
22283 var cy = this.cy;
22284 var offsets = this.findContainerClientCoords();
22285 var offsetLeft = offsets[0];
22286 var offsetTop = offsets[1];
22287 var scale = offsets[4];
22288 var pan = cy.pan();
22289 var zoom = cy.zoom();
22290 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
22291 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
22292 return [x, y];
22293 };
22294
22295 BRp$1.findContainerClientCoords = function () {
22296 if (this.containerBB) {
22297 return this.containerBB;
22298 }
22299
22300 var container = this.container;
22301 var rect = container.getBoundingClientRect();
22302 var style = window$1.getComputedStyle(container);
22303
22304 var styleValue = function styleValue(name) {
22305 return parseFloat(style.getPropertyValue(name));
22306 };
22307
22308 var padding = {
22309 left: styleValue('padding-left'),
22310 right: styleValue('padding-right'),
22311 top: styleValue('padding-top'),
22312 bottom: styleValue('padding-bottom')
22313 };
22314 var border = {
22315 left: styleValue('border-left-width'),
22316 right: styleValue('border-right-width'),
22317 top: styleValue('border-top-width'),
22318 bottom: styleValue('border-bottom-width')
22319 };
22320 var clientWidth = container.clientWidth;
22321 var clientHeight = container.clientHeight;
22322 var paddingHor = padding.left + padding.right;
22323 var paddingVer = padding.top + padding.bottom;
22324 var borderHor = border.left + border.right;
22325 var scale = rect.width / (clientWidth + borderHor);
22326 var unscaledW = clientWidth - paddingHor;
22327 var unscaledH = clientHeight - paddingVer;
22328 var left = rect.left + padding.left + border.left;
22329 var top = rect.top + padding.top + border.top;
22330 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
22331 };
22332
22333 BRp$1.invalidateContainerClientCoordsCache = function () {
22334 this.containerBB = null;
22335 };
22336
22337 BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
22338 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
22339 };
22340
22341 BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
22342 var self = this;
22343 var r = this;
22344 var eles = r.getCachedZSortedEles();
22345 var near = []; // 1 node max, 1 edge max
22346
22347 var zoom = r.cy.zoom();
22348 var hasCompounds = r.cy.hasCompoundNodes();
22349 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
22350 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
22351 var labelThreshold = (isTouch ? 8 : 2) / zoom;
22352 var minSqDist = Infinity;
22353 var nearEdge;
22354 var nearNode;
22355
22356 if (interactiveElementsOnly) {
22357 eles = eles.interactive;
22358 }
22359
22360 function addEle(ele, sqDist) {
22361 if (ele.isNode()) {
22362 if (nearNode) {
22363 return; // can't replace node
22364 } else {
22365 nearNode = ele;
22366 near.push(ele);
22367 }
22368 }
22369
22370 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
22371 if (nearEdge) {
22372 // then replace existing edge
22373 // can replace only if same z-index
22374 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) {
22375 for (var i = 0; i < near.length; i++) {
22376 if (near[i].isEdge()) {
22377 near[i] = ele;
22378 nearEdge = ele;
22379 minSqDist = sqDist != null ? sqDist : minSqDist;
22380 break;
22381 }
22382 }
22383 }
22384 } else {
22385 near.push(ele);
22386 nearEdge = ele;
22387 minSqDist = sqDist != null ? sqDist : minSqDist;
22388 }
22389 }
22390 }
22391
22392 function checkNode(node) {
22393 var width = node.outerWidth() + 2 * nodeThreshold;
22394 var height = node.outerHeight() + 2 * nodeThreshold;
22395 var hw = width / 2;
22396 var hh = height / 2;
22397 var pos = node.position();
22398
22399 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
22400 && pos.y - hh <= y && y <= pos.y + hh // bb check y
22401 ) {
22402 var shape = r.nodeShapes[self.getNodeShape(node)];
22403
22404 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
22405 addEle(node, 0);
22406 return true;
22407 }
22408 }
22409 }
22410
22411 function checkEdge(edge) {
22412 var _p = edge._private;
22413 var rs = _p.rscratch;
22414 var styleWidth = edge.pstyle('width').pfValue;
22415 var scale = edge.pstyle('arrow-scale').value;
22416 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
22417
22418 var widthSq = width * width;
22419 var width2 = width * 2;
22420 var src = _p.source;
22421 var tgt = _p.target;
22422 var sqDist;
22423
22424 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
22425 var pts = rs.allpts;
22426
22427 for (var i = 0; i + 3 < pts.length; i += 2) {
22428 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]))) {
22429 addEle(edge, sqDist);
22430 return true;
22431 }
22432 }
22433 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22434 var pts = rs.allpts;
22435
22436 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
22437 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]))) {
22438 addEle(edge, sqDist);
22439 return true;
22440 }
22441 }
22442 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
22443
22444
22445 var src = src || _p.source;
22446 var tgt = tgt || _p.target;
22447 var arSize = self.getArrowWidth(styleWidth, scale);
22448 var arrows = [{
22449 name: 'source',
22450 x: rs.arrowStartX,
22451 y: rs.arrowStartY,
22452 angle: rs.srcArrowAngle
22453 }, {
22454 name: 'target',
22455 x: rs.arrowEndX,
22456 y: rs.arrowEndY,
22457 angle: rs.tgtArrowAngle
22458 }, {
22459 name: 'mid-source',
22460 x: rs.midX,
22461 y: rs.midY,
22462 angle: rs.midsrcArrowAngle
22463 }, {
22464 name: 'mid-target',
22465 x: rs.midX,
22466 y: rs.midY,
22467 angle: rs.midtgtArrowAngle
22468 }];
22469
22470 for (var i = 0; i < arrows.length; i++) {
22471 var ar = arrows[i];
22472 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
22473 var edgeWidth = edge.pstyle('width').pfValue;
22474
22475 if (shape.roughCollide(x, y, arSize, ar.angle, {
22476 x: ar.x,
22477 y: ar.y
22478 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
22479 x: ar.x,
22480 y: ar.y
22481 }, edgeWidth, edgeThreshold)) {
22482 addEle(edge);
22483 return true;
22484 }
22485 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
22486
22487
22488 if (hasCompounds && near.length > 0) {
22489 checkNode(src);
22490 checkNode(tgt);
22491 }
22492 }
22493
22494 function preprop(obj, name, pre) {
22495 return getPrefixedProperty(obj, name, pre);
22496 }
22497
22498 function checkLabel(ele, prefix) {
22499 var _p = ele._private;
22500 var th = labelThreshold;
22501 var prefixDash;
22502
22503 if (prefix) {
22504 prefixDash = prefix + '-';
22505 } else {
22506 prefixDash = '';
22507 }
22508
22509 ele.boundingBox();
22510 var bb = _p.labelBounds[prefix || 'main'];
22511 var text = ele.pstyle(prefixDash + 'label').value;
22512 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
22513
22514 if (!eventsEnabled || !text) {
22515 return;
22516 }
22517
22518 var lx = preprop(_p.rscratch, 'labelX', prefix);
22519 var ly = preprop(_p.rscratch, 'labelY', prefix);
22520 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
22521 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
22522 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
22523 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
22524
22525 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
22526
22527 var ly1 = bb.y1 - th - oy;
22528 var ly2 = bb.y2 + th - oy;
22529
22530 if (theta) {
22531 var cos = Math.cos(theta);
22532 var sin = Math.sin(theta);
22533
22534 var rotate = function rotate(x, y) {
22535 x = x - lx;
22536 y = y - ly;
22537 return {
22538 x: x * cos - y * sin + lx,
22539 y: x * sin + y * cos + ly
22540 };
22541 };
22542
22543 var px1y1 = rotate(lx1, ly1);
22544 var px1y2 = rotate(lx1, ly2);
22545 var px2y1 = rotate(lx2, ly1);
22546 var px2y2 = rotate(lx2, ly2);
22547 var points = [// with the margin added after the rotation is applied
22548 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
22549
22550 if (pointInsidePolygonPoints(x, y, points)) {
22551 addEle(ele);
22552 return true;
22553 }
22554 } else {
22555 // do a cheaper bb check
22556 if (inBoundingBox(bb, x, y)) {
22557 addEle(ele);
22558 return true;
22559 }
22560 }
22561 }
22562
22563 for (var i = eles.length - 1; i >= 0; i--) {
22564 // reverse order for precedence
22565 var ele = eles[i];
22566
22567 if (ele.isNode()) {
22568 checkNode(ele) || checkLabel(ele);
22569 } else {
22570 // then edge
22571 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
22572 }
22573 }
22574
22575 return near;
22576 }; // 'Give me everything from this box'
22577
22578
22579 BRp$1.getAllInBox = function (x1, y1, x2, y2) {
22580 var eles = this.getCachedZSortedEles().interactive;
22581 var box = [];
22582 var x1c = Math.min(x1, x2);
22583 var x2c = Math.max(x1, x2);
22584 var y1c = Math.min(y1, y2);
22585 var y2c = Math.max(y1, y2);
22586 x1 = x1c;
22587 x2 = x2c;
22588 y1 = y1c;
22589 y2 = y2c;
22590 var boxBb = makeBoundingBox({
22591 x1: x1,
22592 y1: y1,
22593 x2: x2,
22594 y2: y2
22595 });
22596
22597 for (var e = 0; e < eles.length; e++) {
22598 var ele = eles[e];
22599
22600 if (ele.isNode()) {
22601 var node = ele;
22602 var nodeBb = node.boundingBox({
22603 includeNodes: true,
22604 includeEdges: false,
22605 includeLabels: false
22606 });
22607
22608 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22609 box.push(node);
22610 }
22611 } else {
22612 var edge = ele;
22613 var _p = edge._private;
22614 var rs = _p.rscratch;
22615
22616 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22617 continue;
22618 }
22619
22620 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22621 continue;
22622 }
22623
22624 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22625 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22626 var allInside = true;
22627
22628 for (var i = 0; i < pts.length; i++) {
22629 if (!pointInBoundingBox(boxBb, pts[i])) {
22630 allInside = false;
22631 break;
22632 }
22633 }
22634
22635 if (allInside) {
22636 box.push(edge);
22637 }
22638 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22639 box.push(edge);
22640 }
22641 }
22642 }
22643
22644 return box;
22645 };
22646
22647 var BRp$2 = {};
22648
22649 BRp$2.calculateArrowAngles = function (edge) {
22650 var rs = edge._private.rscratch;
22651 var isHaystack = rs.edgeType === 'haystack';
22652 var isBezier = rs.edgeType === 'bezier';
22653 var isMultibezier = rs.edgeType === 'multibezier';
22654 var isSegments = rs.edgeType === 'segments';
22655 var isCompound = rs.edgeType === 'compound';
22656 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
22657
22658 var dispX, dispY;
22659 var startX, startY, endX, endY, midX, midY;
22660
22661 if (isHaystack) {
22662 startX = rs.haystackPts[0];
22663 startY = rs.haystackPts[1];
22664 endX = rs.haystackPts[2];
22665 endY = rs.haystackPts[3];
22666 } else {
22667 startX = rs.arrowStartX;
22668 startY = rs.arrowStartY;
22669 endX = rs.arrowEndX;
22670 endY = rs.arrowEndY;
22671 }
22672
22673 midX = rs.midX;
22674 midY = rs.midY; // source
22675 //
22676
22677 if (isSegments) {
22678 dispX = startX - rs.segpts[0];
22679 dispY = startY - rs.segpts[1];
22680 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22681 var pts = rs.allpts;
22682 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22683 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22684 dispX = startX - bX;
22685 dispY = startY - bY;
22686 } else {
22687 dispX = startX - midX;
22688 dispY = startY - midY;
22689 }
22690
22691 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
22692 //
22693
22694 var midX = rs.midX;
22695 var midY = rs.midY;
22696
22697 if (isHaystack) {
22698 midX = (startX + endX) / 2;
22699 midY = (startY + endY) / 2;
22700 }
22701
22702 dispX = endX - startX;
22703 dispY = endY - startY;
22704
22705 if (isSegments) {
22706 var pts = rs.allpts;
22707
22708 if (pts.length / 2 % 2 === 0) {
22709 var i2 = pts.length / 2;
22710 var i1 = i2 - 2;
22711 dispX = pts[i2] - pts[i1];
22712 dispY = pts[i2 + 1] - pts[i1 + 1];
22713 } else {
22714 var i2 = pts.length / 2 - 1;
22715 var i1 = i2 - 2;
22716 var i3 = i2 + 2;
22717 dispX = pts[i2] - pts[i1];
22718 dispY = pts[i2 + 1] - pts[i1 + 1];
22719 }
22720 } else if (isMultibezier || isCompound || isSelf) {
22721 var pts = rs.allpts;
22722 var cpts = rs.ctrlpts;
22723 var bp0x, bp0y;
22724 var bp1x, bp1y;
22725
22726 if (cpts.length / 2 % 2 === 0) {
22727 var p0 = pts.length / 2 - 1; // startpt
22728
22729 var ic = p0 + 2;
22730 var p1 = ic + 2;
22731 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22732 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22733 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22734 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22735 } else {
22736 var ic = pts.length / 2 - 1; // ctrpt
22737
22738 var p0 = ic - 2; // startpt
22739
22740 var p1 = ic + 2; // endpt
22741
22742 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22743 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22744 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22745 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22746 }
22747
22748 dispX = bp1x - bp0x;
22749 dispY = bp1y - bp0y;
22750 }
22751
22752 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22753 rs.midDispX = dispX;
22754 rs.midDispY = dispY; // mid source
22755 //
22756
22757 dispX *= -1;
22758 dispY *= -1;
22759
22760 if (isSegments) {
22761 var pts = rs.allpts;
22762
22763 if (pts.length / 2 % 2 === 0) ; else {
22764 var i2 = pts.length / 2 - 1;
22765 var i3 = i2 + 2;
22766 dispX = -(pts[i3] - pts[i2]);
22767 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22768 }
22769 }
22770
22771 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22772 //
22773
22774 if (isSegments) {
22775 dispX = endX - rs.segpts[rs.segpts.length - 2];
22776 dispY = endY - rs.segpts[rs.segpts.length - 1];
22777 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22778 var pts = rs.allpts;
22779 var l = pts.length;
22780 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22781 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22782 dispX = endX - bX;
22783 dispY = endY - bY;
22784 } else {
22785 dispX = endX - midX;
22786 dispY = endY - midY;
22787 }
22788
22789 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22790 };
22791
22792 BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22793 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22794 var cachedVal = cache[edgeWidth + ', ' + scale];
22795
22796 if (cachedVal) {
22797 return cachedVal;
22798 }
22799
22800 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22801 cache[edgeWidth + ', ' + scale] = cachedVal;
22802 return cachedVal;
22803 };
22804
22805 var BRp$3 = {};
22806
22807 BRp$3.findHaystackPoints = function (edges) {
22808 for (var i = 0; i < edges.length; i++) {
22809 var edge = edges[i];
22810 var _p = edge._private;
22811 var rs = _p.rscratch;
22812
22813 if (!rs.haystack) {
22814 var angle = Math.random() * 2 * Math.PI;
22815 rs.source = {
22816 x: Math.cos(angle),
22817 y: Math.sin(angle)
22818 };
22819 angle = Math.random() * 2 * Math.PI;
22820 rs.target = {
22821 x: Math.cos(angle),
22822 y: Math.sin(angle)
22823 };
22824 }
22825
22826 var src = _p.source;
22827 var tgt = _p.target;
22828 var srcPos = src.position();
22829 var tgtPos = tgt.position();
22830 var srcW = src.width();
22831 var tgtW = tgt.width();
22832 var srcH = src.height();
22833 var tgtH = tgt.height();
22834 var radius = edge.pstyle('haystack-radius').value;
22835 var halfRadius = radius / 2; // b/c have to half width/height
22836
22837 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];
22838 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22839 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22840
22841 rs.edgeType = 'haystack';
22842 rs.haystack = true;
22843 this.storeEdgeProjections(edge);
22844 this.calculateArrowAngles(edge);
22845 this.recalculateEdgeLabelProjections(edge);
22846 this.calculateLabelAngles(edge);
22847 }
22848 };
22849
22850 BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22851 // Segments (multiple straight lines)
22852 var rs = edge._private.rscratch;
22853 var posPts = pairInfo.posPts,
22854 intersectionPts = pairInfo.intersectionPts,
22855 vectorNormInverse = pairInfo.vectorNormInverse;
22856 var edgeDistances = edge.pstyle('edge-distances').value;
22857 var segmentWs = edge.pstyle('segment-weights');
22858 var segmentDs = edge.pstyle('segment-distances');
22859 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22860 rs.edgeType = 'segments';
22861 rs.segpts = [];
22862
22863 for (var s = 0; s < segmentsN; s++) {
22864 var w = segmentWs.pfValue[s];
22865 var d = segmentDs.pfValue[s];
22866 var w1 = 1 - w;
22867 var w2 = w;
22868 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22869 var adjustedMidpt = {
22870 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22871 y: midptPts.y1 * w1 + midptPts.y2 * w2
22872 };
22873 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22874 }
22875 };
22876
22877 BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22878 // Self-edge
22879 var rs = edge._private.rscratch;
22880 var dirCounts = pairInfo.dirCounts,
22881 srcPos = pairInfo.srcPos;
22882 var ctrlptDists = edge.pstyle('control-point-distances');
22883 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22884 var loopDir = edge.pstyle('loop-direction').pfValue;
22885 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22886 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22887 rs.edgeType = 'self';
22888 var j = i;
22889 var loopDist = stepSize;
22890
22891 if (edgeIsUnbundled) {
22892 j = 0;
22893 loopDist = ctrlptDist;
22894 }
22895
22896 var loopAngle = loopDir - Math.PI / 2;
22897 var outAngle = loopAngle - loopSwp / 2;
22898 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22899
22900 var dc = String(loopDir + '_' + loopSwp);
22901 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22902 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)];
22903 };
22904
22905 BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22906 // Compound edge
22907 var rs = edge._private.rscratch;
22908 rs.edgeType = 'compound';
22909 var srcPos = pairInfo.srcPos,
22910 tgtPos = pairInfo.tgtPos,
22911 srcW = pairInfo.srcW,
22912 srcH = pairInfo.srcH,
22913 tgtW = pairInfo.tgtW,
22914 tgtH = pairInfo.tgtH;
22915 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22916 var ctrlptDists = edge.pstyle('control-point-distances');
22917 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22918 var j = i;
22919 var loopDist = stepSize;
22920
22921 if (edgeIsUnbundled) {
22922 j = 0;
22923 loopDist = ctrlptDist;
22924 }
22925
22926 var loopW = 50;
22927 var loopaPos = {
22928 x: srcPos.x - srcW / 2,
22929 y: srcPos.y - srcH / 2
22930 };
22931 var loopbPos = {
22932 x: tgtPos.x - tgtW / 2,
22933 y: tgtPos.y - tgtH / 2
22934 };
22935 var loopPos = {
22936 x: Math.min(loopaPos.x, loopbPos.x),
22937 y: Math.min(loopaPos.y, loopbPos.y)
22938 }; // avoids cases with impossible beziers
22939
22940 var minCompoundStretch = 0.5;
22941 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22942 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22943 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];
22944 };
22945
22946 BRp$3.findStraightEdgePoints = function (edge) {
22947 // Straight edge within bundle
22948 edge._private.rscratch.edgeType = 'straight';
22949 };
22950
22951 BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22952 var rs = edge._private.rscratch;
22953 var vectorNormInverse = pairInfo.vectorNormInverse,
22954 posPts = pairInfo.posPts,
22955 intersectionPts = pairInfo.intersectionPts;
22956 var edgeDistances = edge.pstyle('edge-distances').value;
22957 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22958 var ctrlptDists = edge.pstyle('control-point-distances');
22959 var ctrlptWs = edge.pstyle('control-point-weights');
22960 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22961 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22962 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22963
22964 var multi = edgeIsUnbundled;
22965 rs.edgeType = multi ? 'multibezier' : 'bezier';
22966 rs.ctrlpts = [];
22967
22968 for (var b = 0; b < bezierN; b++) {
22969 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22970 var manctrlptDist = void 0;
22971 var sign = signum(normctrlptDist);
22972
22973 if (multi) {
22974 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22975
22976 ctrlptWeight = ctrlptWs.value[b];
22977 }
22978
22979 if (edgeIsUnbundled) {
22980 // multi or single unbundled
22981 manctrlptDist = ctrlptDist;
22982 } else {
22983 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22984 }
22985
22986 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22987 var w1 = 1 - ctrlptWeight;
22988 var w2 = ctrlptWeight;
22989 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22990 var adjustedMidpt = {
22991 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22992 y: midptPts.y1 * w1 + midptPts.y2 * w2
22993 };
22994 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22995 }
22996 };
22997
22998 BRp$3.findTaxiPoints = function (edge, pairInfo) {
22999 // Taxicab geometry with two turns maximum
23000 var rs = edge._private.rscratch;
23001 rs.edgeType = 'segments';
23002 var VERTICAL = 'vertical';
23003 var HORIZONTAL = 'horizontal';
23004 var LEFTWARD = 'leftward';
23005 var RIGHTWARD = 'rightward';
23006 var DOWNWARD = 'downward';
23007 var UPWARD = 'upward';
23008 var AUTO = 'auto';
23009 var posPts = pairInfo.posPts,
23010 srcW = pairInfo.srcW,
23011 srcH = pairInfo.srcH,
23012 tgtW = pairInfo.tgtW,
23013 tgtH = pairInfo.tgtH;
23014 var edgeDistances = edge.pstyle('edge-distances').value;
23015 var dIncludesNodeBody = edgeDistances !== 'node-position';
23016 var taxiDir = edge.pstyle('taxi-direction').value;
23017 var rawTaxiDir = taxiDir; // unprocessed value
23018
23019 var taxiTurn = edge.pstyle('taxi-turn');
23020 var turnIsPercent = taxiTurn.units === '%';
23021 var taxiTurnPfVal = taxiTurn.pfValue;
23022 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
23023
23024 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
23025 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
23026 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
23027 var pdx = posPts.x2 - posPts.x1;
23028 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
23029
23030 var subDWH = function subDWH(dxy, dwh) {
23031 if (dxy > 0) {
23032 return Math.max(dxy - dwh, 0);
23033 } else {
23034 return Math.min(dxy + dwh, 0);
23035 }
23036 };
23037
23038 var dx = subDWH(pdx, dw);
23039 var dy = subDWH(pdy, dh);
23040 var isExplicitDir = false;
23041
23042 if (rawTaxiDir === AUTO) {
23043 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
23044 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
23045 taxiDir = VERTICAL;
23046 isExplicitDir = true;
23047 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
23048 taxiDir = HORIZONTAL;
23049 isExplicitDir = true;
23050 }
23051
23052 var isVert = taxiDir === VERTICAL;
23053 var l = isVert ? dy : dx;
23054 var pl = isVert ? pdy : pdx;
23055 var sgnL = signum(pl);
23056 var forcedDir = false;
23057
23058 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
23059 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
23060 sgnL *= -1;
23061 l = sgnL * Math.abs(l);
23062 forcedDir = true;
23063 }
23064
23065 var d;
23066
23067 if (turnIsPercent) {
23068 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
23069 d = p * l;
23070 } else {
23071 var k = taxiTurnPfVal < 0 ? l : 0;
23072 d = k + taxiTurnPfVal * sgnL;
23073 }
23074
23075 var getIsTooClose = function getIsTooClose(d) {
23076 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
23077 };
23078
23079 var isTooCloseSrc = getIsTooClose(d);
23080 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
23081 var isTooClose = isTooCloseSrc || isTooCloseTgt;
23082
23083 if (isTooClose && !forcedDir) {
23084 // non-ideal routing
23085 if (isVert) {
23086 // vertical fallbacks
23087 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
23088 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
23089
23090 if (lShapeInsideSrc) {
23091 // horizontal Z-shape (direction not respected)
23092 var x = (posPts.x1 + posPts.x2) / 2;
23093 var y1 = posPts.y1,
23094 y2 = posPts.y2;
23095 rs.segpts = [x, y1, x, y2];
23096 } else if (lShapeInsideTgt) {
23097 // vertical Z-shape (distance not respected)
23098 var y = (posPts.y1 + posPts.y2) / 2;
23099 var x1 = posPts.x1,
23100 x2 = posPts.x2;
23101 rs.segpts = [x1, y, x2, y];
23102 } else {
23103 // L-shape fallback (turn distance not respected, but works well with tree siblings)
23104 rs.segpts = [posPts.x1, posPts.y2];
23105 }
23106 } else {
23107 // horizontal fallbacks
23108 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
23109
23110 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
23111
23112 if (_lShapeInsideSrc) {
23113 // vertical Z-shape (direction not respected)
23114 var _y = (posPts.y1 + posPts.y2) / 2;
23115
23116 var _x = posPts.x1,
23117 _x2 = posPts.x2;
23118 rs.segpts = [_x, _y, _x2, _y];
23119 } else if (_lShapeInsideTgt) {
23120 // horizontal Z-shape (turn distance not respected)
23121 var _x3 = (posPts.x1 + posPts.x2) / 2;
23122
23123 var _y2 = posPts.y1,
23124 _y3 = posPts.y2;
23125 rs.segpts = [_x3, _y2, _x3, _y3];
23126 } else {
23127 // L-shape (turn distance not respected, but works well for tree siblings)
23128 rs.segpts = [posPts.x2, posPts.y1];
23129 }
23130 }
23131 } else {
23132 // ideal routing
23133 if (isVert) {
23134 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
23135
23136 var _x4 = posPts.x1,
23137 _x5 = posPts.x2;
23138 rs.segpts = [_x4, _y4, _x5, _y4];
23139 } else {
23140 // horizontal
23141 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
23142
23143 var _y5 = posPts.y1,
23144 _y6 = posPts.y2;
23145 rs.segpts = [_x6, _y5, _x6, _y6];
23146 }
23147 }
23148 };
23149
23150 BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
23151 var rs = edge._private.rscratch; // can only correct beziers for now...
23152
23153 if (rs.edgeType === 'bezier') {
23154 var srcPos = pairInfo.srcPos,
23155 tgtPos = pairInfo.tgtPos,
23156 srcW = pairInfo.srcW,
23157 srcH = pairInfo.srcH,
23158 tgtW = pairInfo.tgtW,
23159 tgtH = pairInfo.tgtH,
23160 srcShape = pairInfo.srcShape,
23161 tgtShape = pairInfo.tgtShape;
23162 var badStart = !number(rs.startX) || !number(rs.startY);
23163 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
23164 var badEnd = !number(rs.endX) || !number(rs.endY);
23165 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
23166 var minCpADistFactor = 3;
23167 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23168 var minCpADist = minCpADistFactor * arrowW;
23169 var startACpDist = dist({
23170 x: rs.ctrlpts[0],
23171 y: rs.ctrlpts[1]
23172 }, {
23173 x: rs.startX,
23174 y: rs.startY
23175 });
23176 var closeStartACp = startACpDist < minCpADist;
23177 var endACpDist = dist({
23178 x: rs.ctrlpts[0],
23179 y: rs.ctrlpts[1]
23180 }, {
23181 x: rs.endX,
23182 y: rs.endY
23183 });
23184 var closeEndACp = endACpDist < minCpADist;
23185 var overlapping = false;
23186
23187 if (badStart || badAStart || closeStartACp) {
23188 overlapping = true; // project control point along line from src centre to outside the src shape
23189 // (otherwise intersection will yield nothing)
23190
23191 var cpD = {
23192 // delta
23193 x: rs.ctrlpts[0] - srcPos.x,
23194 y: rs.ctrlpts[1] - srcPos.y
23195 };
23196 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
23197
23198 var cpM = {
23199 // normalised delta
23200 x: cpD.x / cpL,
23201 y: cpD.y / cpL
23202 };
23203 var radius = Math.max(srcW, srcH);
23204 var cpProj = {
23205 // *2 radius guarantees outside shape
23206 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
23207 y: rs.ctrlpts[1] + cpM.y * 2 * radius
23208 };
23209 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
23210
23211 if (closeStartACp) {
23212 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
23213 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
23214 } else {
23215 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
23216 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
23217 }
23218 }
23219
23220 if (badEnd || badAEnd || closeEndACp) {
23221 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
23222 // (otherwise intersection will yield nothing)
23223
23224 var _cpD = {
23225 // delta
23226 x: rs.ctrlpts[0] - tgtPos.x,
23227 y: rs.ctrlpts[1] - tgtPos.y
23228 };
23229
23230 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
23231
23232
23233 var _cpM = {
23234 // normalised delta
23235 x: _cpD.x / _cpL,
23236 y: _cpD.y / _cpL
23237 };
23238
23239 var _radius = Math.max(srcW, srcH);
23240
23241 var _cpProj = {
23242 // *2 radius guarantees outside shape
23243 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
23244 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
23245 };
23246 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
23247
23248 if (closeEndACp) {
23249 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
23250 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
23251 } else {
23252 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
23253 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
23254 }
23255 }
23256
23257 if (overlapping) {
23258 // recalc endpts
23259 this.findEndpoints(edge);
23260 }
23261 }
23262 };
23263
23264 BRp$3.storeAllpts = function (edge) {
23265 var rs = edge._private.rscratch;
23266
23267 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
23268 rs.allpts = [];
23269 rs.allpts.push(rs.startX, rs.startY);
23270
23271 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
23272 // ctrl pt itself
23273 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
23274
23275 if (b + 3 < rs.ctrlpts.length) {
23276 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
23277 }
23278 }
23279
23280 rs.allpts.push(rs.endX, rs.endY);
23281 var m, mt;
23282
23283 if (rs.ctrlpts.length / 2 % 2 === 0) {
23284 m = rs.allpts.length / 2 - 1;
23285 rs.midX = rs.allpts[m];
23286 rs.midY = rs.allpts[m + 1];
23287 } else {
23288 m = rs.allpts.length / 2 - 3;
23289 mt = 0.5;
23290 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
23291 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
23292 }
23293 } else if (rs.edgeType === 'straight') {
23294 // need to calc these after endpts
23295 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
23296
23297 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
23298 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
23299 } else if (rs.edgeType === 'segments') {
23300 rs.allpts = [];
23301 rs.allpts.push(rs.startX, rs.startY);
23302 rs.allpts.push.apply(rs.allpts, rs.segpts);
23303 rs.allpts.push(rs.endX, rs.endY);
23304
23305 if (rs.segpts.length % 4 === 0) {
23306 var i2 = rs.segpts.length / 2;
23307 var i1 = i2 - 2;
23308 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
23309 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
23310 } else {
23311 var _i = rs.segpts.length / 2 - 1;
23312
23313 rs.midX = rs.segpts[_i];
23314 rs.midY = rs.segpts[_i + 1];
23315 }
23316 }
23317 };
23318
23319 BRp$3.checkForInvalidEdgeWarning = function (edge) {
23320 var rs = edge[0]._private.rscratch;
23321
23322 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
23323 rs.loggedErr = false;
23324 } else {
23325 if (!rs.loggedErr) {
23326 rs.loggedErr = true;
23327 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.');
23328 }
23329 }
23330 };
23331
23332 BRp$3.findEdgeControlPoints = function (edges) {
23333 var _this = this;
23334
23335 if (!edges || edges.length === 0) {
23336 return;
23337 }
23338
23339 var r = this;
23340 var cy = r.cy;
23341 var hasCompounds = cy.hasCompoundNodes();
23342 var hashTable = {
23343 map: new Map$1(),
23344 get: function get(pairId) {
23345 var map2 = this.map.get(pairId[0]);
23346
23347 if (map2 != null) {
23348 return map2.get(pairId[1]);
23349 } else {
23350 return null;
23351 }
23352 },
23353 set: function set(pairId, val) {
23354 var map2 = this.map.get(pairId[0]);
23355
23356 if (map2 == null) {
23357 map2 = new Map$1();
23358 this.map.set(pairId[0], map2);
23359 }
23360
23361 map2.set(pairId[1], val);
23362 }
23363 };
23364 var pairIds = [];
23365 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
23366
23367 for (var i = 0; i < edges.length; i++) {
23368 var edge = edges[i];
23369 var _p = edge._private;
23370 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
23371 // they shouldn't take up space
23372
23373 if (edge.removed() || !edge.takesUpSpace()) {
23374 continue;
23375 }
23376
23377 if (curveStyle === 'haystack') {
23378 haystackEdges.push(edge);
23379 continue;
23380 }
23381
23382 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
23383 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
23384 var src = _p.source;
23385 var tgt = _p.target;
23386 var srcIndex = src.poolIndex();
23387 var tgtIndex = tgt.poolIndex();
23388 var pairId = [srcIndex, tgtIndex].sort();
23389 var tableEntry = hashTable.get(pairId);
23390
23391 if (tableEntry == null) {
23392 tableEntry = {
23393 eles: []
23394 };
23395 hashTable.set(pairId, tableEntry);
23396 pairIds.push(pairId);
23397 }
23398
23399 tableEntry.eles.push(edge);
23400
23401 if (edgeIsUnbundled) {
23402 tableEntry.hasUnbundled = true;
23403 }
23404
23405 if (edgeIsBezier) {
23406 tableEntry.hasBezier = true;
23407 }
23408 } // for each pair (src, tgt), create the ctrl pts
23409 // Nested for loop is OK; total number of iterations for both loops = edgeCount
23410
23411
23412 var _loop = function _loop(p) {
23413 var pairId = pairIds[p];
23414 var pairInfo = hashTable.get(pairId);
23415 var swappedpairInfo = void 0;
23416
23417 if (!pairInfo.hasUnbundled) {
23418 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
23419 return e.isBundledBezier();
23420 });
23421 clearArray(pairInfo.eles);
23422 pllEdges.forEach(function (edge) {
23423 return pairInfo.eles.push(edge);
23424 }); // for each pair id, the edges should be sorted by index
23425
23426 pairInfo.eles.sort(function (edge1, edge2) {
23427 return edge1.poolIndex() - edge2.poolIndex();
23428 });
23429 }
23430
23431 var firstEdge = pairInfo.eles[0];
23432 var src = firstEdge.source();
23433 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
23434
23435 if (src.poolIndex() > tgt.poolIndex()) {
23436 var temp = src;
23437 src = tgt;
23438 tgt = temp;
23439 }
23440
23441 var srcPos = pairInfo.srcPos = src.position();
23442 var tgtPos = pairInfo.tgtPos = tgt.position();
23443 var srcW = pairInfo.srcW = src.outerWidth();
23444 var srcH = pairInfo.srcH = src.outerHeight();
23445 var tgtW = pairInfo.tgtW = tgt.outerWidth();
23446 var tgtH = pairInfo.tgtH = tgt.outerHeight();
23447
23448 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
23449
23450 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
23451
23452 pairInfo.dirCounts = {
23453 'north': 0,
23454 'west': 0,
23455 'south': 0,
23456 'east': 0,
23457 'northwest': 0,
23458 'southwest': 0,
23459 'northeast': 0,
23460 'southeast': 0
23461 };
23462
23463 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
23464 var _edge = pairInfo.eles[_i2];
23465 var rs = _edge[0]._private.rscratch;
23466
23467 var _curveStyle = _edge.pstyle('curve-style').value;
23468
23469 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
23470
23471
23472 var edgeIsSwapped = !src.same(_edge.source());
23473
23474 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
23475 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
23476
23477 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
23478 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
23479
23480 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
23481 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
23482 var intersectionPts = pairInfo.intersectionPts = {
23483 x1: srcOutside[0],
23484 x2: tgtOutside[0],
23485 y1: srcOutside[1],
23486 y2: tgtOutside[1]
23487 };
23488 var posPts = pairInfo.posPts = {
23489 x1: srcPos.x,
23490 x2: tgtPos.x,
23491 y1: srcPos.y,
23492 y2: tgtPos.y
23493 };
23494 var dy = tgtOutside[1] - srcOutside[1];
23495 var dx = tgtOutside[0] - srcOutside[0];
23496 var l = Math.sqrt(dx * dx + dy * dy);
23497 var vector = pairInfo.vector = {
23498 x: dx,
23499 y: dy
23500 };
23501 var vectorNorm = pairInfo.vectorNorm = {
23502 x: vector.x / l,
23503 y: vector.y / l
23504 };
23505 var vectorNormInverse = {
23506 x: -vectorNorm.y,
23507 y: vectorNorm.x
23508 }; // if node shapes overlap, then no ctrl pts to draw
23509
23510 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);
23511 pairInfo.vectorNormInverse = vectorNormInverse;
23512 swappedpairInfo = {
23513 nodesOverlap: pairInfo.nodesOverlap,
23514 dirCounts: pairInfo.dirCounts,
23515 calculatedIntersection: true,
23516 hasBezier: pairInfo.hasBezier,
23517 hasUnbundled: pairInfo.hasUnbundled,
23518 eles: pairInfo.eles,
23519 srcPos: tgtPos,
23520 tgtPos: srcPos,
23521 srcW: tgtW,
23522 srcH: tgtH,
23523 tgtW: srcW,
23524 tgtH: srcH,
23525 srcIntn: tgtIntn,
23526 tgtIntn: srcIntn,
23527 srcShape: tgtShape,
23528 tgtShape: srcShape,
23529 posPts: {
23530 x1: posPts.x2,
23531 y1: posPts.y2,
23532 x2: posPts.x1,
23533 y2: posPts.y1
23534 },
23535 intersectionPts: {
23536 x1: intersectionPts.x2,
23537 y1: intersectionPts.y2,
23538 x2: intersectionPts.x1,
23539 y2: intersectionPts.y1
23540 },
23541 vector: {
23542 x: -vector.x,
23543 y: -vector.y
23544 },
23545 vectorNorm: {
23546 x: -vectorNorm.x,
23547 y: -vectorNorm.y
23548 },
23549 vectorNormInverse: {
23550 x: -vectorNormInverse.x,
23551 y: -vectorNormInverse.y
23552 }
23553 };
23554 }
23555
23556 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
23557 rs.nodesOverlap = passedPairInfo.nodesOverlap;
23558 rs.srcIntn = passedPairInfo.srcIntn;
23559 rs.tgtIntn = passedPairInfo.tgtIntn;
23560
23561 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
23562 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23563 } else if (src === tgt) {
23564 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23565 } else if (_curveStyle === 'segments') {
23566 _this.findSegmentsPoints(_edge, passedPairInfo);
23567 } else if (_curveStyle === 'taxi') {
23568 _this.findTaxiPoints(_edge, passedPairInfo);
23569 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
23570 _this.findStraightEdgePoints(_edge);
23571 } else {
23572 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
23573 }
23574
23575 _this.findEndpoints(_edge);
23576
23577 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
23578
23579 _this.checkForInvalidEdgeWarning(_edge);
23580
23581 _this.storeAllpts(_edge);
23582
23583 _this.storeEdgeProjections(_edge);
23584
23585 _this.calculateArrowAngles(_edge);
23586
23587 _this.recalculateEdgeLabelProjections(_edge);
23588
23589 _this.calculateLabelAngles(_edge);
23590 } // for pair edges
23591
23592 };
23593
23594 for (var p = 0; p < pairIds.length; p++) {
23595 _loop(p);
23596 } // for pair ids
23597 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
23598
23599
23600 this.findHaystackPoints(haystackEdges);
23601 };
23602
23603 function getPts(pts) {
23604 var retPts = [];
23605
23606 if (pts == null) {
23607 return;
23608 }
23609
23610 for (var i = 0; i < pts.length; i += 2) {
23611 var x = pts[i];
23612 var y = pts[i + 1];
23613 retPts.push({
23614 x: x,
23615 y: y
23616 });
23617 }
23618
23619 return retPts;
23620 }
23621
23622 BRp$3.getSegmentPoints = function (edge) {
23623 var rs = edge[0]._private.rscratch;
23624 var type = rs.edgeType;
23625
23626 if (type === 'segments') {
23627 this.recalculateRenderedStyle(edge);
23628 return getPts(rs.segpts);
23629 }
23630 };
23631
23632 BRp$3.getControlPoints = function (edge) {
23633 var rs = edge[0]._private.rscratch;
23634 var type = rs.edgeType;
23635
23636 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23637 this.recalculateRenderedStyle(edge);
23638 return getPts(rs.ctrlpts);
23639 }
23640 };
23641
23642 BRp$3.getEdgeMidpoint = function (edge) {
23643 var rs = edge[0]._private.rscratch;
23644 this.recalculateRenderedStyle(edge);
23645 return {
23646 x: rs.midX,
23647 y: rs.midY
23648 };
23649 };
23650
23651 var BRp$4 = {};
23652
23653 BRp$4.manualEndptToPx = function (node, prop) {
23654 var r = this;
23655 var npos = node.position();
23656 var w = node.outerWidth();
23657 var h = node.outerHeight();
23658
23659 if (prop.value.length === 2) {
23660 var p = [prop.pfValue[0], prop.pfValue[1]];
23661
23662 if (prop.units[0] === '%') {
23663 p[0] = p[0] * w;
23664 }
23665
23666 if (prop.units[1] === '%') {
23667 p[1] = p[1] * h;
23668 }
23669
23670 p[0] += npos.x;
23671 p[1] += npos.y;
23672 return p;
23673 } else {
23674 var angle = prop.pfValue[0];
23675 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23676
23677 var l = 2 * Math.max(w, h);
23678 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23679 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
23680 }
23681 };
23682
23683 BRp$4.findEndpoints = function (edge) {
23684 var r = this;
23685 var intersect;
23686 var source = edge.source()[0];
23687 var target = edge.target()[0];
23688 var srcPos = source.position();
23689 var tgtPos = target.position();
23690 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23691 var srcArShape = edge.pstyle('source-arrow-shape').value;
23692 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23693 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23694 var curveStyle = edge.pstyle('curve-style').value;
23695 var rs = edge._private.rscratch;
23696 var et = rs.edgeType;
23697 var taxi = curveStyle === 'taxi';
23698 var self = et === 'self' || et === 'compound';
23699 var bezier = et === 'bezier' || et === 'multibezier' || self;
23700 var multi = et !== 'bezier';
23701 var lines = et === 'straight' || et === 'segments';
23702 var segments = et === 'segments';
23703 var hasEndpts = bezier || multi || lines;
23704 var overrideEndpts = self || taxi;
23705 var srcManEndpt = edge.pstyle('source-endpoint');
23706 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23707 var tgtManEndpt = edge.pstyle('target-endpoint');
23708 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23709 rs.srcManEndpt = srcManEndpt;
23710 rs.tgtManEndpt = tgtManEndpt;
23711 var p1; // last known point of edge on target side
23712
23713 var p2; // last known point of edge on source side
23714
23715 var p1_i; // point to intersect with target shape
23716
23717 var p2_i; // point to intersect with source shape
23718
23719 if (bezier) {
23720 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23721 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23722 p1 = cpEnd;
23723 p2 = cpStart;
23724 } else if (lines) {
23725 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23726 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23727 p1 = tgtArrowFromPt;
23728 p2 = srcArrowFromPt;
23729 }
23730
23731 if (tgtManEndptVal === 'inside-to-node') {
23732 intersect = [tgtPos.x, tgtPos.y];
23733 } else if (tgtManEndpt.units) {
23734 intersect = this.manualEndptToPx(target, tgtManEndpt);
23735 } else if (tgtManEndptVal === 'outside-to-line') {
23736 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23737 } else {
23738 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23739 p1_i = p1;
23740 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23741 p1_i = [srcPos.x, srcPos.y];
23742 }
23743
23744 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
23745
23746 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23747 var trs = target._private.rscratch;
23748 var lw = trs.labelWidth;
23749 var lh = trs.labelHeight;
23750 var lx = trs.labelX;
23751 var ly = trs.labelY;
23752 var lw2 = lw / 2;
23753 var lh2 = lh / 2;
23754 var va = target.pstyle('text-valign').value;
23755
23756 if (va === 'top') {
23757 ly -= lh2;
23758 } else if (va === 'bottom') {
23759 ly += lh2;
23760 }
23761
23762 var ha = target.pstyle('text-halign').value;
23763
23764 if (ha === 'left') {
23765 lx -= lw2;
23766 } else if (ha === 'right') {
23767 lx += lw2;
23768 }
23769
23770 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);
23771
23772 if (labelIntersect.length > 0) {
23773 var refPt = srcPos;
23774 var intSqdist = sqdist(refPt, array2point(intersect));
23775 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23776 var minSqDist = intSqdist;
23777
23778 if (labIntSqdist < intSqdist) {
23779 intersect = labelIntersect;
23780 minSqDist = labIntSqdist;
23781 }
23782
23783 if (labelIntersect.length > 2) {
23784 var labInt2SqDist = sqdist(refPt, {
23785 x: labelIntersect[2],
23786 y: labelIntersect[3]
23787 });
23788
23789 if (labInt2SqDist < minSqDist) {
23790 intersect = [labelIntersect[2], labelIntersect[3]];
23791 }
23792 }
23793 }
23794 }
23795 }
23796
23797 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23798 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23799 rs.endX = edgeEnd[0];
23800 rs.endY = edgeEnd[1];
23801 rs.arrowEndX = arrowEnd[0];
23802 rs.arrowEndY = arrowEnd[1];
23803
23804 if (srcManEndptVal === 'inside-to-node') {
23805 intersect = [srcPos.x, srcPos.y];
23806 } else if (srcManEndpt.units) {
23807 intersect = this.manualEndptToPx(source, srcManEndpt);
23808 } else if (srcManEndptVal === 'outside-to-line') {
23809 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23810 } else {
23811 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23812 p2_i = p2;
23813 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23814 p2_i = [tgtPos.x, tgtPos.y];
23815 }
23816
23817 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23818
23819 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23820 var srs = source._private.rscratch;
23821 var _lw = srs.labelWidth;
23822 var _lh = srs.labelHeight;
23823 var _lx = srs.labelX;
23824 var _ly = srs.labelY;
23825
23826 var _lw2 = _lw / 2;
23827
23828 var _lh2 = _lh / 2;
23829
23830 var _va = source.pstyle('text-valign').value;
23831
23832 if (_va === 'top') {
23833 _ly -= _lh2;
23834 } else if (_va === 'bottom') {
23835 _ly += _lh2;
23836 }
23837
23838 var _ha = source.pstyle('text-halign').value;
23839
23840 if (_ha === 'left') {
23841 _lx -= _lw2;
23842 } else if (_ha === 'right') {
23843 _lx += _lw2;
23844 }
23845
23846 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);
23847
23848 if (_labelIntersect.length > 0) {
23849 var _refPt = tgtPos;
23850
23851 var _intSqdist = sqdist(_refPt, array2point(intersect));
23852
23853 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23854
23855 var _minSqDist = _intSqdist;
23856
23857 if (_labIntSqdist < _intSqdist) {
23858 intersect = [_labelIntersect[0], _labelIntersect[1]];
23859 _minSqDist = _labIntSqdist;
23860 }
23861
23862 if (_labelIntersect.length > 2) {
23863 var _labInt2SqDist = sqdist(_refPt, {
23864 x: _labelIntersect[2],
23865 y: _labelIntersect[3]
23866 });
23867
23868 if (_labInt2SqDist < _minSqDist) {
23869 intersect = [_labelIntersect[2], _labelIntersect[3]];
23870 }
23871 }
23872 }
23873 }
23874 }
23875
23876 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23877 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23878 rs.startX = edgeStart[0];
23879 rs.startY = edgeStart[1];
23880 rs.arrowStartX = arrowStart[0];
23881 rs.arrowStartY = arrowStart[1];
23882
23883 if (hasEndpts) {
23884 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23885 rs.badLine = true;
23886 } else {
23887 rs.badLine = false;
23888 }
23889 }
23890 };
23891
23892 BRp$4.getSourceEndpoint = function (edge) {
23893 var rs = edge[0]._private.rscratch;
23894 this.recalculateRenderedStyle(edge);
23895
23896 switch (rs.edgeType) {
23897 case 'haystack':
23898 return {
23899 x: rs.haystackPts[0],
23900 y: rs.haystackPts[1]
23901 };
23902
23903 default:
23904 return {
23905 x: rs.arrowStartX,
23906 y: rs.arrowStartY
23907 };
23908 }
23909 };
23910
23911 BRp$4.getTargetEndpoint = function (edge) {
23912 var rs = edge[0]._private.rscratch;
23913 this.recalculateRenderedStyle(edge);
23914
23915 switch (rs.edgeType) {
23916 case 'haystack':
23917 return {
23918 x: rs.haystackPts[2],
23919 y: rs.haystackPts[3]
23920 };
23921
23922 default:
23923 return {
23924 x: rs.arrowEndX,
23925 y: rs.arrowEndY
23926 };
23927 }
23928 };
23929
23930 var BRp$5 = {};
23931
23932 function pushBezierPts(r, edge, pts) {
23933 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23934 return qbezierAt(p1, p2, p3, t);
23935 };
23936
23937 var _p = edge._private;
23938 var bpts = _p.rstyle.bezierPts;
23939
23940 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23941 var p = r.bezierProjPcts[i];
23942 bpts.push({
23943 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23944 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23945 });
23946 }
23947 }
23948
23949 BRp$5.storeEdgeProjections = function (edge) {
23950 var _p = edge._private;
23951 var rs = _p.rscratch;
23952 var et = rs.edgeType; // clear the cached points state
23953
23954 _p.rstyle.bezierPts = null;
23955 _p.rstyle.linePts = null;
23956 _p.rstyle.haystackPts = null;
23957
23958 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23959 _p.rstyle.bezierPts = [];
23960
23961 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23962 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23963 }
23964 } else if (et === 'segments') {
23965 var lpts = _p.rstyle.linePts = [];
23966
23967 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23968 lpts.push({
23969 x: rs.allpts[i],
23970 y: rs.allpts[i + 1]
23971 });
23972 }
23973 } else if (et === 'haystack') {
23974 var hpts = rs.haystackPts;
23975 _p.rstyle.haystackPts = [{
23976 x: hpts[0],
23977 y: hpts[1]
23978 }, {
23979 x: hpts[2],
23980 y: hpts[3]
23981 }];
23982 }
23983
23984 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23985 };
23986
23987 BRp$5.recalculateEdgeProjections = function (edges) {
23988 this.findEdgeControlPoints(edges);
23989 };
23990
23991 /* global document */
23992
23993 var BRp$6 = {};
23994
23995 BRp$6.recalculateNodeLabelProjection = function (node) {
23996 var content = node.pstyle('label').strValue;
23997
23998 if (emptyString(content)) {
23999 return;
24000 }
24001
24002 var textX, textY;
24003 var _p = node._private;
24004 var nodeWidth = node.width();
24005 var nodeHeight = node.height();
24006 var padding = node.padding();
24007 var nodePos = node.position();
24008 var textHalign = node.pstyle('text-halign').strValue;
24009 var textValign = node.pstyle('text-valign').strValue;
24010 var rs = _p.rscratch;
24011 var rstyle = _p.rstyle;
24012
24013 switch (textHalign) {
24014 case 'left':
24015 textX = nodePos.x - nodeWidth / 2 - padding;
24016 break;
24017
24018 case 'right':
24019 textX = nodePos.x + nodeWidth / 2 + padding;
24020 break;
24021
24022 default:
24023 // e.g. center
24024 textX = nodePos.x;
24025 }
24026
24027 switch (textValign) {
24028 case 'top':
24029 textY = nodePos.y - nodeHeight / 2 - padding;
24030 break;
24031
24032 case 'bottom':
24033 textY = nodePos.y + nodeHeight / 2 + padding;
24034 break;
24035
24036 default:
24037 // e.g. middle
24038 textY = nodePos.y;
24039 }
24040
24041 rs.labelX = textX;
24042 rs.labelY = textY;
24043 rstyle.labelX = textX;
24044 rstyle.labelY = textY;
24045 this.calculateLabelAngles(node);
24046 this.applyLabelDimensions(node);
24047 };
24048
24049 var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
24050 var angle = Math.atan(dy / dx);
24051
24052 if (dx === 0 && angle < 0) {
24053 angle = angle * -1;
24054 }
24055
24056 return angle;
24057 };
24058
24059 var lineAngle = function lineAngle(p0, p1) {
24060 var dx = p1.x - p0.x;
24061 var dy = p1.y - p0.y;
24062 return lineAngleFromDelta(dx, dy);
24063 };
24064
24065 var bezierAngle = function bezierAngle(p0, p1, p2, t) {
24066 var t0 = bound(0, t - 0.001, 1);
24067 var t1 = bound(0, t + 0.001, 1);
24068 var lp0 = qbezierPtAt(p0, p1, p2, t0);
24069 var lp1 = qbezierPtAt(p0, p1, p2, t1);
24070 return lineAngle(lp0, lp1);
24071 };
24072
24073 BRp$6.recalculateEdgeLabelProjections = function (edge) {
24074 var p;
24075 var _p = edge._private;
24076 var rs = _p.rscratch;
24077 var r = this;
24078 var content = {
24079 mid: edge.pstyle('label').strValue,
24080 source: edge.pstyle('source-label').strValue,
24081 target: edge.pstyle('target-label').strValue
24082 };
24083
24084 if (content.mid || content.source || content.target) ; else {
24085 return; // no labels => no calcs
24086 } // add center point to style so bounding box calculations can use it
24087 //
24088
24089
24090 p = {
24091 x: rs.midX,
24092 y: rs.midY
24093 };
24094
24095 var setRs = function setRs(propName, prefix, value) {
24096 setPrefixedProperty(_p.rscratch, propName, prefix, value);
24097 setPrefixedProperty(_p.rstyle, propName, prefix, value);
24098 };
24099
24100 setRs('labelX', null, p.x);
24101 setRs('labelY', null, p.y);
24102 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
24103 setRs('labelAutoAngle', null, midAngle);
24104
24105 var createControlPointInfo = function createControlPointInfo() {
24106 if (createControlPointInfo.cache) {
24107 return createControlPointInfo.cache;
24108 } // use cache so only 1x per edge
24109
24110
24111 var ctrlpts = []; // store each ctrlpt info init
24112
24113 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
24114 var p0 = {
24115 x: rs.allpts[i],
24116 y: rs.allpts[i + 1]
24117 };
24118 var p1 = {
24119 x: rs.allpts[i + 2],
24120 y: rs.allpts[i + 3]
24121 }; // ctrlpt
24122
24123 var p2 = {
24124 x: rs.allpts[i + 4],
24125 y: rs.allpts[i + 5]
24126 };
24127 ctrlpts.push({
24128 p0: p0,
24129 p1: p1,
24130 p2: p2,
24131 startDist: 0,
24132 length: 0,
24133 segments: []
24134 });
24135 }
24136
24137 var bpts = _p.rstyle.bezierPts;
24138 var nProjs = r.bezierProjPcts.length;
24139
24140 function addSegment(cp, p0, p1, t0, t1) {
24141 var length = dist(p0, p1);
24142 var prevSegment = cp.segments[cp.segments.length - 1];
24143 var segment = {
24144 p0: p0,
24145 p1: p1,
24146 t0: t0,
24147 t1: t1,
24148 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
24149 length: length
24150 };
24151 cp.segments.push(segment);
24152 cp.length += length;
24153 } // update each ctrlpt with segment info
24154
24155
24156 for (var _i = 0; _i < ctrlpts.length; _i++) {
24157 var cp = ctrlpts[_i];
24158 var prevCp = ctrlpts[_i - 1];
24159
24160 if (prevCp) {
24161 cp.startDist = prevCp.startDist + prevCp.length;
24162 }
24163
24164 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
24165
24166 for (var j = 0; j < nProjs - 1; j++) {
24167 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
24168 }
24169
24170 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
24171 }
24172
24173 return createControlPointInfo.cache = ctrlpts;
24174 };
24175
24176 var calculateEndProjection = function calculateEndProjection(prefix) {
24177 var angle;
24178 var isSrc = prefix === 'source';
24179
24180 if (!content[prefix]) {
24181 return;
24182 }
24183
24184 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
24185
24186 switch (rs.edgeType) {
24187 case 'self':
24188 case 'compound':
24189 case 'bezier':
24190 case 'multibezier':
24191 {
24192 var cps = createControlPointInfo();
24193 var selected;
24194 var startDist = 0;
24195 var totalDist = 0; // find the segment we're on
24196
24197 for (var i = 0; i < cps.length; i++) {
24198 var _cp = cps[isSrc ? i : cps.length - 1 - i];
24199
24200 for (var j = 0; j < _cp.segments.length; j++) {
24201 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
24202 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
24203 startDist = totalDist;
24204 totalDist += _seg.length;
24205
24206 if (totalDist >= offset || lastSeg) {
24207 selected = {
24208 cp: _cp,
24209 segment: _seg
24210 };
24211 break;
24212 }
24213 }
24214
24215 if (selected) {
24216 break;
24217 }
24218 }
24219
24220 var cp = selected.cp;
24221 var seg = selected.segment;
24222 var tSegment = (offset - startDist) / seg.length;
24223 var segDt = seg.t1 - seg.t0;
24224 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
24225 t = bound(0, t, 1);
24226 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
24227 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
24228 break;
24229 }
24230
24231 case 'straight':
24232 case 'segments':
24233 case 'haystack':
24234 {
24235 var d = 0,
24236 di,
24237 d0;
24238 var p0, p1;
24239 var l = rs.allpts.length;
24240
24241 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
24242 if (isSrc) {
24243 p0 = {
24244 x: rs.allpts[_i2],
24245 y: rs.allpts[_i2 + 1]
24246 };
24247 p1 = {
24248 x: rs.allpts[_i2 + 2],
24249 y: rs.allpts[_i2 + 3]
24250 };
24251 } else {
24252 p0 = {
24253 x: rs.allpts[l - 2 - _i2],
24254 y: rs.allpts[l - 1 - _i2]
24255 };
24256 p1 = {
24257 x: rs.allpts[l - 4 - _i2],
24258 y: rs.allpts[l - 3 - _i2]
24259 };
24260 }
24261
24262 di = dist(p0, p1);
24263 d0 = d;
24264 d += di;
24265
24266 if (d >= offset) {
24267 break;
24268 }
24269 }
24270
24271 var pD = offset - d0;
24272
24273 var _t = pD / di;
24274
24275 _t = bound(0, _t, 1);
24276 p = lineAt(p0, p1, _t);
24277 angle = lineAngle(p0, p1);
24278 break;
24279 }
24280 }
24281
24282 setRs('labelX', prefix, p.x);
24283 setRs('labelY', prefix, p.y);
24284 setRs('labelAutoAngle', prefix, angle);
24285 };
24286
24287 calculateEndProjection('source');
24288 calculateEndProjection('target');
24289 this.applyLabelDimensions(edge);
24290 };
24291
24292 BRp$6.applyLabelDimensions = function (ele) {
24293 this.applyPrefixedLabelDimensions(ele);
24294
24295 if (ele.isEdge()) {
24296 this.applyPrefixedLabelDimensions(ele, 'source');
24297 this.applyPrefixedLabelDimensions(ele, 'target');
24298 }
24299 };
24300
24301 BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
24302 var _p = ele._private;
24303 var text = this.getLabelText(ele, prefix);
24304 var labelDims = this.calculateLabelDimensions(ele, text);
24305 var lineHeight = ele.pstyle('line-height').pfValue;
24306 var textWrap = ele.pstyle('text-wrap').strValue;
24307 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
24308 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
24309 var normPerLineHeight = labelDims.height / numLines;
24310 var labelLineHeight = normPerLineHeight * lineHeight;
24311 var width = labelDims.width;
24312 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
24313 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
24314 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
24315 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
24316 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
24317 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
24318 };
24319
24320 BRp$6.getLabelText = function (ele, prefix) {
24321 var _p = ele._private;
24322 var pfd = prefix ? prefix + '-' : '';
24323 var text = ele.pstyle(pfd + 'label').strValue;
24324 var textTransform = ele.pstyle('text-transform').value;
24325
24326 var rscratch = function rscratch(propName, value) {
24327 if (value) {
24328 setPrefixedProperty(_p.rscratch, propName, prefix, value);
24329 return value;
24330 } else {
24331 return getPrefixedProperty(_p.rscratch, propName, prefix);
24332 }
24333 }; // for empty text, skip all processing
24334
24335
24336 if (!text) {
24337 return '';
24338 }
24339
24340 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
24341 text = text.toUpperCase();
24342 } else if (textTransform == 'lowercase') {
24343 text = text.toLowerCase();
24344 }
24345
24346 var wrapStyle = ele.pstyle('text-wrap').value;
24347
24348 if (wrapStyle === 'wrap') {
24349 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
24350
24351 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
24352 return rscratch('labelWrapCachedText');
24353 }
24354
24355 var zwsp = "\u200B";
24356 var lines = text.split('\n');
24357 var maxW = ele.pstyle('text-max-width').pfValue;
24358 var overflow = ele.pstyle('text-overflow-wrap').value;
24359 var overflowAny = overflow === 'anywhere';
24360 var wrappedLines = [];
24361 var wordsRegex = /[\s\u200b]+/;
24362 var wordSeparator = overflowAny ? '' : ' ';
24363
24364 for (var l = 0; l < lines.length; l++) {
24365 var line = lines[l];
24366 var lineDims = this.calculateLabelDimensions(ele, line);
24367 var lineW = lineDims.width;
24368
24369 if (overflowAny) {
24370 var processedLine = line.split('').join(zwsp);
24371 line = processedLine;
24372 }
24373
24374 if (lineW > maxW) {
24375 // line is too long
24376 var words = line.split(wordsRegex);
24377 var subline = '';
24378
24379 for (var w = 0; w < words.length; w++) {
24380 var word = words[w];
24381 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
24382 var testDims = this.calculateLabelDimensions(ele, testLine);
24383 var testW = testDims.width;
24384
24385 if (testW <= maxW) {
24386 // word fits on current line
24387 subline += word + wordSeparator;
24388 } else {
24389 // word starts new line
24390 if (subline) {
24391 wrappedLines.push(subline);
24392 }
24393
24394 subline = word + wordSeparator;
24395 }
24396 } // if there's remaining text, put it in a wrapped line
24397
24398
24399 if (!subline.match(/^[\s\u200b]+$/)) {
24400 wrappedLines.push(subline);
24401 }
24402 } else {
24403 // line is already short enough
24404 wrappedLines.push(line);
24405 }
24406 } // for
24407
24408
24409 rscratch('labelWrapCachedLines', wrappedLines);
24410 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
24411 rscratch('labelWrapKey', labelKey);
24412 } else if (wrapStyle === 'ellipsis') {
24413 var _maxW = ele.pstyle('text-max-width').pfValue;
24414 var ellipsized = '';
24415 var ellipsis = "\u2026";
24416 var incLastCh = false;
24417
24418 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
24419 // the label already fits
24420 return text;
24421 }
24422
24423 for (var i = 0; i < text.length; i++) {
24424 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
24425
24426 if (widthWithNextCh > _maxW) {
24427 break;
24428 }
24429
24430 ellipsized += text[i];
24431
24432 if (i === text.length - 1) {
24433 incLastCh = true;
24434 }
24435 }
24436
24437 if (!incLastCh) {
24438 ellipsized += ellipsis;
24439 }
24440
24441 return ellipsized;
24442 } // if ellipsize
24443
24444
24445 return text;
24446 };
24447
24448 BRp$6.getLabelJustification = function (ele) {
24449 var justification = ele.pstyle('text-justification').strValue;
24450 var textHalign = ele.pstyle('text-halign').strValue;
24451
24452 if (justification === 'auto') {
24453 if (ele.isNode()) {
24454 switch (textHalign) {
24455 case 'left':
24456 return 'right';
24457
24458 case 'right':
24459 return 'left';
24460
24461 default:
24462 return 'center';
24463 }
24464 } else {
24465 return 'center';
24466 }
24467 } else {
24468 return justification;
24469 }
24470 };
24471
24472 BRp$6.calculateLabelDimensions = function (ele, text) {
24473 var r = this;
24474 var cacheKey = hashString(text, ele._private.labelDimsKey);
24475 var cache = r.labelDimCache || (r.labelDimCache = []);
24476 var existingVal = cache[cacheKey];
24477
24478 if (existingVal != null) {
24479 return existingVal;
24480 }
24481
24482 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
24483
24484 var fStyle = ele.pstyle('font-style').strValue;
24485 var size = ele.pstyle('font-size').pfValue;
24486 var family = ele.pstyle('font-family').strValue;
24487 var weight = ele.pstyle('font-weight').strValue;
24488 var canvas = this.labelCalcCanvas;
24489 var c2d = this.labelCalcCanvasContext;
24490
24491 if (!canvas) {
24492 canvas = this.labelCalcCanvas = document.createElement('canvas');
24493 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
24494 var ds = canvas.style;
24495 ds.position = 'absolute';
24496 ds.left = '-9999px';
24497 ds.top = '-9999px';
24498 ds.zIndex = '-1';
24499 ds.visibility = 'hidden';
24500 ds.pointerEvents = 'none';
24501 }
24502
24503 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
24504 var width = 0;
24505 var height = 0;
24506 var lines = text.split('\n');
24507
24508 for (var i = 0; i < lines.length; i++) {
24509 var line = lines[i];
24510 var metrics = c2d.measureText(line);
24511 var w = Math.ceil(metrics.width);
24512 var h = size;
24513 width = Math.max(w, width);
24514 height += h;
24515 }
24516
24517 width += padding;
24518 height += padding;
24519 return cache[cacheKey] = {
24520 width: width,
24521 height: height
24522 };
24523 };
24524
24525 BRp$6.calculateLabelAngle = function (ele, prefix) {
24526 var _p = ele._private;
24527 var rs = _p.rscratch;
24528 var isEdge = ele.isEdge();
24529 var prefixDash = prefix ? prefix + '-' : '';
24530 var rot = ele.pstyle(prefixDash + 'text-rotation');
24531 var rotStr = rot.strValue;
24532
24533 if (rotStr === 'none') {
24534 return 0;
24535 } else if (isEdge && rotStr === 'autorotate') {
24536 return rs.labelAutoAngle;
24537 } else if (rotStr === 'autorotate') {
24538 return 0;
24539 } else {
24540 return rot.pfValue;
24541 }
24542 };
24543
24544 BRp$6.calculateLabelAngles = function (ele) {
24545 var r = this;
24546 var isEdge = ele.isEdge();
24547 var _p = ele._private;
24548 var rs = _p.rscratch;
24549 rs.labelAngle = r.calculateLabelAngle(ele);
24550
24551 if (isEdge) {
24552 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
24553 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
24554 }
24555 };
24556
24557 var BRp$7 = {};
24558 var TOO_SMALL_CUT_RECT = 28;
24559 var warnedCutRect = false;
24560
24561 BRp$7.getNodeShape = function (node) {
24562 var r = this;
24563 var shape = node.pstyle('shape').value;
24564
24565 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
24566 if (!warnedCutRect) {
24567 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
24568 warnedCutRect = true;
24569 }
24570
24571 return 'rectangle';
24572 }
24573
24574 if (node.isParent()) {
24575 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
24576 return shape;
24577 } else {
24578 return 'rectangle';
24579 }
24580 }
24581
24582 if (shape === 'polygon') {
24583 var points = node.pstyle('shape-polygon-points').value;
24584 return r.nodeShapes.makePolygon(points).name;
24585 }
24586
24587 return shape;
24588 };
24589
24590 var BRp$8 = {};
24591
24592 BRp$8.registerCalculationListeners = function () {
24593 var cy = this.cy;
24594 var elesToUpdate = cy.collection();
24595 var r = this;
24596
24597 var enqueue = function enqueue(eles) {
24598 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
24599 elesToUpdate.merge(eles);
24600
24601 if (dirtyStyleCaches) {
24602 for (var i = 0; i < eles.length; i++) {
24603 var ele = eles[i];
24604 var _p = ele._private;
24605 var rstyle = _p.rstyle;
24606 rstyle.clean = false;
24607 rstyle.cleanConnected = false;
24608 }
24609 }
24610 };
24611
24612 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24613 var ele = e.target;
24614 enqueue(ele);
24615 }).on('style.* background.*', function onDirtyStyle(e) {
24616 var ele = e.target;
24617 enqueue(ele, false);
24618 });
24619
24620 var updateEleCalcs = function updateEleCalcs(willDraw) {
24621 if (willDraw) {
24622 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
24623 // before calculating rendered style (and pstyle might not be called yet)
24624
24625 elesToUpdate.cleanStyle();
24626
24627 for (var i = 0; i < elesToUpdate.length; i++) {
24628 var ele = elesToUpdate[i];
24629 var rstyle = ele._private.rstyle;
24630
24631 if (ele.isNode() && !rstyle.cleanConnected) {
24632 enqueue(ele.connectedEdges());
24633 rstyle.cleanConnected = true;
24634 }
24635 }
24636
24637 if (fns) {
24638 for (var _i = 0; _i < fns.length; _i++) {
24639 var fn = fns[_i];
24640 fn(willDraw, elesToUpdate);
24641 }
24642 }
24643
24644 r.recalculateRenderedStyle(elesToUpdate);
24645 elesToUpdate = cy.collection();
24646 }
24647 };
24648
24649 r.flushRenderedStyleQueue = function () {
24650 updateEleCalcs(true);
24651 };
24652
24653 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24654 };
24655
24656 BRp$8.onUpdateEleCalcs = function (fn) {
24657 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24658 fns.push(fn);
24659 };
24660
24661 BRp$8.recalculateRenderedStyle = function (eles, useCache) {
24662 var isCleanConnected = function isCleanConnected(ele) {
24663 return ele._private.rstyle.cleanConnected;
24664 };
24665
24666 var edges = [];
24667 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24668
24669 if (this.destroyed) {
24670 return;
24671 } // use cache by default for perf
24672
24673
24674 if (useCache === undefined) {
24675 useCache = true;
24676 }
24677
24678 for (var i = 0; i < eles.length; i++) {
24679 var ele = eles[i];
24680 var _p = ele._private;
24681 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
24682 // (and a request for recalc may come in between frames)
24683
24684 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24685 rstyle.clean = false;
24686 } // only update if dirty and in graph
24687
24688
24689 if (useCache && rstyle.clean || ele.removed()) {
24690 continue;
24691 } // only update if not display: none
24692
24693
24694 if (ele.pstyle('display').value === 'none') {
24695 continue;
24696 }
24697
24698 if (_p.group === 'nodes') {
24699 nodes.push(ele);
24700 } else {
24701 // edges
24702 edges.push(ele);
24703 }
24704
24705 rstyle.clean = true;
24706 } // update node data from projections
24707
24708
24709 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24710 var _ele = nodes[_i2];
24711 var _p2 = _ele._private;
24712 var _rstyle = _p2.rstyle;
24713
24714 var pos = _ele.position();
24715
24716 this.recalculateNodeLabelProjection(_ele);
24717 _rstyle.nodeX = pos.x;
24718 _rstyle.nodeY = pos.y;
24719 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24720 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24721 }
24722
24723 this.recalculateEdgeProjections(edges); // update edge data from projections
24724
24725 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24726 var _ele2 = edges[_i3];
24727 var _p3 = _ele2._private;
24728 var _rstyle2 = _p3.rstyle;
24729 var rs = _p3.rscratch; // update rstyle positions
24730
24731 _rstyle2.srcX = rs.arrowStartX;
24732 _rstyle2.srcY = rs.arrowStartY;
24733 _rstyle2.tgtX = rs.arrowEndX;
24734 _rstyle2.tgtY = rs.arrowEndY;
24735 _rstyle2.midX = rs.midX;
24736 _rstyle2.midY = rs.midY;
24737 _rstyle2.labelAngle = rs.labelAngle;
24738 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24739 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24740 }
24741 };
24742
24743 var BRp$9 = {};
24744
24745 BRp$9.updateCachedGrabbedEles = function () {
24746 var eles = this.cachedZSortedEles;
24747
24748 if (!eles) {
24749 // just let this be recalculated on the next z sort tick
24750 return;
24751 }
24752
24753 eles.drag = [];
24754 eles.nondrag = [];
24755 var grabTargets = [];
24756
24757 for (var i = 0; i < eles.length; i++) {
24758 var ele = eles[i];
24759 var rs = ele._private.rscratch;
24760
24761 if (ele.grabbed() && !ele.isParent()) {
24762 grabTargets.push(ele);
24763 } else if (rs.inDragLayer) {
24764 eles.drag.push(ele);
24765 } else {
24766 eles.nondrag.push(ele);
24767 }
24768 } // put the grab target nodes last so it's on top of its neighbourhood
24769
24770
24771 for (var i = 0; i < grabTargets.length; i++) {
24772 var ele = grabTargets[i];
24773 eles.drag.push(ele);
24774 }
24775 };
24776
24777 BRp$9.invalidateCachedZSortedEles = function () {
24778 this.cachedZSortedEles = null;
24779 };
24780
24781 BRp$9.getCachedZSortedEles = function (forceRecalc) {
24782 if (forceRecalc || !this.cachedZSortedEles) {
24783 var eles = this.cy.mutableElements().toArray();
24784 eles.sort(zIndexSort);
24785 eles.interactive = eles.filter(function (ele) {
24786 return ele.interactive();
24787 });
24788 this.cachedZSortedEles = eles;
24789 this.updateCachedGrabbedEles();
24790 } else {
24791 eles = this.cachedZSortedEles;
24792 }
24793
24794 return eles;
24795 };
24796
24797 var BRp$a = {};
24798 [BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24799 extend(BRp$a, props);
24800 });
24801
24802 var BRp$b = {};
24803
24804 BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24805 var r = this;
24806 var imageCache = r.imageCache = r.imageCache || {};
24807 var cache = imageCache[url];
24808
24809 if (cache) {
24810 if (!cache.image.complete) {
24811 cache.image.addEventListener('load', onLoad);
24812 }
24813
24814 return cache.image;
24815 } else {
24816 cache = imageCache[url] = imageCache[url] || {};
24817 var image = cache.image = new Image(); // eslint-disable-line no-undef
24818
24819 image.addEventListener('load', onLoad);
24820 image.addEventListener('error', function () {
24821 image.error = true;
24822 }); // #1582 safari doesn't load data uris with crossOrigin properly
24823 // https://bugs.webkit.org/show_bug.cgi?id=123978
24824
24825 var dataUriPrefix = 'data:';
24826 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24827
24828 if (!isDataUri) {
24829 image.crossOrigin = crossOrigin; // prevent tainted canvas
24830 }
24831
24832 image.src = url;
24833 return image;
24834 }
24835 };
24836
24837 var BRp$c = {};
24838 /* global document, window, ResizeObserver, MutationObserver */
24839
24840 BRp$c.registerBinding = function (target, event, handler, useCapture) {
24841 // eslint-disable-line no-unused-vars
24842 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24843
24844 var b = this.binder(target);
24845 return b.on.apply(b, args);
24846 };
24847
24848 BRp$c.binder = function (tgt) {
24849 var r = this;
24850 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24851
24852 if (r.supportsPassiveEvents == null) {
24853 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24854 var supportsPassive = false;
24855
24856 try {
24857 var opts = Object.defineProperty({}, 'passive', {
24858 get: function get() {
24859 supportsPassive = true;
24860 return true;
24861 }
24862 });
24863 window.addEventListener('test', null, opts);
24864 } catch (err) {// not supported
24865 }
24866
24867 r.supportsPassiveEvents = supportsPassive;
24868 }
24869
24870 var on = function on(event, handler, useCapture) {
24871 var args = Array.prototype.slice.call(arguments);
24872
24873 if (tgtIsDom && r.supportsPassiveEvents) {
24874 // replace useCapture w/ opts obj
24875 args[2] = {
24876 capture: useCapture != null ? useCapture : false,
24877 passive: false,
24878 once: false
24879 };
24880 }
24881
24882 r.bindings.push({
24883 target: tgt,
24884 args: args
24885 });
24886 (tgt.addEventListener || tgt.on).apply(tgt, args);
24887 return this;
24888 };
24889
24890 return {
24891 on: on,
24892 addEventListener: on,
24893 addListener: on,
24894 bind: on
24895 };
24896 };
24897
24898 BRp$c.nodeIsDraggable = function (node) {
24899 return node && node.isNode() && !node.locked() && node.grabbable();
24900 };
24901
24902 BRp$c.nodeIsGrabbable = function (node) {
24903 return this.nodeIsDraggable(node) && node.interactive();
24904 };
24905
24906 BRp$c.load = function () {
24907 var r = this;
24908
24909 var isSelected = function isSelected(ele) {
24910 return ele.selected();
24911 };
24912
24913 var triggerEvents = function triggerEvents(target, names, e, position) {
24914 if (target == null) {
24915 target = r.cy;
24916 }
24917
24918 for (var i = 0; i < names.length; i++) {
24919 var name = names[i];
24920 target.emit({
24921 originalEvent: e,
24922 type: name,
24923 position: position
24924 });
24925 }
24926 };
24927
24928 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24929 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24930 };
24931
24932 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24933 var allowPassthrough = true;
24934
24935 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24936 // a grabbable compound node below the ele => no passthrough panning
24937 for (var i = 0; downs && i < downs.length; i++) {
24938 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24939
24940 if (down.isNode() && down.isParent() && !down.pannable()) {
24941 allowPassthrough = false;
24942 break;
24943 }
24944 }
24945 } else {
24946 allowPassthrough = true;
24947 }
24948
24949 return allowPassthrough;
24950 };
24951
24952 var setGrabbed = function setGrabbed(ele) {
24953 ele[0]._private.grabbed = true;
24954 };
24955
24956 var setFreed = function setFreed(ele) {
24957 ele[0]._private.grabbed = false;
24958 };
24959
24960 var setInDragLayer = function setInDragLayer(ele) {
24961 ele[0]._private.rscratch.inDragLayer = true;
24962 };
24963
24964 var setOutDragLayer = function setOutDragLayer(ele) {
24965 ele[0]._private.rscratch.inDragLayer = false;
24966 };
24967
24968 var setGrabTarget = function setGrabTarget(ele) {
24969 ele[0]._private.rscratch.isGrabTarget = true;
24970 };
24971
24972 var removeGrabTarget = function removeGrabTarget(ele) {
24973 ele[0]._private.rscratch.isGrabTarget = false;
24974 };
24975
24976 var addToDragList = function addToDragList(ele, opts) {
24977 var list = opts.addToList;
24978 var listHasEle = list.has(ele);
24979
24980 if (!listHasEle) {
24981 list.merge(ele);
24982 setGrabbed(ele);
24983 }
24984 }; // helper function to determine which child nodes and inner edges
24985 // of a compound node to be dragged as well as the grabbed and selected nodes
24986
24987
24988 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24989 if (!node.cy().hasCompoundNodes()) {
24990 return;
24991 }
24992
24993 if (opts.inDragLayer == null && opts.addToList == null) {
24994 return;
24995 } // nothing to do
24996
24997
24998 var innerNodes = node.descendants();
24999
25000 if (opts.inDragLayer) {
25001 innerNodes.forEach(setInDragLayer);
25002 innerNodes.connectedEdges().forEach(setInDragLayer);
25003 }
25004
25005 if (opts.addToList) {
25006 opts.addToList.unmerge(innerNodes);
25007 }
25008 }; // adds the given nodes and its neighbourhood to the drag layer
25009
25010
25011 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
25012 opts = opts || {};
25013 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
25014
25015 if (opts.inDragLayer) {
25016 nodes.forEach(setInDragLayer);
25017 nodes.neighborhood().stdFilter(function (ele) {
25018 return !hasCompoundNodes || ele.isEdge();
25019 }).forEach(setInDragLayer);
25020 }
25021
25022 if (opts.addToList) {
25023 nodes.forEach(function (ele) {
25024 addToDragList(ele, opts);
25025 });
25026 }
25027
25028 addDescendantsToDrag(nodes, opts); // always add to drag
25029 // also add nodes and edges related to the topmost ancestor
25030
25031 updateAncestorsInDragLayer(nodes, {
25032 inDragLayer: opts.inDragLayer
25033 });
25034 r.updateCachedGrabbedEles();
25035 };
25036
25037 var addNodeToDrag = addNodesToDrag;
25038
25039 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
25040 if (!grabbedEles) {
25041 return;
25042 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
25043
25044
25045 r.getCachedZSortedEles().forEach(function (ele) {
25046 setFreed(ele);
25047 setOutDragLayer(ele);
25048 removeGrabTarget(ele);
25049 });
25050 r.updateCachedGrabbedEles();
25051 }; // helper function to determine which ancestor nodes and edges should go
25052 // to the drag layer (or should be removed from drag layer).
25053
25054
25055 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
25056 if (opts.inDragLayer == null && opts.addToList == null) {
25057 return;
25058 } // nothing to do
25059
25060
25061 if (!node.cy().hasCompoundNodes()) {
25062 return;
25063 } // find top-level parent
25064
25065
25066 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
25067
25068 if (parent.same(node)) {
25069 return;
25070 }
25071
25072 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
25073 var edges = nodes.connectedEdges();
25074
25075 if (opts.inDragLayer) {
25076 edges.forEach(setInDragLayer);
25077 nodes.forEach(setInDragLayer);
25078 }
25079
25080 if (opts.addToList) {
25081 nodes.forEach(function (ele) {
25082 addToDragList(ele, opts);
25083 });
25084 }
25085 };
25086
25087 var blurActiveDomElement = function blurActiveDomElement() {
25088 if (document.activeElement != null && document.activeElement.blur != null) {
25089 document.activeElement.blur();
25090 }
25091 };
25092
25093 var haveMutationsApi = typeof MutationObserver !== 'undefined';
25094 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
25095
25096 if (haveMutationsApi) {
25097 r.removeObserver = new MutationObserver(function (mutns) {
25098 // eslint-disable-line no-undef
25099 for (var i = 0; i < mutns.length; i++) {
25100 var mutn = mutns[i];
25101 var rNodes = mutn.removedNodes;
25102
25103 if (rNodes) {
25104 for (var j = 0; j < rNodes.length; j++) {
25105 var rNode = rNodes[j];
25106
25107 if (rNode === r.container) {
25108 r.destroy();
25109 break;
25110 }
25111 }
25112 }
25113 }
25114 });
25115
25116 if (r.container.parentNode) {
25117 r.removeObserver.observe(r.container.parentNode, {
25118 childList: true
25119 });
25120 }
25121 } else {
25122 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
25123 // eslint-disable-line no-unused-vars
25124 r.destroy();
25125 });
25126 }
25127
25128 var onResize = lodash_debounce(function () {
25129 r.cy.resize();
25130 }, 100);
25131
25132 if (haveMutationsApi) {
25133 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
25134
25135 r.styleObserver.observe(r.container, {
25136 attributes: true
25137 });
25138 } // auto resize
25139
25140
25141 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
25142
25143 if (haveResizeObserverApi) {
25144 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
25145
25146 r.resizeObserver.observe(r.container);
25147 }
25148
25149 var forEachUp = function forEachUp(domEle, fn) {
25150 while (domEle != null) {
25151 fn(domEle);
25152 domEle = domEle.parentNode;
25153 }
25154 };
25155
25156 var invalidateCoords = function invalidateCoords() {
25157 r.invalidateContainerClientCoordsCache();
25158 };
25159
25160 forEachUp(r.container, function (domEle) {
25161 r.registerBinding(domEle, 'transitionend', invalidateCoords);
25162 r.registerBinding(domEle, 'animationend', invalidateCoords);
25163 r.registerBinding(domEle, 'scroll', invalidateCoords);
25164 }); // stop right click menu from appearing on cy
25165
25166 r.registerBinding(r.container, 'contextmenu', function (e) {
25167 e.preventDefault();
25168 });
25169
25170 var inBoxSelection = function inBoxSelection() {
25171 return r.selection[4] !== 0;
25172 };
25173
25174 var eventInContainer = function eventInContainer(e) {
25175 // save cycles if mouse events aren't to be captured
25176 var containerPageCoords = r.findContainerClientCoords();
25177 var x = containerPageCoords[0];
25178 var y = containerPageCoords[1];
25179 var width = containerPageCoords[2];
25180 var height = containerPageCoords[3];
25181 var positions = e.touches ? e.touches : [e];
25182 var atLeastOnePosInside = false;
25183
25184 for (var i = 0; i < positions.length; i++) {
25185 var p = positions[i];
25186
25187 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
25188 atLeastOnePosInside = true;
25189 break;
25190 }
25191 }
25192
25193 if (!atLeastOnePosInside) {
25194 return false;
25195 }
25196
25197 var container = r.container;
25198 var target = e.target;
25199 var tParent = target.parentNode;
25200 var containerIsTarget = false;
25201
25202 while (tParent) {
25203 if (tParent === container) {
25204 containerIsTarget = true;
25205 break;
25206 }
25207
25208 tParent = tParent.parentNode;
25209 }
25210
25211 if (!containerIsTarget) {
25212 return false;
25213 } // if target is outisde cy container, then this event is not for us
25214
25215
25216 return true;
25217 }; // Primary key
25218
25219
25220 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
25221 if (!eventInContainer(e)) {
25222 return;
25223 }
25224
25225 e.preventDefault();
25226 blurActiveDomElement();
25227 r.hoverData.capture = true;
25228 r.hoverData.which = e.which;
25229 var cy = r.cy;
25230 var gpos = [e.clientX, e.clientY];
25231 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
25232 var select = r.selection;
25233 var nears = r.findNearestElements(pos[0], pos[1], true, false);
25234 var near = nears[0];
25235 var draggedElements = r.dragData.possibleDragElements;
25236 r.hoverData.mdownPos = pos;
25237 r.hoverData.mdownGPos = gpos;
25238
25239 var checkForTaphold = function checkForTaphold() {
25240 r.hoverData.tapholdCancelled = false;
25241 clearTimeout(r.hoverData.tapholdTimeout);
25242 r.hoverData.tapholdTimeout = setTimeout(function () {
25243 if (r.hoverData.tapholdCancelled) {
25244 return;
25245 } else {
25246 var ele = r.hoverData.down;
25247
25248 if (ele) {
25249 ele.emit({
25250 originalEvent: e,
25251 type: 'taphold',
25252 position: {
25253 x: pos[0],
25254 y: pos[1]
25255 }
25256 });
25257 } else {
25258 cy.emit({
25259 originalEvent: e,
25260 type: 'taphold',
25261 position: {
25262 x: pos[0],
25263 y: pos[1]
25264 }
25265 });
25266 }
25267 }
25268 }, r.tapholdDuration);
25269 }; // Right click button
25270
25271
25272 if (e.which == 3) {
25273 r.hoverData.cxtStarted = true;
25274 var cxtEvt = {
25275 originalEvent: e,
25276 type: 'cxttapstart',
25277 position: {
25278 x: pos[0],
25279 y: pos[1]
25280 }
25281 };
25282
25283 if (near) {
25284 near.activate();
25285 near.emit(cxtEvt);
25286 r.hoverData.down = near;
25287 } else {
25288 cy.emit(cxtEvt);
25289 }
25290
25291 r.hoverData.downTime = new Date().getTime();
25292 r.hoverData.cxtDragged = false; // Primary button
25293 } else if (e.which == 1) {
25294 if (near) {
25295 near.activate();
25296 } // Element dragging
25297
25298
25299 {
25300 // If something is under the cursor and it is draggable, prepare to grab it
25301 if (near != null) {
25302 if (r.nodeIsGrabbable(near)) {
25303 var makeEvent = function makeEvent(type) {
25304 return {
25305 originalEvent: e,
25306 type: type,
25307 position: {
25308 x: pos[0],
25309 y: pos[1]
25310 }
25311 };
25312 };
25313
25314 var triggerGrab = function triggerGrab(ele) {
25315 ele.emit(makeEvent('grab'));
25316 };
25317
25318 setGrabTarget(near);
25319
25320 if (!near.selected()) {
25321 draggedElements = r.dragData.possibleDragElements = cy.collection();
25322 addNodeToDrag(near, {
25323 addToList: draggedElements
25324 });
25325 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
25326 } else {
25327 draggedElements = r.dragData.possibleDragElements = cy.collection();
25328 var selectedNodes = cy.$(function (ele) {
25329 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
25330 });
25331 addNodesToDrag(selectedNodes, {
25332 addToList: draggedElements
25333 });
25334 near.emit(makeEvent('grabon'));
25335 selectedNodes.forEach(triggerGrab);
25336 }
25337
25338 r.redrawHint('eles', true);
25339 r.redrawHint('drag', true);
25340 }
25341 }
25342
25343 r.hoverData.down = near;
25344 r.hoverData.downs = nears;
25345 r.hoverData.downTime = new Date().getTime();
25346 }
25347 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
25348 x: pos[0],
25349 y: pos[1]
25350 });
25351
25352 if (near == null) {
25353 select[4] = 1;
25354 r.data.bgActivePosistion = {
25355 x: pos[0],
25356 y: pos[1]
25357 };
25358 r.redrawHint('select', true);
25359 r.redraw();
25360 } else if (near.pannable()) {
25361 select[4] = 1; // for future pan
25362 }
25363
25364 checkForTaphold();
25365 } // Initialize selection box coordinates
25366
25367
25368 select[0] = select[2] = pos[0];
25369 select[1] = select[3] = pos[1];
25370 }, false);
25371 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
25372 // eslint-disable-line no-undef
25373 var capture = r.hoverData.capture;
25374
25375 if (!capture && !eventInContainer(e)) {
25376 return;
25377 }
25378
25379 var preventDefault = false;
25380 var cy = r.cy;
25381 var zoom = cy.zoom();
25382 var gpos = [e.clientX, e.clientY];
25383 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
25384 var mdownPos = r.hoverData.mdownPos;
25385 var mdownGPos = r.hoverData.mdownGPos;
25386 var select = r.selection;
25387 var near = null;
25388
25389 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
25390 near = r.findNearestElement(pos[0], pos[1], true, false);
25391 }
25392
25393 var last = r.hoverData.last;
25394 var down = r.hoverData.down;
25395 var disp = [pos[0] - select[2], pos[1] - select[3]];
25396 var draggedElements = r.dragData.possibleDragElements;
25397 var isOverThresholdDrag;
25398
25399 if (mdownGPos) {
25400 var dx = gpos[0] - mdownGPos[0];
25401 var dx2 = dx * dx;
25402 var dy = gpos[1] - mdownGPos[1];
25403 var dy2 = dy * dy;
25404 var dist2 = dx2 + dy2;
25405 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
25406 }
25407
25408 var multSelKeyDown = isMultSelKeyDown(e);
25409
25410 if (isOverThresholdDrag) {
25411 r.hoverData.tapholdCancelled = true;
25412 }
25413
25414 var updateDragDelta = function updateDragDelta() {
25415 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
25416
25417 if (dragDelta.length === 0) {
25418 dragDelta.push(disp[0]);
25419 dragDelta.push(disp[1]);
25420 } else {
25421 dragDelta[0] += disp[0];
25422 dragDelta[1] += disp[1];
25423 }
25424 };
25425
25426 preventDefault = true;
25427 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
25428 x: pos[0],
25429 y: pos[1]
25430 });
25431
25432 var goIntoBoxMode = function goIntoBoxMode() {
25433 r.data.bgActivePosistion = undefined;
25434
25435 if (!r.hoverData.selecting) {
25436 cy.emit({
25437 originalEvent: e,
25438 type: 'boxstart',
25439 position: {
25440 x: pos[0],
25441 y: pos[1]
25442 }
25443 });
25444 }
25445
25446 select[4] = 1;
25447 r.hoverData.selecting = true;
25448 r.redrawHint('select', true);
25449 r.redraw();
25450 }; // trigger context drag if rmouse down
25451
25452
25453 if (r.hoverData.which === 3) {
25454 // but only if over threshold
25455 if (isOverThresholdDrag) {
25456 var cxtEvt = {
25457 originalEvent: e,
25458 type: 'cxtdrag',
25459 position: {
25460 x: pos[0],
25461 y: pos[1]
25462 }
25463 };
25464
25465 if (down) {
25466 down.emit(cxtEvt);
25467 } else {
25468 cy.emit(cxtEvt);
25469 }
25470
25471 r.hoverData.cxtDragged = true;
25472
25473 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
25474 if (r.hoverData.cxtOver) {
25475 r.hoverData.cxtOver.emit({
25476 originalEvent: e,
25477 type: 'cxtdragout',
25478 position: {
25479 x: pos[0],
25480 y: pos[1]
25481 }
25482 });
25483 }
25484
25485 r.hoverData.cxtOver = near;
25486
25487 if (near) {
25488 near.emit({
25489 originalEvent: e,
25490 type: 'cxtdragover',
25491 position: {
25492 x: pos[0],
25493 y: pos[1]
25494 }
25495 });
25496 }
25497 }
25498 } // Check if we are drag panning the entire graph
25499
25500 } else if (r.hoverData.dragging) {
25501 preventDefault = true;
25502
25503 if (cy.panningEnabled() && cy.userPanningEnabled()) {
25504 var deltaP;
25505
25506 if (r.hoverData.justStartedPan) {
25507 var mdPos = r.hoverData.mdownPos;
25508 deltaP = {
25509 x: (pos[0] - mdPos[0]) * zoom,
25510 y: (pos[1] - mdPos[1]) * zoom
25511 };
25512 r.hoverData.justStartedPan = false;
25513 } else {
25514 deltaP = {
25515 x: disp[0] * zoom,
25516 y: disp[1] * zoom
25517 };
25518 }
25519
25520 cy.panBy(deltaP);
25521 cy.emit('dragpan');
25522 r.hoverData.dragged = true;
25523 } // Needs reproject due to pan changing viewport
25524
25525
25526 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
25527 } else if (select[4] == 1 && (down == null || down.pannable())) {
25528 if (isOverThresholdDrag) {
25529 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
25530 goIntoBoxMode();
25531 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
25532 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
25533
25534 if (allowPassthrough) {
25535 r.hoverData.dragging = true;
25536 r.hoverData.justStartedPan = true;
25537 select[4] = 0;
25538 r.data.bgActivePosistion = array2point(mdownPos);
25539 r.redrawHint('select', true);
25540 r.redraw();
25541 }
25542 }
25543
25544 if (down && down.pannable() && down.active()) {
25545 down.unactivate();
25546 }
25547 }
25548 } else {
25549 if (down && down.pannable() && down.active()) {
25550 down.unactivate();
25551 }
25552
25553 if ((!down || !down.grabbed()) && near != last) {
25554 if (last) {
25555 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
25556 x: pos[0],
25557 y: pos[1]
25558 });
25559 }
25560
25561 if (near) {
25562 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
25563 x: pos[0],
25564 y: pos[1]
25565 });
25566 }
25567
25568 r.hoverData.last = near;
25569 }
25570
25571 if (down) {
25572 if (isOverThresholdDrag) {
25573 // then we can take action
25574 if (cy.boxSelectionEnabled() && multSelKeyDown) {
25575 // then selection overrides
25576 if (down && down.grabbed()) {
25577 freeDraggedElements(draggedElements);
25578 down.emit('freeon');
25579 draggedElements.emit('free');
25580
25581 if (r.dragData.didDrag) {
25582 down.emit('dragfreeon');
25583 draggedElements.emit('dragfree');
25584 }
25585 }
25586
25587 goIntoBoxMode();
25588 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
25589 // drag node
25590 var justStartedDrag = !r.dragData.didDrag;
25591
25592 if (justStartedDrag) {
25593 r.redrawHint('eles', true);
25594 }
25595
25596 r.dragData.didDrag = true; // indicate that we actually did drag the node
25597
25598 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
25599
25600 if (!r.hoverData.draggingEles) {
25601 addNodesToDrag(draggedElements, {
25602 inDragLayer: true
25603 });
25604 }
25605
25606 var totalShift = {
25607 x: 0,
25608 y: 0
25609 };
25610
25611 if (number(disp[0]) && number(disp[1])) {
25612 totalShift.x += disp[0];
25613 totalShift.y += disp[1];
25614
25615 if (justStartedDrag) {
25616 var dragDelta = r.hoverData.dragDelta;
25617
25618 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25619 totalShift.x += dragDelta[0];
25620 totalShift.y += dragDelta[1];
25621 }
25622 }
25623 }
25624
25625 for (var i = 0; i < draggedElements.length; i++) {
25626 var dEle = draggedElements[i];
25627
25628 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
25629 toTrigger.push(dEle);
25630 }
25631 }
25632
25633 r.hoverData.draggingEles = true;
25634 toTrigger.silentShift(totalShift).emit('position drag');
25635 r.redrawHint('drag', true);
25636 r.redraw();
25637 }
25638 } else {
25639 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25640 updateDragDelta();
25641 }
25642 } // prevent the dragging from triggering text selection on the page
25643
25644
25645 preventDefault = true;
25646 }
25647
25648 select[2] = pos[0];
25649 select[3] = pos[1];
25650
25651 if (preventDefault) {
25652 if (e.stopPropagation) e.stopPropagation();
25653 if (e.preventDefault) e.preventDefault();
25654 return false;
25655 }
25656 }, false);
25657 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25658 // eslint-disable-line no-undef
25659 var capture = r.hoverData.capture;
25660
25661 if (!capture) {
25662 return;
25663 }
25664
25665 r.hoverData.capture = false;
25666 var cy = r.cy;
25667 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25668 var select = r.selection;
25669 var near = r.findNearestElement(pos[0], pos[1], true, false);
25670 var draggedElements = r.dragData.possibleDragElements;
25671 var down = r.hoverData.down;
25672 var multSelKeyDown = isMultSelKeyDown(e);
25673
25674 if (r.data.bgActivePosistion) {
25675 r.redrawHint('select', true);
25676 r.redraw();
25677 }
25678
25679 r.hoverData.tapholdCancelled = true;
25680 r.data.bgActivePosistion = undefined; // not active bg now
25681
25682 if (down) {
25683 down.unactivate();
25684 }
25685
25686 if (r.hoverData.which === 3) {
25687 var cxtEvt = {
25688 originalEvent: e,
25689 type: 'cxttapend',
25690 position: {
25691 x: pos[0],
25692 y: pos[1]
25693 }
25694 };
25695
25696 if (down) {
25697 down.emit(cxtEvt);
25698 } else {
25699 cy.emit(cxtEvt);
25700 }
25701
25702 if (!r.hoverData.cxtDragged) {
25703 var cxtTap = {
25704 originalEvent: e,
25705 type: 'cxttap',
25706 position: {
25707 x: pos[0],
25708 y: pos[1]
25709 }
25710 };
25711
25712 if (down) {
25713 down.emit(cxtTap);
25714 } else {
25715 cy.emit(cxtTap);
25716 }
25717 }
25718
25719 r.hoverData.cxtDragged = false;
25720 r.hoverData.which = null;
25721 } else if (r.hoverData.which === 1) {
25722 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25723 x: pos[0],
25724 y: pos[1]
25725 });
25726
25727 if (!r.dragData.didDrag // didn't move a node around
25728 && !r.hoverData.dragged // didn't pan
25729 && !r.hoverData.selecting // not box selection
25730 && !r.hoverData.isOverThresholdDrag // didn't move too much
25731 ) {
25732 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
25733 x: pos[0],
25734 y: pos[1]
25735 });
25736 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25737
25738
25739 if (down == null && // not mousedown on node
25740 !r.dragData.didDrag // didn't move the node around
25741 && !r.hoverData.selecting // not box selection
25742 && !r.hoverData.dragged // didn't pan
25743 && !isMultSelKeyDown(e)) {
25744 cy.$(isSelected).unselect(['tapunselect']);
25745
25746 if (draggedElements.length > 0) {
25747 r.redrawHint('eles', true);
25748 }
25749
25750 r.dragData.possibleDragElements = draggedElements = cy.collection();
25751 } // Single selection
25752
25753
25754 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25755 if (near != null && near._private.selectable) {
25756 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25757 if (near.selected()) {
25758 near.unselect(['tapunselect']);
25759 } else {
25760 near.select(['tapselect']);
25761 }
25762 } else {
25763 if (!multSelKeyDown) {
25764 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25765 near.select(['tapselect']);
25766 }
25767 }
25768
25769 r.redrawHint('eles', true);
25770 }
25771 }
25772
25773 if (r.hoverData.selecting) {
25774 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25775 r.redrawHint('select', true);
25776
25777 if (box.length > 0) {
25778 r.redrawHint('eles', true);
25779 }
25780
25781 cy.emit({
25782 type: 'boxend',
25783 originalEvent: e,
25784 position: {
25785 x: pos[0],
25786 y: pos[1]
25787 }
25788 });
25789
25790 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25791 return ele.selectable() && !ele.selected();
25792 };
25793
25794 if (cy.selectionType() === 'additive') {
25795 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25796 } else {
25797 if (!multSelKeyDown) {
25798 cy.$(isSelected).unmerge(box).unselect();
25799 }
25800
25801 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25802 } // always need redraw in case eles unselectable
25803
25804
25805 r.redraw();
25806 } // Cancel drag pan
25807
25808
25809 if (r.hoverData.dragging) {
25810 r.hoverData.dragging = false;
25811 r.redrawHint('select', true);
25812 r.redrawHint('eles', true);
25813 r.redraw();
25814 }
25815
25816 if (!select[4]) {
25817 r.redrawHint('drag', true);
25818 r.redrawHint('eles', true);
25819 var downWasGrabbed = down && down.grabbed();
25820 freeDraggedElements(draggedElements);
25821
25822 if (downWasGrabbed) {
25823 down.emit('freeon');
25824 draggedElements.emit('free');
25825
25826 if (r.dragData.didDrag) {
25827 down.emit('dragfreeon');
25828 draggedElements.emit('dragfree');
25829 }
25830 }
25831 }
25832 } // else not right mouse
25833
25834
25835 select[4] = 0;
25836 r.hoverData.down = null;
25837 r.hoverData.cxtStarted = false;
25838 r.hoverData.draggingEles = false;
25839 r.hoverData.selecting = false;
25840 r.hoverData.isOverThresholdDrag = false;
25841 r.dragData.didDrag = false;
25842 r.hoverData.dragged = false;
25843 r.hoverData.dragDelta = [];
25844 r.hoverData.mdownPos = null;
25845 r.hoverData.mdownGPos = null;
25846 }, false);
25847
25848 var wheelHandler = function wheelHandler(e) {
25849 if (r.scrollingPage) {
25850 return;
25851 } // while scrolling, ignore wheel-to-zoom
25852
25853
25854 var cy = r.cy;
25855 var zoom = cy.zoom();
25856 var pan = cy.pan();
25857 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25858 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25859
25860 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25861 // if pan dragging or cxt dragging, wheel movements make no zoom
25862 e.preventDefault();
25863 return;
25864 }
25865
25866 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25867 e.preventDefault();
25868 r.data.wheelZooming = true;
25869 clearTimeout(r.data.wheelTimeout);
25870 r.data.wheelTimeout = setTimeout(function () {
25871 r.data.wheelZooming = false;
25872 r.redrawHint('eles', true);
25873 r.redraw();
25874 }, 150);
25875 var diff;
25876
25877 if (e.deltaY != null) {
25878 diff = e.deltaY / -250;
25879 } else if (e.wheelDeltaY != null) {
25880 diff = e.wheelDeltaY / 1000;
25881 } else {
25882 diff = e.wheelDelta / 1000;
25883 }
25884
25885 diff = diff * r.wheelSensitivity;
25886 var needsWheelFix = e.deltaMode === 1;
25887
25888 if (needsWheelFix) {
25889 // fixes slow wheel events on ff/linux and ff/windows
25890 diff *= 33;
25891 }
25892
25893 var newZoom = cy.zoom() * Math.pow(10, diff);
25894
25895 if (e.type === 'gesturechange') {
25896 newZoom = r.gestureStartZoom * e.scale;
25897 }
25898
25899 cy.zoom({
25900 level: newZoom,
25901 renderedPosition: {
25902 x: rpos[0],
25903 y: rpos[1]
25904 }
25905 });
25906 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25907 }
25908 }; // Functions to help with whether mouse wheel should trigger zooming
25909 // --
25910
25911
25912 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25913 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25914 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25915 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25916
25917 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25918 // eslint-disable-line no-unused-vars
25919 r.scrollingPage = true;
25920 clearTimeout(r.scrollingPageTimeout);
25921 r.scrollingPageTimeout = setTimeout(function () {
25922 r.scrollingPage = false;
25923 }, 250);
25924 }, true); // desktop safari pinch to zoom start
25925
25926 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25927 r.gestureStartZoom = r.cy.zoom();
25928
25929 if (!r.hasTouchStarted) {
25930 // don't affect touch devices like iphone
25931 e.preventDefault();
25932 }
25933 }, true);
25934 r.registerBinding(r.container, 'gesturechange', function (e) {
25935 if (!r.hasTouchStarted) {
25936 // don't affect touch devices like iphone
25937 wheelHandler(e);
25938 }
25939 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25940 // Handle mouseout on Cytoscape container
25941
25942 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25943 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25944 r.cy.emit({
25945 originalEvent: e,
25946 type: 'mouseout',
25947 position: {
25948 x: pos[0],
25949 y: pos[1]
25950 }
25951 });
25952 }, false);
25953 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25954 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25955 r.cy.emit({
25956 originalEvent: e,
25957 type: 'mouseover',
25958 position: {
25959 x: pos[0],
25960 y: pos[1]
25961 }
25962 });
25963 }, false);
25964 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25965
25966 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25967
25968 var center1, modelCenter1; // center point on start pinch to zoom
25969
25970 var offsetLeft, offsetTop;
25971 var containerWidth, containerHeight;
25972 var twoFingersStartInside;
25973
25974 var distance = function distance(x1, y1, x2, y2) {
25975 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25976 };
25977
25978 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25979 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25980 };
25981
25982 var touchstartHandler;
25983 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25984 r.hasTouchStarted = true;
25985
25986 if (!eventInContainer(e)) {
25987 return;
25988 }
25989
25990 blurActiveDomElement();
25991 r.touchData.capture = true;
25992 r.data.bgActivePosistion = undefined;
25993 var cy = r.cy;
25994 var now = r.touchData.now;
25995 var earlier = r.touchData.earlier;
25996
25997 if (e.touches[0]) {
25998 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25999 now[0] = pos[0];
26000 now[1] = pos[1];
26001 }
26002
26003 if (e.touches[1]) {
26004 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26005 now[2] = pos[0];
26006 now[3] = pos[1];
26007 }
26008
26009 if (e.touches[2]) {
26010 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26011 now[4] = pos[0];
26012 now[5] = pos[1];
26013 } // record starting points for pinch-to-zoom
26014
26015
26016 if (e.touches[1]) {
26017 r.touchData.singleTouchMoved = true;
26018 freeDraggedElements(r.dragData.touchDragEles);
26019 var offsets = r.findContainerClientCoords();
26020 offsetLeft = offsets[0];
26021 offsetTop = offsets[1];
26022 containerWidth = offsets[2];
26023 containerHeight = offsets[3];
26024 f1x1 = e.touches[0].clientX - offsetLeft;
26025 f1y1 = e.touches[0].clientY - offsetTop;
26026 f2x1 = e.touches[1].clientX - offsetLeft;
26027 f2y1 = e.touches[1].clientY - offsetTop;
26028 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
26029 var pan = cy.pan();
26030 var zoom = cy.zoom();
26031 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
26032 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
26033 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
26034 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
26035
26036 var cxtDistThreshold = 200;
26037 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
26038
26039 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
26040 var near1 = r.findNearestElement(now[0], now[1], true, true);
26041 var near2 = r.findNearestElement(now[2], now[3], true, true);
26042
26043 if (near1 && near1.isNode()) {
26044 near1.activate().emit({
26045 originalEvent: e,
26046 type: 'cxttapstart',
26047 position: {
26048 x: now[0],
26049 y: now[1]
26050 }
26051 });
26052 r.touchData.start = near1;
26053 } else if (near2 && near2.isNode()) {
26054 near2.activate().emit({
26055 originalEvent: e,
26056 type: 'cxttapstart',
26057 position: {
26058 x: now[0],
26059 y: now[1]
26060 }
26061 });
26062 r.touchData.start = near2;
26063 } else {
26064 cy.emit({
26065 originalEvent: e,
26066 type: 'cxttapstart',
26067 position: {
26068 x: now[0],
26069 y: now[1]
26070 }
26071 });
26072 }
26073
26074 if (r.touchData.start) {
26075 r.touchData.start._private.grabbed = false;
26076 }
26077
26078 r.touchData.cxt = true;
26079 r.touchData.cxtDragged = false;
26080 r.data.bgActivePosistion = undefined;
26081 r.redraw();
26082 return;
26083 }
26084 }
26085
26086 if (e.touches[2]) {
26087 // ignore
26088 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
26089 if (cy.boxSelectionEnabled()) {
26090 e.preventDefault();
26091 }
26092 } else if (e.touches[1]) ; else if (e.touches[0]) {
26093 var nears = r.findNearestElements(now[0], now[1], true, true);
26094 var near = nears[0];
26095
26096 if (near != null) {
26097 near.activate();
26098 r.touchData.start = near;
26099 r.touchData.starts = nears;
26100
26101 if (r.nodeIsGrabbable(near)) {
26102 var draggedEles = r.dragData.touchDragEles = cy.collection();
26103 var selectedNodes = null;
26104 r.redrawHint('eles', true);
26105 r.redrawHint('drag', true);
26106
26107 if (near.selected()) {
26108 // reset drag elements, since near will be added again
26109 selectedNodes = cy.$(function (ele) {
26110 return ele.selected() && r.nodeIsGrabbable(ele);
26111 });
26112 addNodesToDrag(selectedNodes, {
26113 addToList: draggedEles
26114 });
26115 } else {
26116 addNodeToDrag(near, {
26117 addToList: draggedEles
26118 });
26119 }
26120
26121 setGrabTarget(near);
26122
26123 var makeEvent = function makeEvent(type) {
26124 return {
26125 originalEvent: e,
26126 type: type,
26127 position: {
26128 x: now[0],
26129 y: now[1]
26130 }
26131 };
26132 };
26133
26134 near.emit(makeEvent('grabon'));
26135
26136 if (selectedNodes) {
26137 selectedNodes.forEach(function (n) {
26138 n.emit(makeEvent('grab'));
26139 });
26140 } else {
26141 near.emit(makeEvent('grab'));
26142 }
26143 }
26144 }
26145
26146 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
26147 x: now[0],
26148 y: now[1]
26149 });
26150
26151 if (near == null) {
26152 r.data.bgActivePosistion = {
26153 x: pos[0],
26154 y: pos[1]
26155 };
26156 r.redrawHint('select', true);
26157 r.redraw();
26158 } // Tap, taphold
26159 // -----
26160
26161
26162 r.touchData.singleTouchMoved = false;
26163 r.touchData.singleTouchStartTime = +new Date();
26164 clearTimeout(r.touchData.tapholdTimeout);
26165 r.touchData.tapholdTimeout = setTimeout(function () {
26166 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
26167 && !r.touchData.selecting // box selection shouldn't allow taphold through
26168 ) {
26169 triggerEvents(r.touchData.start, ['taphold'], e, {
26170 x: now[0],
26171 y: now[1]
26172 });
26173 }
26174 }, r.tapholdDuration);
26175 }
26176
26177 if (e.touches.length >= 1) {
26178 var sPos = r.touchData.startPosition = [];
26179
26180 for (var i = 0; i < now.length; i++) {
26181 sPos[i] = earlier[i] = now[i];
26182 }
26183
26184 var touch0 = e.touches[0];
26185 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
26186 }
26187 }, false);
26188 var touchmoveHandler;
26189 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
26190 // eslint-disable-line no-undef
26191 var capture = r.touchData.capture;
26192
26193 if (!capture && !eventInContainer(e)) {
26194 return;
26195 }
26196
26197 var select = r.selection;
26198 var cy = r.cy;
26199 var now = r.touchData.now;
26200 var earlier = r.touchData.earlier;
26201 var zoom = cy.zoom();
26202
26203 if (e.touches[0]) {
26204 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26205 now[0] = pos[0];
26206 now[1] = pos[1];
26207 }
26208
26209 if (e.touches[1]) {
26210 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26211 now[2] = pos[0];
26212 now[3] = pos[1];
26213 }
26214
26215 if (e.touches[2]) {
26216 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26217 now[4] = pos[0];
26218 now[5] = pos[1];
26219 }
26220
26221 var startGPos = r.touchData.startGPosition;
26222 var isOverThresholdDrag;
26223
26224 if (capture && e.touches[0] && startGPos) {
26225 var disp = [];
26226
26227 for (var j = 0; j < now.length; j++) {
26228 disp[j] = now[j] - earlier[j];
26229 }
26230
26231 var dx = e.touches[0].clientX - startGPos[0];
26232 var dx2 = dx * dx;
26233 var dy = e.touches[0].clientY - startGPos[1];
26234 var dy2 = dy * dy;
26235 var dist2 = dx2 + dy2;
26236 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
26237 } // context swipe cancelling
26238
26239
26240 if (capture && r.touchData.cxt) {
26241 e.preventDefault();
26242 var f1x2 = e.touches[0].clientX - offsetLeft,
26243 f1y2 = e.touches[0].clientY - offsetTop;
26244 var f2x2 = e.touches[1].clientX - offsetLeft,
26245 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
26246
26247 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
26248 var factorSq = distance2Sq / distance1Sq;
26249 var distThreshold = 150;
26250 var distThresholdSq = distThreshold * distThreshold;
26251 var factorThreshold = 1.5;
26252 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
26253
26254 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
26255 r.touchData.cxt = false;
26256 r.data.bgActivePosistion = undefined;
26257 r.redrawHint('select', true);
26258 var cxtEvt = {
26259 originalEvent: e,
26260 type: 'cxttapend',
26261 position: {
26262 x: now[0],
26263 y: now[1]
26264 }
26265 };
26266
26267 if (r.touchData.start) {
26268 r.touchData.start.unactivate().emit(cxtEvt);
26269 r.touchData.start = null;
26270 } else {
26271 cy.emit(cxtEvt);
26272 }
26273 }
26274 } // context swipe
26275
26276
26277 if (capture && r.touchData.cxt) {
26278 var cxtEvt = {
26279 originalEvent: e,
26280 type: 'cxtdrag',
26281 position: {
26282 x: now[0],
26283 y: now[1]
26284 }
26285 };
26286 r.data.bgActivePosistion = undefined;
26287 r.redrawHint('select', true);
26288
26289 if (r.touchData.start) {
26290 r.touchData.start.emit(cxtEvt);
26291 } else {
26292 cy.emit(cxtEvt);
26293 }
26294
26295 if (r.touchData.start) {
26296 r.touchData.start._private.grabbed = false;
26297 }
26298
26299 r.touchData.cxtDragged = true;
26300 var near = r.findNearestElement(now[0], now[1], true, true);
26301
26302 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
26303 if (r.touchData.cxtOver) {
26304 r.touchData.cxtOver.emit({
26305 originalEvent: e,
26306 type: 'cxtdragout',
26307 position: {
26308 x: now[0],
26309 y: now[1]
26310 }
26311 });
26312 }
26313
26314 r.touchData.cxtOver = near;
26315
26316 if (near) {
26317 near.emit({
26318 originalEvent: e,
26319 type: 'cxtdragover',
26320 position: {
26321 x: now[0],
26322 y: now[1]
26323 }
26324 });
26325 }
26326 } // box selection
26327
26328 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
26329 e.preventDefault();
26330 r.data.bgActivePosistion = undefined;
26331 this.lastThreeTouch = +new Date();
26332
26333 if (!r.touchData.selecting) {
26334 cy.emit({
26335 originalEvent: e,
26336 type: 'boxstart',
26337 position: {
26338 x: now[0],
26339 y: now[1]
26340 }
26341 });
26342 }
26343
26344 r.touchData.selecting = true;
26345 r.touchData.didSelect = true;
26346 select[4] = 1;
26347
26348 if (!select || select.length === 0 || select[0] === undefined) {
26349 select[0] = (now[0] + now[2] + now[4]) / 3;
26350 select[1] = (now[1] + now[3] + now[5]) / 3;
26351 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
26352 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
26353 } else {
26354 select[2] = (now[0] + now[2] + now[4]) / 3;
26355 select[3] = (now[1] + now[3] + now[5]) / 3;
26356 }
26357
26358 r.redrawHint('select', true);
26359 r.redraw(); // pinch to zoom
26360 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
26361 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
26362 // two fingers => pinch to zoom
26363 e.preventDefault();
26364 r.data.bgActivePosistion = undefined;
26365 r.redrawHint('select', true);
26366 var draggedEles = r.dragData.touchDragEles;
26367
26368 if (draggedEles) {
26369 r.redrawHint('drag', true);
26370
26371 for (var i = 0; i < draggedEles.length; i++) {
26372 var de_p = draggedEles[i]._private;
26373 de_p.grabbed = false;
26374 de_p.rscratch.inDragLayer = false;
26375 }
26376 }
26377
26378 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
26379
26380 var f1x2 = e.touches[0].clientX - offsetLeft,
26381 f1y2 = e.touches[0].clientY - offsetTop;
26382 var f2x2 = e.touches[1].clientX - offsetLeft,
26383 f2y2 = e.touches[1].clientY - offsetTop;
26384 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
26385 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
26386
26387 var factor = distance2 / distance1;
26388
26389 if (twoFingersStartInside) {
26390 // delta finger1
26391 var df1x = f1x2 - f1x1;
26392 var df1y = f1y2 - f1y1; // delta finger 2
26393
26394 var df2x = f2x2 - f2x1;
26395 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
26396 // i.e. so pinching cancels out and moving together pans
26397
26398 var tx = (df1x + df2x) / 2;
26399 var ty = (df1y + df2y) / 2; // now calculate the zoom
26400
26401 var zoom1 = cy.zoom();
26402 var zoom2 = zoom1 * factor;
26403 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
26404
26405 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
26406 var ctry = modelCenter1[1] * zoom1 + pan1.y;
26407 var pan2 = {
26408 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
26409 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
26410 }; // remove dragged eles
26411
26412 if (_start && _start.active()) {
26413 var draggedEles = r.dragData.touchDragEles;
26414 freeDraggedElements(draggedEles);
26415 r.redrawHint('drag', true);
26416 r.redrawHint('eles', true);
26417
26418 _start.unactivate().emit('freeon');
26419
26420 draggedEles.emit('free');
26421
26422 if (r.dragData.didDrag) {
26423 _start.emit('dragfreeon');
26424
26425 draggedEles.emit('dragfree');
26426 }
26427 }
26428
26429 cy.viewport({
26430 zoom: zoom2,
26431 pan: pan2,
26432 cancelOnFailedZoom: true
26433 });
26434 cy.emit('pinchzoom');
26435 distance1 = distance2;
26436 f1x1 = f1x2;
26437 f1y1 = f1y2;
26438 f2x1 = f2x2;
26439 f2y1 = f2y2;
26440 r.pinching = true;
26441 } // Re-project
26442
26443
26444 if (e.touches[0]) {
26445 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26446 now[0] = pos[0];
26447 now[1] = pos[1];
26448 }
26449
26450 if (e.touches[1]) {
26451 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26452 now[2] = pos[0];
26453 now[3] = pos[1];
26454 }
26455
26456 if (e.touches[2]) {
26457 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26458 now[4] = pos[0];
26459 now[5] = pos[1];
26460 }
26461 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
26462 ) {
26463 var start = r.touchData.start;
26464 var last = r.touchData.last;
26465 var near;
26466
26467 if (!r.hoverData.draggingEles && !r.swipePanning) {
26468 near = r.findNearestElement(now[0], now[1], true, true);
26469 }
26470
26471 if (capture && start != null) {
26472 e.preventDefault();
26473 } // dragging nodes
26474
26475
26476 if (capture && start != null && r.nodeIsDraggable(start)) {
26477 if (isOverThresholdDrag) {
26478 // then dragging can happen
26479 var draggedEles = r.dragData.touchDragEles;
26480 var justStartedDrag = !r.dragData.didDrag;
26481
26482 if (justStartedDrag) {
26483 addNodesToDrag(draggedEles, {
26484 inDragLayer: true
26485 });
26486 }
26487
26488 r.dragData.didDrag = true;
26489 var totalShift = {
26490 x: 0,
26491 y: 0
26492 };
26493
26494 if (number(disp[0]) && number(disp[1])) {
26495 totalShift.x += disp[0];
26496 totalShift.y += disp[1];
26497
26498 if (justStartedDrag) {
26499 r.redrawHint('eles', true);
26500 var dragDelta = r.touchData.dragDelta;
26501
26502 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
26503 totalShift.x += dragDelta[0];
26504 totalShift.y += dragDelta[1];
26505 }
26506 }
26507 }
26508
26509 r.hoverData.draggingEles = true;
26510 draggedEles.silentShift(totalShift).emit('position drag');
26511 r.redrawHint('drag', true);
26512
26513 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
26514 r.redrawHint('eles', true);
26515 }
26516
26517 r.redraw();
26518 } else {
26519 // otherise keep track of drag delta for later
26520 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
26521
26522 if (dragDelta.length === 0) {
26523 dragDelta.push(disp[0]);
26524 dragDelta.push(disp[1]);
26525 } else {
26526 dragDelta[0] += disp[0];
26527 dragDelta[1] += disp[1];
26528 }
26529 }
26530 } // touchmove
26531
26532
26533 {
26534 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
26535 x: now[0],
26536 y: now[1]
26537 });
26538
26539 if ((!start || !start.grabbed()) && near != last) {
26540 if (last) {
26541 last.emit({
26542 originalEvent: e,
26543 type: 'tapdragout',
26544 position: {
26545 x: now[0],
26546 y: now[1]
26547 }
26548 });
26549 }
26550
26551 if (near) {
26552 near.emit({
26553 originalEvent: e,
26554 type: 'tapdragover',
26555 position: {
26556 x: now[0],
26557 y: now[1]
26558 }
26559 });
26560 }
26561 }
26562
26563 r.touchData.last = near;
26564 } // check to cancel taphold
26565
26566 if (capture) {
26567 for (var i = 0; i < now.length; i++) {
26568 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
26569 r.touchData.singleTouchMoved = true;
26570 }
26571 }
26572 } // panning
26573
26574
26575 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
26576 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
26577
26578 if (allowPassthrough) {
26579 e.preventDefault();
26580
26581 if (!r.data.bgActivePosistion) {
26582 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
26583 }
26584
26585 if (r.swipePanning) {
26586 cy.panBy({
26587 x: disp[0] * zoom,
26588 y: disp[1] * zoom
26589 });
26590 cy.emit('dragpan');
26591 } else if (isOverThresholdDrag) {
26592 r.swipePanning = true;
26593 cy.panBy({
26594 x: dx * zoom,
26595 y: dy * zoom
26596 });
26597 cy.emit('dragpan');
26598
26599 if (start) {
26600 start.unactivate();
26601 r.redrawHint('select', true);
26602 r.touchData.start = null;
26603 }
26604 }
26605 } // Re-project
26606
26607
26608 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26609 now[0] = pos[0];
26610 now[1] = pos[1];
26611 }
26612 }
26613
26614 for (var j = 0; j < now.length; j++) {
26615 earlier[j] = now[j];
26616 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26617
26618
26619 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26620 r.data.bgActivePosistion = undefined;
26621 r.redrawHint('select', true);
26622 r.redraw();
26623 }
26624 }, false);
26625 var touchcancelHandler;
26626 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26627 // eslint-disable-line no-unused-vars
26628 var start = r.touchData.start;
26629 r.touchData.capture = false;
26630
26631 if (start) {
26632 start.unactivate();
26633 }
26634 });
26635 var touchendHandler;
26636 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26637 // eslint-disable-line no-unused-vars
26638 var start = r.touchData.start;
26639 var capture = r.touchData.capture;
26640
26641 if (capture) {
26642 if (e.touches.length === 0) {
26643 r.touchData.capture = false;
26644 }
26645
26646 e.preventDefault();
26647 } else {
26648 return;
26649 }
26650
26651 var select = r.selection;
26652 r.swipePanning = false;
26653 r.hoverData.draggingEles = false;
26654 var cy = r.cy;
26655 var zoom = cy.zoom();
26656 var now = r.touchData.now;
26657 var earlier = r.touchData.earlier;
26658
26659 if (e.touches[0]) {
26660 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26661 now[0] = pos[0];
26662 now[1] = pos[1];
26663 }
26664
26665 if (e.touches[1]) {
26666 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26667 now[2] = pos[0];
26668 now[3] = pos[1];
26669 }
26670
26671 if (e.touches[2]) {
26672 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26673 now[4] = pos[0];
26674 now[5] = pos[1];
26675 }
26676
26677 if (start) {
26678 start.unactivate();
26679 }
26680
26681 var ctxTapend;
26682
26683 if (r.touchData.cxt) {
26684 ctxTapend = {
26685 originalEvent: e,
26686 type: 'cxttapend',
26687 position: {
26688 x: now[0],
26689 y: now[1]
26690 }
26691 };
26692
26693 if (start) {
26694 start.emit(ctxTapend);
26695 } else {
26696 cy.emit(ctxTapend);
26697 }
26698
26699 if (!r.touchData.cxtDragged) {
26700 var ctxTap = {
26701 originalEvent: e,
26702 type: 'cxttap',
26703 position: {
26704 x: now[0],
26705 y: now[1]
26706 }
26707 };
26708
26709 if (start) {
26710 start.emit(ctxTap);
26711 } else {
26712 cy.emit(ctxTap);
26713 }
26714 }
26715
26716 if (r.touchData.start) {
26717 r.touchData.start._private.grabbed = false;
26718 }
26719
26720 r.touchData.cxt = false;
26721 r.touchData.start = null;
26722 r.redraw();
26723 return;
26724 } // no more box selection if we don't have three fingers
26725
26726
26727 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26728 r.touchData.selecting = false;
26729 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26730 select[0] = undefined;
26731 select[1] = undefined;
26732 select[2] = undefined;
26733 select[3] = undefined;
26734 select[4] = 0;
26735 r.redrawHint('select', true);
26736 cy.emit({
26737 type: 'boxend',
26738 originalEvent: e,
26739 position: {
26740 x: now[0],
26741 y: now[1]
26742 }
26743 });
26744
26745 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26746 return ele.selectable() && !ele.selected();
26747 };
26748
26749 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26750
26751 if (box.nonempty()) {
26752 r.redrawHint('eles', true);
26753 }
26754
26755 r.redraw();
26756 }
26757
26758 if (start != null) {
26759 start.unactivate();
26760 }
26761
26762 if (e.touches[2]) {
26763 r.data.bgActivePosistion = undefined;
26764 r.redrawHint('select', true);
26765 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26766 r.data.bgActivePosistion = undefined;
26767 r.redrawHint('select', true);
26768 var draggedEles = r.dragData.touchDragEles;
26769
26770 if (start != null) {
26771 var startWasGrabbed = start._private.grabbed;
26772 freeDraggedElements(draggedEles);
26773 r.redrawHint('drag', true);
26774 r.redrawHint('eles', true);
26775
26776 if (startWasGrabbed) {
26777 start.emit('freeon');
26778 draggedEles.emit('free');
26779
26780 if (r.dragData.didDrag) {
26781 start.emit('dragfreeon');
26782 draggedEles.emit('dragfree');
26783 }
26784 }
26785
26786 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26787 x: now[0],
26788 y: now[1]
26789 });
26790 start.unactivate();
26791 r.touchData.start = null;
26792 } else {
26793 var near = r.findNearestElement(now[0], now[1], true, true);
26794 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26795 x: now[0],
26796 y: now[1]
26797 });
26798 }
26799
26800 var dx = r.touchData.startPosition[0] - now[0];
26801 var dx2 = dx * dx;
26802 var dy = r.touchData.startPosition[1] - now[1];
26803 var dy2 = dy * dy;
26804 var dist2 = dx2 + dy2;
26805 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26806
26807 if (!r.touchData.singleTouchMoved) {
26808 if (!start) {
26809 cy.$(':selected').unselect(['tapunselect']);
26810 }
26811
26812 triggerEvents(start, ['tap', 'vclick'], e, {
26813 x: now[0],
26814 y: now[1]
26815 });
26816 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26817
26818
26819 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26820 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26821 ) {
26822 if (cy.selectionType() === 'single') {
26823 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26824 start.select(['tapselect']);
26825 } else {
26826 if (start.selected()) {
26827 start.unselect(['tapunselect']);
26828 } else {
26829 start.select(['tapselect']);
26830 }
26831 }
26832
26833 r.redrawHint('eles', true);
26834 }
26835
26836 r.touchData.singleTouchMoved = true;
26837 }
26838
26839 for (var j = 0; j < now.length; j++) {
26840 earlier[j] = now[j];
26841 }
26842
26843 r.dragData.didDrag = false; // reset for next touchstart
26844
26845 if (e.touches.length === 0) {
26846 r.touchData.dragDelta = [];
26847 r.touchData.startPosition = null;
26848 r.touchData.startGPosition = null;
26849 r.touchData.didSelect = false;
26850 }
26851
26852 if (e.touches.length < 2) {
26853 if (e.touches.length === 1) {
26854 // the old start global pos'n may not be the same finger that remains
26855 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26856 }
26857
26858 r.pinching = false;
26859 r.redrawHint('eles', true);
26860 r.redraw();
26861 } //r.redraw();
26862
26863 }, false); // fallback compatibility layer for ms pointer events
26864
26865 if (typeof TouchEvent === 'undefined') {
26866 var pointers = [];
26867
26868 var makeTouch = function makeTouch(e) {
26869 return {
26870 clientX: e.clientX,
26871 clientY: e.clientY,
26872 force: 1,
26873 identifier: e.pointerId,
26874 pageX: e.pageX,
26875 pageY: e.pageY,
26876 radiusX: e.width / 2,
26877 radiusY: e.height / 2,
26878 screenX: e.screenX,
26879 screenY: e.screenY,
26880 target: e.target
26881 };
26882 };
26883
26884 var makePointer = function makePointer(e) {
26885 return {
26886 event: e,
26887 touch: makeTouch(e)
26888 };
26889 };
26890
26891 var addPointer = function addPointer(e) {
26892 pointers.push(makePointer(e));
26893 };
26894
26895 var removePointer = function removePointer(e) {
26896 for (var i = 0; i < pointers.length; i++) {
26897 var p = pointers[i];
26898
26899 if (p.event.pointerId === e.pointerId) {
26900 pointers.splice(i, 1);
26901 return;
26902 }
26903 }
26904 };
26905
26906 var updatePointer = function updatePointer(e) {
26907 var p = pointers.filter(function (p) {
26908 return p.event.pointerId === e.pointerId;
26909 })[0];
26910 p.event = e;
26911 p.touch = makeTouch(e);
26912 };
26913
26914 var addTouchesToEvent = function addTouchesToEvent(e) {
26915 e.touches = pointers.map(function (p) {
26916 return p.touch;
26917 });
26918 };
26919
26920 var pointerIsMouse = function pointerIsMouse(e) {
26921 return e.pointerType === 'mouse' || e.pointerType === 4;
26922 };
26923
26924 r.registerBinding(r.container, 'pointerdown', function (e) {
26925 if (pointerIsMouse(e)) {
26926 return;
26927 } // mouse already handled
26928
26929
26930 e.preventDefault();
26931 addPointer(e);
26932 addTouchesToEvent(e);
26933 touchstartHandler(e);
26934 });
26935 r.registerBinding(r.container, 'pointerup', function (e) {
26936 if (pointerIsMouse(e)) {
26937 return;
26938 } // mouse already handled
26939
26940
26941 removePointer(e);
26942 addTouchesToEvent(e);
26943 touchendHandler(e);
26944 });
26945 r.registerBinding(r.container, 'pointercancel', function (e) {
26946 if (pointerIsMouse(e)) {
26947 return;
26948 } // mouse already handled
26949
26950
26951 removePointer(e);
26952 addTouchesToEvent(e);
26953 touchcancelHandler(e);
26954 });
26955 r.registerBinding(r.container, 'pointermove', function (e) {
26956 if (pointerIsMouse(e)) {
26957 return;
26958 } // mouse already handled
26959
26960
26961 e.preventDefault();
26962 updatePointer(e);
26963 addTouchesToEvent(e);
26964 touchmoveHandler(e);
26965 });
26966 }
26967 };
26968
26969 var BRp$d = {};
26970
26971 BRp$d.generatePolygon = function (name, points) {
26972 return this.nodeShapes[name] = {
26973 renderer: this,
26974 name: name,
26975 points: points,
26976 draw: function draw(context, centerX, centerY, width, height) {
26977 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26978 },
26979 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26980 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26981 },
26982 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26983 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26984 }
26985 };
26986 };
26987
26988 BRp$d.generateEllipse = function () {
26989 return this.nodeShapes['ellipse'] = {
26990 renderer: this,
26991 name: 'ellipse',
26992 draw: function draw(context, centerX, centerY, width, height) {
26993 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26994 },
26995 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26996 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26997 },
26998 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26999 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
27000 }
27001 };
27002 };
27003
27004 BRp$d.generateRoundPolygon = function (name, points) {
27005 // Pre-compute control points
27006 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
27007 // the unit vectors.
27008 // For simplicity the layout will be:
27009 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
27010 var allPoints = new Array(points.length * 2);
27011
27012 for (var i = 0; i < points.length / 2; i++) {
27013 var sourceIndex = i * 2;
27014 var destIndex = void 0;
27015
27016 if (i < points.length / 2 - 1) {
27017 destIndex = (i + 1) * 2;
27018 } else {
27019 destIndex = 0;
27020 }
27021
27022 allPoints[i * 4] = points[sourceIndex];
27023 allPoints[i * 4 + 1] = points[sourceIndex + 1];
27024 var xDest = points[destIndex] - points[sourceIndex];
27025 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
27026 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
27027 allPoints[i * 4 + 2] = xDest / norm;
27028 allPoints[i * 4 + 3] = yDest / norm;
27029 }
27030
27031 return this.nodeShapes[name] = {
27032 renderer: this,
27033 name: name,
27034 points: allPoints,
27035 draw: function draw(context, centerX, centerY, width, height) {
27036 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
27037 },
27038 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27039 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
27040 },
27041 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27042 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
27043 }
27044 };
27045 };
27046
27047 BRp$d.generateRoundRectangle = function () {
27048 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
27049 renderer: this,
27050 name: 'round-rectangle',
27051 points: generateUnitNgonPointsFitToSquare(4, 0),
27052 draw: function draw(context, centerX, centerY, width, height) {
27053 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27054 },
27055 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27056 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27057 },
27058 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27059 var cornerRadius = getRoundRectangleRadius(width, height);
27060 var diam = cornerRadius * 2; // Check hBox
27061
27062 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27063 return true;
27064 } // Check vBox
27065
27066
27067 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27068 return true;
27069 } // Check top left quarter circle
27070
27071
27072 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
27073 return true;
27074 } // Check top right quarter circle
27075
27076
27077 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
27078 return true;
27079 } // Check bottom right quarter circle
27080
27081
27082 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27083 return true;
27084 } // Check bottom left quarter circle
27085
27086
27087 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27088 return true;
27089 }
27090
27091 return false;
27092 }
27093 };
27094 };
27095
27096 BRp$d.generateCutRectangle = function () {
27097 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
27098 renderer: this,
27099 name: 'cut-rectangle',
27100 cornerLength: getCutRectangleCornerLength(),
27101 points: generateUnitNgonPointsFitToSquare(4, 0),
27102 draw: function draw(context, centerX, centerY, width, height) {
27103 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27104 },
27105 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
27106 var cl = this.cornerLength;
27107 var hh = height / 2;
27108 var hw = width / 2;
27109 var xBegin = centerX - hw;
27110 var xEnd = centerX + hw;
27111 var yBegin = centerY - hh;
27112 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
27113
27114 return {
27115 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
27116 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
27117 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
27118 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
27119 };
27120 },
27121 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27122 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27123 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
27124 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27125 },
27126 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27127 // Check hBox
27128 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
27129 return true;
27130 } // Check vBox
27131
27132
27133 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
27134 return true;
27135 }
27136
27137 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
27138 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
27139 }
27140 };
27141 };
27142
27143 BRp$d.generateBarrel = function () {
27144 return this.nodeShapes['barrel'] = {
27145 renderer: this,
27146 name: 'barrel',
27147 points: generateUnitNgonPointsFitToSquare(4, 0),
27148 draw: function draw(context, centerX, centerY, width, height) {
27149 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27150 },
27151 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27152 // use two fixed t values for the bezier curve approximation
27153 var t0 = 0.15;
27154 var t1 = 0.5;
27155 var t2 = 0.85;
27156 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27157
27158 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
27159 // approximate curve pts based on the two t values
27160 var m0 = qbezierPtAt({
27161 x: pts[0],
27162 y: pts[1]
27163 }, {
27164 x: pts[2],
27165 y: pts[3]
27166 }, {
27167 x: pts[4],
27168 y: pts[5]
27169 }, t0);
27170 var m1 = qbezierPtAt({
27171 x: pts[0],
27172 y: pts[1]
27173 }, {
27174 x: pts[2],
27175 y: pts[3]
27176 }, {
27177 x: pts[4],
27178 y: pts[5]
27179 }, t1);
27180 var m2 = qbezierPtAt({
27181 x: pts[0],
27182 y: pts[1]
27183 }, {
27184 x: pts[2],
27185 y: pts[3]
27186 }, {
27187 x: pts[4],
27188 y: pts[5]
27189 }, t2);
27190 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
27191 };
27192
27193 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
27194 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27195 },
27196 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
27197 var hh = height / 2;
27198 var hw = width / 2;
27199 var xBegin = centerX - hw;
27200 var xEnd = centerX + hw;
27201 var yBegin = centerY - hh;
27202 var yEnd = centerY + hh;
27203 var curveConstants = getBarrelCurveConstants(width, height);
27204 var hOffset = curveConstants.heightOffset;
27205 var wOffset = curveConstants.widthOffset;
27206 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
27207
27208 var pts = {
27209 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
27210 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
27211 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
27212 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
27213 };
27214 pts.topLeft.isTop = true;
27215 pts.topRight.isTop = true;
27216 pts.bottomLeft.isBottom = true;
27217 pts.bottomRight.isBottom = true;
27218 return pts;
27219 },
27220 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27221 var curveConstants = getBarrelCurveConstants(width, height);
27222 var hOffset = curveConstants.heightOffset;
27223 var wOffset = curveConstants.widthOffset; // Check hBox
27224
27225 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
27226 return true;
27227 } // Check vBox
27228
27229
27230 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
27231 return true;
27232 }
27233
27234 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
27235
27236 var getCurveT = function getCurveT(x, y, curvePts) {
27237 var x0 = curvePts[4];
27238 var x1 = curvePts[2];
27239 var x2 = curvePts[0];
27240 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
27241
27242 var y2 = curvePts[1];
27243 var xMin = Math.min(x0, x2);
27244 var xMax = Math.max(x0, x2);
27245 var yMin = Math.min(y0, y2);
27246 var yMax = Math.max(y0, y2);
27247
27248 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
27249 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
27250 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
27251 var validRoots = roots.filter(function (r) {
27252 return 0 <= r && r <= 1;
27253 });
27254
27255 if (validRoots.length > 0) {
27256 return validRoots[0];
27257 }
27258 }
27259
27260 return null;
27261 };
27262
27263 var curveRegions = Object.keys(barrelCurvePts);
27264
27265 for (var i = 0; i < curveRegions.length; i++) {
27266 var corner = curveRegions[i];
27267 var cornerPts = barrelCurvePts[corner];
27268 var t = getCurveT(x, y, cornerPts);
27269
27270 if (t == null) {
27271 continue;
27272 }
27273
27274 var y0 = cornerPts[5];
27275 var y1 = cornerPts[3];
27276 var y2 = cornerPts[1];
27277 var bezY = qbezierAt(y0, y1, y2, t);
27278
27279 if (cornerPts.isTop && bezY <= y) {
27280 return true;
27281 }
27282
27283 if (cornerPts.isBottom && y <= bezY) {
27284 return true;
27285 }
27286 }
27287
27288 return false;
27289 }
27290 };
27291 };
27292
27293 BRp$d.generateBottomRoundrectangle = function () {
27294 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
27295 renderer: this,
27296 name: 'bottom-round-rectangle',
27297 points: generateUnitNgonPointsFitToSquare(4, 0),
27298 draw: function draw(context, centerX, centerY, width, height) {
27299 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27300 },
27301 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27302 var topStartX = nodeX - (width / 2 + padding);
27303 var topStartY = nodeY - (height / 2 + padding);
27304 var topEndY = topStartY;
27305 var topEndX = nodeX + (width / 2 + padding);
27306 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
27307
27308 if (topIntersections.length > 0) {
27309 return topIntersections;
27310 }
27311
27312 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27313 },
27314 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27315 var cornerRadius = getRoundRectangleRadius(width, height);
27316 var diam = 2 * cornerRadius; // Check hBox
27317
27318 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27319 return true;
27320 } // Check vBox
27321
27322
27323 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27324 return true;
27325 } // check non-rounded top side
27326
27327
27328 var outerWidth = width / 2 + 2 * padding;
27329 var outerHeight = height / 2 + 2 * padding;
27330 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
27331
27332 if (pointInsidePolygonPoints(x, y, points)) {
27333 return true;
27334 } // Check bottom right quarter circle
27335
27336
27337 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27338 return true;
27339 } // Check bottom left quarter circle
27340
27341
27342 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27343 return true;
27344 }
27345
27346 return false;
27347 }
27348 };
27349 };
27350
27351 BRp$d.registerNodeShapes = function () {
27352 var nodeShapes = this.nodeShapes = {};
27353 var renderer = this;
27354 this.generateEllipse();
27355 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
27356 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
27357 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
27358 nodeShapes['square'] = nodeShapes['rectangle'];
27359 this.generateRoundRectangle();
27360 this.generateCutRectangle();
27361 this.generateBarrel();
27362 this.generateBottomRoundrectangle();
27363 {
27364 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
27365 this.generatePolygon('diamond', diamondPoints);
27366 this.generateRoundPolygon('round-diamond', diamondPoints);
27367 }
27368 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27369 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27370 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27371 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27372 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27373 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27374 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
27375 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
27376 var star5Points = new Array(20);
27377 {
27378 var outerPoints = generateUnitNgonPoints(5, 0);
27379 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
27380
27381 var innerRadius = 0.5 * (3 - Math.sqrt(5));
27382 innerRadius *= 1.57;
27383
27384 for (var i = 0; i < innerPoints.length / 2; i++) {
27385 innerPoints[i * 2] *= innerRadius;
27386 innerPoints[i * 2 + 1] *= innerRadius;
27387 }
27388
27389 for (var i = 0; i < 20 / 4; i++) {
27390 star5Points[i * 4] = outerPoints[i * 2];
27391 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
27392 star5Points[i * 4 + 2] = innerPoints[i * 2];
27393 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
27394 }
27395 }
27396 star5Points = fitPolygonToSquare(star5Points);
27397 this.generatePolygon('star', star5Points);
27398 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
27399 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
27400 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]);
27401 {
27402 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
27403 this.generatePolygon('tag', tagPoints);
27404 this.generateRoundPolygon('round-tag', tagPoints);
27405 }
27406
27407 nodeShapes.makePolygon = function (points) {
27408 // use caching on user-specified polygons so they are as fast as native shapes
27409 var key = points.join('$');
27410 var name = 'polygon-' + key;
27411 var shape;
27412
27413 if (shape = this[name]) {
27414 // got cached shape
27415 return shape;
27416 } // create and cache new shape
27417
27418
27419 return renderer.generatePolygon(name, points);
27420 };
27421 };
27422
27423 var BRp$e = {};
27424
27425 BRp$e.timeToRender = function () {
27426 return this.redrawTotalTime / this.redrawCount;
27427 };
27428
27429 BRp$e.redraw = function (options) {
27430 options = options || staticEmptyObject();
27431 var r = this;
27432
27433 if (r.averageRedrawTime === undefined) {
27434 r.averageRedrawTime = 0;
27435 }
27436
27437 if (r.lastRedrawTime === undefined) {
27438 r.lastRedrawTime = 0;
27439 }
27440
27441 if (r.lastDrawTime === undefined) {
27442 r.lastDrawTime = 0;
27443 }
27444
27445 r.requestedFrame = true;
27446 r.renderOptions = options;
27447 };
27448
27449 BRp$e.beforeRender = function (fn, priority) {
27450 // the renderer can't add tick callbacks when destroyed
27451 if (this.destroyed) {
27452 return;
27453 }
27454
27455 if (priority == null) {
27456 error('Priority is not optional for beforeRender');
27457 }
27458
27459 var cbs = this.beforeRenderCallbacks;
27460 cbs.push({
27461 fn: fn,
27462 priority: priority
27463 }); // higher priority callbacks executed first
27464
27465 cbs.sort(function (a, b) {
27466 return b.priority - a.priority;
27467 });
27468 };
27469
27470 var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
27471 var cbs = r.beforeRenderCallbacks;
27472
27473 for (var i = 0; i < cbs.length; i++) {
27474 cbs[i].fn(willDraw, startTime);
27475 }
27476 };
27477
27478 BRp$e.startRenderLoop = function () {
27479 var r = this;
27480 var cy = r.cy;
27481
27482 if (r.renderLoopStarted) {
27483 return;
27484 } else {
27485 r.renderLoopStarted = true;
27486 }
27487
27488 var renderFn = function renderFn(requestTime) {
27489 if (r.destroyed) {
27490 return;
27491 }
27492
27493 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
27494 beforeRenderCallbacks(r, true, requestTime);
27495 var startTime = performanceNow();
27496 r.render(r.renderOptions);
27497 var endTime = r.lastDrawTime = performanceNow();
27498
27499 if (r.averageRedrawTime === undefined) {
27500 r.averageRedrawTime = endTime - startTime;
27501 }
27502
27503 if (r.redrawCount === undefined) {
27504 r.redrawCount = 0;
27505 }
27506
27507 r.redrawCount++;
27508
27509 if (r.redrawTotalTime === undefined) {
27510 r.redrawTotalTime = 0;
27511 }
27512
27513 var duration = endTime - startTime;
27514 r.redrawTotalTime += duration;
27515 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
27516
27517 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
27518 r.requestedFrame = false;
27519 } else {
27520 beforeRenderCallbacks(r, false, requestTime);
27521 }
27522
27523 r.skipFrame = false;
27524 requestAnimationFrame(renderFn);
27525 };
27526
27527 requestAnimationFrame(renderFn);
27528 };
27529
27530 var BaseRenderer = function BaseRenderer(options) {
27531 this.init(options);
27532 };
27533
27534 var BR = BaseRenderer;
27535 var BRp$f = BR.prototype;
27536 BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
27537
27538 BRp$f.init = function (options) {
27539 var r = this;
27540 r.options = options;
27541 r.cy = options.cy;
27542 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
27543
27544 if (window$1) {
27545 var document = window$1.document;
27546 var head = document.head;
27547 var stylesheetId = '__________cytoscape_stylesheet';
27548 var className = '__________cytoscape_container';
27549 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
27550
27551 if (ctr.className.indexOf(className) < 0) {
27552 ctr.className = (ctr.className || '') + ' ' + className;
27553 }
27554
27555 if (!stylesheetAlreadyExists) {
27556 var stylesheet = document.createElement('style');
27557 stylesheet.id = stylesheetId;
27558 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
27559 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
27560 }
27561
27562 var computedStyle = window$1.getComputedStyle(ctr);
27563 var position = computedStyle.getPropertyValue('position');
27564
27565 if (position === 'static') {
27566 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
27567 }
27568 }
27569
27570 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
27571
27572 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
27573
27574 r.hoverData = {
27575 down: null,
27576 last: null,
27577 downTime: null,
27578 triggerMode: null,
27579 dragging: false,
27580 initialPan: [null, null],
27581 capture: false
27582 };
27583 r.dragData = {
27584 possibleDragElements: []
27585 };
27586 r.touchData = {
27587 start: null,
27588 capture: false,
27589 // These 3 fields related to tap, taphold events
27590 startPosition: [null, null, null, null, null, null],
27591 singleTouchStartTime: null,
27592 singleTouchMoved: true,
27593 now: [null, null, null, null, null, null],
27594 earlier: [null, null, null, null, null, null]
27595 };
27596 r.redraws = 0;
27597 r.showFps = options.showFps;
27598 r.debug = options.debug;
27599 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27600 r.textureOnViewport = options.textureOnViewport;
27601 r.wheelSensitivity = options.wheelSensitivity;
27602 r.motionBlurEnabled = options.motionBlur; // on by default
27603
27604 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
27605 r.motionBlur = options.motionBlur; // for initial kick off
27606
27607 r.motionBlurOpacity = options.motionBlurOpacity;
27608 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27609 r.motionBlurPxRatio = 1;
27610 r.mbPxRBlurry = 1; //0.8;
27611
27612 r.minMbLowQualFrames = 4;
27613 r.fullQualityMb = false;
27614 r.clearedForMotionBlur = [];
27615 r.desktopTapThreshold = options.desktopTapThreshold;
27616 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27617 r.touchTapThreshold = options.touchTapThreshold;
27618 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27619 r.tapholdDuration = 500;
27620 r.bindings = [];
27621 r.beforeRenderCallbacks = [];
27622 r.beforeRenderPriorities = {
27623 // higher priority execs before lower one
27624 animations: 400,
27625 eleCalcs: 300,
27626 eleTxrDeq: 200,
27627 lyrTxrDeq: 150,
27628 lyrTxrSkip: 100
27629 };
27630 r.registerNodeShapes();
27631 r.registerArrowShapes();
27632 r.registerCalculationListeners();
27633 };
27634
27635 BRp$f.notify = function (eventName, eles) {
27636 var r = this;
27637 var cy = r.cy; // the renderer can't be notified after it's destroyed
27638
27639 if (this.destroyed) {
27640 return;
27641 }
27642
27643 if (eventName === 'init') {
27644 r.load();
27645 return;
27646 }
27647
27648 if (eventName === 'destroy') {
27649 r.destroy();
27650 return;
27651 }
27652
27653 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27654 r.invalidateCachedZSortedEles();
27655 }
27656
27657 if (eventName === 'viewport') {
27658 r.redrawHint('select', true);
27659 }
27660
27661 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27662 r.invalidateContainerClientCoordsCache();
27663 r.matchCanvasSize(r.container);
27664 }
27665
27666 r.redrawHint('eles', true);
27667 r.redrawHint('drag', true);
27668 this.startRenderLoop();
27669 this.redraw();
27670 };
27671
27672 BRp$f.destroy = function () {
27673 var r = this;
27674 r.destroyed = true;
27675 r.cy.stopAnimationLoop();
27676
27677 for (var i = 0; i < r.bindings.length; i++) {
27678 var binding = r.bindings[i];
27679 var b = binding;
27680 var tgt = b.target;
27681 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27682 }
27683
27684 r.bindings = [];
27685 r.beforeRenderCallbacks = [];
27686 r.onUpdateEleCalcsFns = [];
27687
27688 if (r.removeObserver) {
27689 r.removeObserver.disconnect();
27690 }
27691
27692 if (r.styleObserver) {
27693 r.styleObserver.disconnect();
27694 }
27695
27696 if (r.resizeObserver) {
27697 r.resizeObserver.disconnect();
27698 }
27699
27700 if (r.labelCalcDiv) {
27701 try {
27702 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27703 } catch (e) {// ie10 issue #1014
27704 }
27705 }
27706 };
27707
27708 BRp$f.isHeadless = function () {
27709 return false;
27710 };
27711
27712 [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
27713 extend(BRp$f, props);
27714 });
27715
27716 var fullFpsTime = 1000 / 60; // assume 60 frames per second
27717
27718 var defs = {
27719 setupDequeueing: function setupDequeueing(opts) {
27720 return function setupDequeueingImpl() {
27721 var self = this;
27722 var r = this.renderer;
27723
27724 if (self.dequeueingSetup) {
27725 return;
27726 } else {
27727 self.dequeueingSetup = true;
27728 }
27729
27730 var queueRedraw = lodash_debounce(function () {
27731 r.redrawHint('eles', true);
27732 r.redrawHint('drag', true);
27733 r.redraw();
27734 }, opts.deqRedrawThreshold);
27735
27736 var dequeue = function dequeue(willDraw, frameStartTime) {
27737 var startTime = performanceNow();
27738 var avgRenderTime = r.averageRedrawTime;
27739 var renderTime = r.lastRedrawTime;
27740 var deqd = [];
27741 var extent = r.cy.extent();
27742 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27743 // queue won't automatically be flushed before dequeueing starts
27744
27745 if (!willDraw) {
27746 r.flushRenderedStyleQueue();
27747 }
27748
27749 while (true) {
27750 // eslint-disable-line no-constant-condition
27751 var now = performanceNow();
27752 var duration = now - startTime;
27753 var frameDuration = now - frameStartTime;
27754
27755 if (renderTime < fullFpsTime) {
27756 // if we're rendering faster than the ideal fps, then do dequeueing
27757 // during all of the remaining frame time
27758 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27759
27760 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27761 break;
27762 }
27763 } else {
27764 if (willDraw) {
27765 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27766 break;
27767 }
27768 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27769 break;
27770 }
27771 }
27772
27773 var thisDeqd = opts.deq(self, pixelRatio, extent);
27774
27775 if (thisDeqd.length > 0) {
27776 for (var i = 0; i < thisDeqd.length; i++) {
27777 deqd.push(thisDeqd[i]);
27778 }
27779 } else {
27780 break;
27781 }
27782 } // callbacks on dequeue
27783
27784
27785 if (deqd.length > 0) {
27786 opts.onDeqd(self, deqd);
27787
27788 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27789 queueRedraw();
27790 }
27791 }
27792 };
27793
27794 var priority = opts.priority || noop;
27795 r.beforeRender(dequeue, priority(self));
27796 };
27797 }
27798 };
27799
27800 // Uses keys so elements may share the same cache.
27801
27802 var ElementTextureCacheLookup =
27803 /*#__PURE__*/
27804 function () {
27805 function ElementTextureCacheLookup(getKey) {
27806 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27807
27808 _classCallCheck(this, ElementTextureCacheLookup);
27809
27810 this.idsByKey = new Map$1();
27811 this.keyForId = new Map$1();
27812 this.cachesByLvl = new Map$1();
27813 this.lvls = [];
27814 this.getKey = getKey;
27815 this.doesEleInvalidateKey = doesEleInvalidateKey;
27816 }
27817
27818 _createClass(ElementTextureCacheLookup, [{
27819 key: "getIdsFor",
27820 value: function getIdsFor(key) {
27821 if (key == null) {
27822 error("Can not get id list for null key");
27823 }
27824
27825 var idsByKey = this.idsByKey;
27826 var ids = this.idsByKey.get(key);
27827
27828 if (!ids) {
27829 ids = new Set$1();
27830 idsByKey.set(key, ids);
27831 }
27832
27833 return ids;
27834 }
27835 }, {
27836 key: "addIdForKey",
27837 value: function addIdForKey(key, id) {
27838 if (key != null) {
27839 this.getIdsFor(key).add(id);
27840 }
27841 }
27842 }, {
27843 key: "deleteIdForKey",
27844 value: function deleteIdForKey(key, id) {
27845 if (key != null) {
27846 this.getIdsFor(key)["delete"](id);
27847 }
27848 }
27849 }, {
27850 key: "getNumberOfIdsForKey",
27851 value: function getNumberOfIdsForKey(key) {
27852 if (key == null) {
27853 return 0;
27854 } else {
27855 return this.getIdsFor(key).size;
27856 }
27857 }
27858 }, {
27859 key: "updateKeyMappingFor",
27860 value: function updateKeyMappingFor(ele) {
27861 var id = ele.id();
27862 var prevKey = this.keyForId.get(id);
27863 var currKey = this.getKey(ele);
27864 this.deleteIdForKey(prevKey, id);
27865 this.addIdForKey(currKey, id);
27866 this.keyForId.set(id, currKey);
27867 }
27868 }, {
27869 key: "deleteKeyMappingFor",
27870 value: function deleteKeyMappingFor(ele) {
27871 var id = ele.id();
27872 var prevKey = this.keyForId.get(id);
27873 this.deleteIdForKey(prevKey, id);
27874 this.keyForId["delete"](id);
27875 }
27876 }, {
27877 key: "keyHasChangedFor",
27878 value: function keyHasChangedFor(ele) {
27879 var id = ele.id();
27880 var prevKey = this.keyForId.get(id);
27881 var newKey = this.getKey(ele);
27882 return prevKey !== newKey;
27883 }
27884 }, {
27885 key: "isInvalid",
27886 value: function isInvalid(ele) {
27887 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27888 }
27889 }, {
27890 key: "getCachesAt",
27891 value: function getCachesAt(lvl) {
27892 var cachesByLvl = this.cachesByLvl,
27893 lvls = this.lvls;
27894 var caches = cachesByLvl.get(lvl);
27895
27896 if (!caches) {
27897 caches = new Map$1();
27898 cachesByLvl.set(lvl, caches);
27899 lvls.push(lvl);
27900 }
27901
27902 return caches;
27903 }
27904 }, {
27905 key: "getCache",
27906 value: function getCache(key, lvl) {
27907 return this.getCachesAt(lvl).get(key);
27908 }
27909 }, {
27910 key: "get",
27911 value: function get(ele, lvl) {
27912 var key = this.getKey(ele);
27913 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27914
27915 if (cache != null) {
27916 this.updateKeyMappingFor(ele);
27917 }
27918
27919 return cache;
27920 }
27921 }, {
27922 key: "getForCachedKey",
27923 value: function getForCachedKey(ele, lvl) {
27924 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27925
27926 var cache = this.getCache(key, lvl);
27927 return cache;
27928 }
27929 }, {
27930 key: "hasCache",
27931 value: function hasCache(key, lvl) {
27932 return this.getCachesAt(lvl).has(key);
27933 }
27934 }, {
27935 key: "has",
27936 value: function has(ele, lvl) {
27937 var key = this.getKey(ele);
27938 return this.hasCache(key, lvl);
27939 }
27940 }, {
27941 key: "setCache",
27942 value: function setCache(key, lvl, cache) {
27943 cache.key = key;
27944 this.getCachesAt(lvl).set(key, cache);
27945 }
27946 }, {
27947 key: "set",
27948 value: function set(ele, lvl, cache) {
27949 var key = this.getKey(ele);
27950 this.setCache(key, lvl, cache);
27951 this.updateKeyMappingFor(ele);
27952 }
27953 }, {
27954 key: "deleteCache",
27955 value: function deleteCache(key, lvl) {
27956 this.getCachesAt(lvl)["delete"](key);
27957 }
27958 }, {
27959 key: "delete",
27960 value: function _delete(ele, lvl) {
27961 var key = this.getKey(ele);
27962 this.deleteCache(key, lvl);
27963 }
27964 }, {
27965 key: "invalidateKey",
27966 value: function invalidateKey(key) {
27967 var _this = this;
27968
27969 this.lvls.forEach(function (lvl) {
27970 return _this.deleteCache(key, lvl);
27971 });
27972 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27973
27974 }, {
27975 key: "invalidate",
27976 value: function invalidate(ele) {
27977 var id = ele.id();
27978 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27979
27980 this.deleteKeyMappingFor(ele);
27981 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27982
27983 if (entireKeyInvalidated) {
27984 // clear mapping for current key
27985 this.invalidateKey(key);
27986 }
27987
27988 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27989 }
27990 }]);
27991
27992 return ElementTextureCacheLookup;
27993 }();
27994
27995 var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27996
27997 var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27998
27999 var minLvl = -4; // when scaling smaller than that we don't need to re-render
28000
28001 var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
28002
28003 var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
28004
28005 var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
28006
28007 var defTxrWidth = 1024; // default/minimum texture width
28008
28009 var maxTxrW = 1024; // the maximum width of a texture
28010
28011 var maxTxrH = 1024; // the maximum height of a texture
28012
28013 var minUtility = 0.2; // if usage of texture is less than this, it is retired
28014
28015 var maxFullness = 0.8; // fullness of texture after which queue removal is checked
28016
28017 var maxFullnessChecks = 10; // dequeued after this many checks
28018
28019 var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
28020
28021 var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
28022
28023 var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
28024
28025 var deqFastCost = 0.9; // % of frame time to be used when >60fps
28026
28027 var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
28028
28029 var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
28030
28031 var getTxrReasons = {
28032 dequeue: 'dequeue',
28033 downscale: 'downscale',
28034 highQuality: 'highQuality'
28035 };
28036 var initDefaults = defaults({
28037 getKey: null,
28038 doesEleInvalidateKey: falsify,
28039 drawElement: null,
28040 getBoundingBox: null,
28041 getRotationPoint: null,
28042 getRotationOffset: null,
28043 isVisible: trueify,
28044 allowEdgeTxrCaching: true,
28045 allowParentTxrCaching: true
28046 });
28047
28048 var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
28049 var self = this;
28050 self.renderer = renderer;
28051 self.onDequeues = [];
28052 var opts = initDefaults(initOptions);
28053 extend(self, opts);
28054 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
28055 self.setupDequeueing();
28056 };
28057
28058 var ETCp = ElementTextureCache.prototype;
28059 ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
28060
28061 ETCp.getTextureQueue = function (txrH) {
28062 var self = this;
28063 self.eleImgCaches = self.eleImgCaches || {};
28064 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
28065 }; // the list of usused textures which can be recycled (in use in texture queue)
28066
28067
28068 ETCp.getRetiredTextureQueue = function (txrH) {
28069 var self = this;
28070 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
28071 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
28072 return rtxtrQ;
28073 }; // queue of element draw requests at different scale levels
28074
28075
28076 ETCp.getElementQueue = function () {
28077 var self = this;
28078 var q = self.eleCacheQueue = self.eleCacheQueue || new heap$1(function (a, b) {
28079 return b.reqs - a.reqs;
28080 });
28081 return q;
28082 }; // queue of element draw requests at different scale levels (element id lookup)
28083
28084
28085 ETCp.getElementKeyToQueue = function () {
28086 var self = this;
28087 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
28088 return k2q;
28089 };
28090
28091 ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
28092 var self = this;
28093 var r = this.renderer;
28094 var zoom = r.cy.zoom();
28095 var lookup = this.lookup;
28096
28097 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
28098 return null;
28099 }
28100
28101 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
28102 return null;
28103 }
28104
28105 if (lvl == null) {
28106 lvl = Math.ceil(log2(zoom * pxRatio));
28107 }
28108
28109 if (lvl < minLvl) {
28110 lvl = minLvl;
28111 } else if (zoom >= maxZoom || lvl > maxLvl) {
28112 return null;
28113 }
28114
28115 var scale = Math.pow(2, lvl);
28116 var eleScaledH = bb.h * scale;
28117 var eleScaledW = bb.w * scale;
28118 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
28119
28120 if (!this.isVisible(ele, scaledLabelShown)) {
28121 return null;
28122 }
28123
28124 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
28125
28126 if (eleCache && eleCache.invalidated) {
28127 eleCache.invalidated = false;
28128 eleCache.texture.invalidatedWidth -= eleCache.width;
28129 }
28130
28131 if (eleCache) {
28132 return eleCache;
28133 }
28134
28135 var txrH; // which texture height this ele belongs to
28136
28137 if (eleScaledH <= minTxrH) {
28138 txrH = minTxrH;
28139 } else if (eleScaledH <= txrStepH) {
28140 txrH = txrStepH;
28141 } else {
28142 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
28143 }
28144
28145 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
28146 return null; // caching large elements is not efficient
28147 }
28148
28149 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
28150
28151 var txr = txrQ[txrQ.length - 2];
28152
28153 var addNewTxr = function addNewTxr() {
28154 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
28155 }; // try the last one if there is no second last one
28156
28157
28158 if (!txr) {
28159 txr = txrQ[txrQ.length - 1];
28160 } // if the last one doesn't exist, we need a first one
28161
28162
28163 if (!txr) {
28164 txr = addNewTxr();
28165 } // if there's no room in the current texture, we need a new one
28166
28167
28168 if (txr.width - txr.usedWidth < eleScaledW) {
28169 txr = addNewTxr();
28170 }
28171
28172 var scalableFrom = function scalableFrom(otherCache) {
28173 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
28174 };
28175
28176 var deqing = reason && reason === getTxrReasons.dequeue;
28177 var highQualityReq = reason && reason === getTxrReasons.highQuality;
28178 var downscaleReq = reason && reason === getTxrReasons.downscale;
28179 var higherCache; // the nearest cache with a higher level
28180
28181 for (var l = lvl + 1; l <= maxLvl; l++) {
28182 var c = lookup.get(ele, l);
28183
28184 if (c) {
28185 higherCache = c;
28186 break;
28187 }
28188 }
28189
28190 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
28191
28192 var downscale = function downscale() {
28193 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
28194 }; // reset ele area in texture
28195
28196
28197 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28198 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
28199
28200 if (scalableFrom(oneUpCache)) {
28201 // then we can relatively cheaply rescale the existing image w/o rerendering
28202 downscale();
28203 } else if (scalableFrom(higherCache)) {
28204 // then use the higher cache for now and queue the next level down
28205 // to cheaply scale towards the smaller level
28206 if (highQualityReq) {
28207 for (var _l = higherCache.level; _l > lvl; _l--) {
28208 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
28209 }
28210
28211 downscale();
28212 } else {
28213 self.queueElement(ele, higherCache.level - 1);
28214 return higherCache;
28215 }
28216 } else {
28217 var lowerCache; // the nearest cache with a lower level
28218
28219 if (!deqing && !highQualityReq && !downscaleReq) {
28220 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
28221 var _c = lookup.get(ele, _l2);
28222
28223 if (_c) {
28224 lowerCache = _c;
28225 break;
28226 }
28227 }
28228 }
28229
28230 if (scalableFrom(lowerCache)) {
28231 // then use the lower quality cache for now and queue the better one for later
28232 self.queueElement(ele, lvl);
28233 return lowerCache;
28234 }
28235
28236 txr.context.translate(txr.usedWidth, 0);
28237 txr.context.scale(scale, scale);
28238 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
28239 txr.context.scale(1 / scale, 1 / scale);
28240 txr.context.translate(-txr.usedWidth, 0);
28241 }
28242
28243 eleCache = {
28244 x: txr.usedWidth,
28245 texture: txr,
28246 level: lvl,
28247 scale: scale,
28248 width: eleScaledW,
28249 height: eleScaledH,
28250 scaledLabelShown: scaledLabelShown
28251 };
28252 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
28253 txr.eleCaches.push(eleCache);
28254 lookup.set(ele, lvl, eleCache);
28255 self.checkTextureFullness(txr);
28256 return eleCache;
28257 };
28258
28259 ETCp.invalidateElements = function (eles) {
28260 for (var i = 0; i < eles.length; i++) {
28261 this.invalidateElement(eles[i]);
28262 }
28263 };
28264
28265 ETCp.invalidateElement = function (ele) {
28266 var self = this;
28267 var lookup = self.lookup;
28268 var caches = [];
28269 var invalid = lookup.isInvalid(ele);
28270
28271 if (!invalid) {
28272 return; // override the invalidation request if the element key has not changed
28273 }
28274
28275 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
28276 var cache = lookup.getForCachedKey(ele, lvl);
28277
28278 if (cache) {
28279 caches.push(cache);
28280 }
28281 }
28282
28283 var noOtherElesUseCache = lookup.invalidate(ele);
28284
28285 if (noOtherElesUseCache) {
28286 for (var i = 0; i < caches.length; i++) {
28287 var _cache = caches[i];
28288 var txr = _cache.texture; // remove space from the texture it belongs to
28289
28290 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
28291
28292 _cache.invalidated = true; // retire the texture if its utility is low
28293
28294 self.checkTextureUtility(txr);
28295 }
28296 } // remove from queue since the old req was for the old state
28297
28298
28299 self.removeFromQueue(ele);
28300 };
28301
28302 ETCp.checkTextureUtility = function (txr) {
28303 // invalidate all entries in the cache if the cache size is small
28304 if (txr.invalidatedWidth >= minUtility * txr.width) {
28305 this.retireTexture(txr);
28306 }
28307 };
28308
28309 ETCp.checkTextureFullness = function (txr) {
28310 // if texture has been mostly filled and passed over several times, remove
28311 // it from the queue so we don't need to waste time looking at it to put new things
28312 var self = this;
28313 var txrQ = self.getTextureQueue(txr.height);
28314
28315 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
28316 removeFromArray(txrQ, txr);
28317 } else {
28318 txr.fullnessChecks++;
28319 }
28320 };
28321
28322 ETCp.retireTexture = function (txr) {
28323 var self = this;
28324 var txrH = txr.height;
28325 var txrQ = self.getTextureQueue(txrH);
28326 var lookup = this.lookup; // retire the texture from the active / searchable queue:
28327
28328 removeFromArray(txrQ, txr);
28329 txr.retired = true; // remove the refs from the eles to the caches:
28330
28331 var eleCaches = txr.eleCaches;
28332
28333 for (var i = 0; i < eleCaches.length; i++) {
28334 var eleCache = eleCaches[i];
28335 lookup.deleteCache(eleCache.key, eleCache.level);
28336 }
28337
28338 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
28339
28340 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28341 rtxtrQ.push(txr);
28342 };
28343
28344 ETCp.addTexture = function (txrH, minW) {
28345 var self = this;
28346 var txrQ = self.getTextureQueue(txrH);
28347 var txr = {};
28348 txrQ.push(txr);
28349 txr.eleCaches = [];
28350 txr.height = txrH;
28351 txr.width = Math.max(defTxrWidth, minW);
28352 txr.usedWidth = 0;
28353 txr.invalidatedWidth = 0;
28354 txr.fullnessChecks = 0;
28355 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
28356 txr.context = txr.canvas.getContext('2d');
28357 return txr;
28358 };
28359
28360 ETCp.recycleTexture = function (txrH, minW) {
28361 var self = this;
28362 var txrQ = self.getTextureQueue(txrH);
28363 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28364
28365 for (var i = 0; i < rtxtrQ.length; i++) {
28366 var txr = rtxtrQ[i];
28367
28368 if (txr.width >= minW) {
28369 txr.retired = false;
28370 txr.usedWidth = 0;
28371 txr.invalidatedWidth = 0;
28372 txr.fullnessChecks = 0;
28373 clearArray(txr.eleCaches);
28374 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28375 txr.context.clearRect(0, 0, txr.width, txr.height);
28376 removeFromArray(rtxtrQ, txr);
28377 txrQ.push(txr);
28378 return txr;
28379 }
28380 }
28381 };
28382
28383 ETCp.queueElement = function (ele, lvl) {
28384 var self = this;
28385 var q = self.getElementQueue();
28386 var k2q = self.getElementKeyToQueue();
28387 var key = this.getKey(ele);
28388 var existingReq = k2q[key];
28389
28390 if (existingReq) {
28391 // use the max lvl b/c in between lvls are cheap to make
28392 existingReq.level = Math.max(existingReq.level, lvl);
28393 existingReq.eles.merge(ele);
28394 existingReq.reqs++;
28395 q.updateItem(existingReq);
28396 } else {
28397 var req = {
28398 eles: ele.spawn().merge(ele),
28399 level: lvl,
28400 reqs: 1,
28401 key: key
28402 };
28403 q.push(req);
28404 k2q[key] = req;
28405 }
28406 };
28407
28408 ETCp.dequeue = function (pxRatio
28409 /*, extent*/
28410 ) {
28411 var self = this;
28412 var q = self.getElementQueue();
28413 var k2q = self.getElementKeyToQueue();
28414 var dequeued = [];
28415 var lookup = self.lookup;
28416
28417 for (var i = 0; i < maxDeqSize; i++) {
28418 if (q.size() > 0) {
28419 var req = q.pop();
28420 var key = req.key;
28421 var ele = req.eles[0]; // all eles have the same key
28422
28423 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
28424
28425 k2q[key] = null; // dequeueing isn't necessary with an existing cache
28426
28427 if (cacheExists) {
28428 continue;
28429 }
28430
28431 dequeued.push(req);
28432 var bb = self.getBoundingBox(ele);
28433 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
28434 } else {
28435 break;
28436 }
28437 }
28438
28439 return dequeued;
28440 };
28441
28442 ETCp.removeFromQueue = function (ele) {
28443 var self = this;
28444 var q = self.getElementQueue();
28445 var k2q = self.getElementKeyToQueue();
28446 var key = this.getKey(ele);
28447 var req = k2q[key];
28448
28449 if (req != null) {
28450 if (req.eles.length === 1) {
28451 // remove if last ele in the req
28452 // bring to front of queue
28453 req.reqs = MAX_INT;
28454 q.updateItem(req);
28455 q.pop(); // remove from queue
28456
28457 k2q[key] = null; // remove from lookup map
28458 } else {
28459 // otherwise just remove ele from req
28460 req.eles.unmerge(ele);
28461 }
28462 }
28463 };
28464
28465 ETCp.onDequeue = function (fn) {
28466 this.onDequeues.push(fn);
28467 };
28468
28469 ETCp.offDequeue = function (fn) {
28470 removeFromArray(this.onDequeues, fn);
28471 };
28472
28473 ETCp.setupDequeueing = defs.setupDequeueing({
28474 deqRedrawThreshold: deqRedrawThreshold,
28475 deqCost: deqCost,
28476 deqAvgCost: deqAvgCost,
28477 deqNoDrawCost: deqNoDrawCost,
28478 deqFastCost: deqFastCost,
28479 deq: function deq(self, pxRatio, extent) {
28480 return self.dequeue(pxRatio, extent);
28481 },
28482 onDeqd: function onDeqd(self, deqd) {
28483 for (var i = 0; i < self.onDequeues.length; i++) {
28484 var fn = self.onDequeues[i];
28485 fn(deqd);
28486 }
28487 },
28488 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
28489 for (var i = 0; i < deqd.length; i++) {
28490 var eles = deqd[i].eles;
28491
28492 for (var j = 0; j < eles.length; j++) {
28493 var bb = eles[j].boundingBox();
28494
28495 if (boundingBoxesIntersect(bb, extent)) {
28496 return true;
28497 }
28498 }
28499 }
28500
28501 return false;
28502 },
28503 priority: function priority(self) {
28504 return self.renderer.beforeRenderPriorities.eleTxrDeq;
28505 }
28506 });
28507
28508 var defNumLayers = 1; // default number of layers to use
28509
28510 var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
28511
28512 var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
28513
28514 var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
28515
28516 var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
28517
28518 var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
28519
28520 var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
28521
28522 var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
28523
28524 var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
28525
28526 var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
28527
28528 var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
28529
28530 var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
28531
28532 var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
28533
28534 var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
28535 // var log = function(){ console.log.apply( console, arguments ); };
28536
28537 var LayeredTextureCache = function LayeredTextureCache(renderer) {
28538 var self = this;
28539 var r = self.renderer = renderer;
28540 var cy = r.cy;
28541 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
28542
28543 self.firstGet = true;
28544 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
28545 self.skipping = false;
28546 self.eleTxrDeqs = cy.collection();
28547 self.scheduleElementRefinement = lodash_debounce(function () {
28548 self.refineElementTextures(self.eleTxrDeqs);
28549 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
28550 }, refineEleDebounceTime);
28551 r.beforeRender(function (willDraw, now) {
28552 if (now - self.lastInvalidationTime <= invalidThreshold) {
28553 self.skipping = true;
28554 } else {
28555 self.skipping = false;
28556 }
28557 }, r.beforeRenderPriorities.lyrTxrSkip);
28558
28559 var qSort = function qSort(a, b) {
28560 return b.reqs - a.reqs;
28561 };
28562
28563 self.layersQueue = new heap$1(qSort);
28564 self.setupDequeueing();
28565 };
28566
28567 var LTCp = LayeredTextureCache.prototype;
28568 var layerIdPool = 0;
28569 var MAX_INT$1 = Math.pow(2, 53) - 1;
28570
28571 LTCp.makeLayer = function (bb, lvl) {
28572 var scale = Math.pow(2, lvl);
28573 var w = Math.ceil(bb.w * scale);
28574 var h = Math.ceil(bb.h * scale);
28575 var canvas = this.renderer.makeOffscreenCanvas(w, h);
28576 var layer = {
28577 id: layerIdPool = ++layerIdPool % MAX_INT$1,
28578 bb: bb,
28579 level: lvl,
28580 width: w,
28581 height: h,
28582 canvas: canvas,
28583 context: canvas.getContext('2d'),
28584 eles: [],
28585 elesQueue: [],
28586 reqs: 0
28587 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
28588
28589 var cxt = layer.context;
28590 var dx = -layer.bb.x1;
28591 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
28592
28593 cxt.scale(scale, scale);
28594 cxt.translate(dx, dy);
28595 return layer;
28596 };
28597
28598 LTCp.getLayers = function (eles, pxRatio, lvl) {
28599 var self = this;
28600 var r = self.renderer;
28601 var cy = r.cy;
28602 var zoom = cy.zoom();
28603 var firstGet = self.firstGet;
28604 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28605 //log eles.map(function(ele){ return ele.id() }) );
28606
28607 if (lvl == null) {
28608 lvl = Math.ceil(log2(zoom * pxRatio));
28609
28610 if (lvl < minLvl$1) {
28611 lvl = minLvl$1;
28612 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
28613 return null;
28614 }
28615 }
28616
28617 self.validateLayersElesOrdering(lvl, eles);
28618 var layersByLvl = self.layersByLevel;
28619 var scale = Math.pow(2, lvl);
28620 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28621 var bb;
28622 var lvlComplete = self.levelIsComplete(lvl, eles);
28623 var tmpLayers;
28624
28625 var checkTempLevels = function checkTempLevels() {
28626 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28627 self.validateLayersElesOrdering(l, eles);
28628
28629 if (self.levelIsComplete(l, eles)) {
28630 tmpLayers = layersByLvl[l];
28631 return true;
28632 }
28633 };
28634
28635 var checkLvls = function checkLvls(dir) {
28636 if (tmpLayers) {
28637 return;
28638 }
28639
28640 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
28641 if (canUseAsTmpLvl(l)) {
28642 break;
28643 }
28644 }
28645 };
28646
28647 checkLvls(+1);
28648 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28649
28650 for (var i = layers.length - 1; i >= 0; i--) {
28651 var layer = layers[i];
28652
28653 if (layer.invalid) {
28654 removeFromArray(layers, layer);
28655 }
28656 }
28657 };
28658
28659 if (!lvlComplete) {
28660 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28661 // and later queue the current layerset so we can get the proper quality level soon
28662 checkTempLevels();
28663 } else {
28664 // log('level complete, using existing layers\n--');
28665 return layers;
28666 }
28667
28668 var getBb = function getBb() {
28669 if (!bb) {
28670 bb = makeBoundingBox();
28671
28672 for (var i = 0; i < eles.length; i++) {
28673 updateBoundingBox(bb, eles[i].boundingBox());
28674 }
28675 }
28676
28677 return bb;
28678 };
28679
28680 var makeLayer = function makeLayer(opts) {
28681 opts = opts || {};
28682 var after = opts.after;
28683 getBb();
28684 var area = bb.w * scale * (bb.h * scale);
28685
28686 if (area > maxLayerArea) {
28687 return null;
28688 }
28689
28690 var layer = self.makeLayer(bb, lvl);
28691
28692 if (after != null) {
28693 var index = layers.indexOf(after) + 1;
28694 layers.splice(index, 0, layer);
28695 } else if (opts.insert === undefined || opts.insert) {
28696 // no after specified => first layer made so put at start
28697 layers.unshift(layer);
28698 } // if( tmpLayers ){
28699 //self.queueLayer( layer );
28700 // }
28701
28702
28703 return layer;
28704 };
28705
28706 if (self.skipping && !firstGet) {
28707 // log('skip layers');
28708 return null;
28709 } // log('do layers');
28710
28711
28712 var layer = null;
28713 var maxElesPerLayer = eles.length / defNumLayers;
28714 var allowLazyQueueing = !firstGet;
28715
28716 for (var i = 0; i < eles.length; i++) {
28717 var ele = eles[i];
28718 var rs = ele._private.rscratch;
28719 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28720
28721 var existingLayer = caches[lvl];
28722
28723 if (existingLayer) {
28724 // reuse layer for later eles
28725 // log('reuse layer for', ele.id());
28726 layer = existingLayer;
28727 continue;
28728 }
28729
28730 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28731 // log('make new layer for ele %s', ele.id());
28732 layer = makeLayer({
28733 insert: true,
28734 after: layer
28735 }); // if now layer can be built then we can't use layers at this level
28736
28737 if (!layer) {
28738 return null;
28739 } // log('new layer with id %s', layer.id);
28740
28741 }
28742
28743 if (tmpLayers || allowLazyQueueing) {
28744 // log('queue ele %s in layer %s', ele.id(), layer.id);
28745 self.queueLayer(layer, ele);
28746 } else {
28747 // log('draw ele %s in layer %s', ele.id(), layer.id);
28748 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28749 }
28750
28751 layer.eles.push(ele);
28752 caches[lvl] = layer;
28753 } // log('--');
28754
28755
28756 if (tmpLayers) {
28757 // then we only queued the current layerset and can't draw it yet
28758 return tmpLayers;
28759 }
28760
28761 if (allowLazyQueueing) {
28762 // log('lazy queue level', lvl);
28763 return null;
28764 }
28765
28766 return layers;
28767 }; // a layer may want to use an ele cache of a higher level to avoid blurriness
28768 // so the layer level might not equal the ele level
28769
28770
28771 LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28772 return lvl;
28773 };
28774
28775 LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28776 var self = this;
28777 var r = this.renderer;
28778 var context = layer.context;
28779 var bb = ele.boundingBox();
28780
28781 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28782 return;
28783 }
28784
28785 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28786
28787 {
28788 r.setImgSmoothing(context, false);
28789 }
28790
28791 {
28792 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28793 }
28794
28795 {
28796 r.setImgSmoothing(context, true);
28797 }
28798 };
28799
28800 LTCp.levelIsComplete = function (lvl, eles) {
28801 var self = this;
28802 var layers = self.layersByLevel[lvl];
28803
28804 if (!layers || layers.length === 0) {
28805 return false;
28806 }
28807
28808 var numElesInLayers = 0;
28809
28810 for (var i = 0; i < layers.length; i++) {
28811 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28812
28813 if (layer.reqs > 0) {
28814 return false;
28815 } // if the layer is invalid, the level is not complete
28816
28817
28818 if (layer.invalid) {
28819 return false;
28820 }
28821
28822 numElesInLayers += layer.eles.length;
28823 } // we should have exactly the number of eles passed in to be complete
28824
28825
28826 if (numElesInLayers !== eles.length) {
28827 return false;
28828 }
28829
28830 return true;
28831 };
28832
28833 LTCp.validateLayersElesOrdering = function (lvl, eles) {
28834 var layers = this.layersByLevel[lvl];
28835
28836 if (!layers) {
28837 return;
28838 } // if in a layer the eles are not in the same order, then the layer is invalid
28839 // (i.e. there is an ele in between the eles in the layer)
28840
28841
28842 for (var i = 0; i < layers.length; i++) {
28843 var layer = layers[i];
28844 var offset = -1; // find the offset
28845
28846 for (var j = 0; j < eles.length; j++) {
28847 if (layer.eles[0] === eles[j]) {
28848 offset = j;
28849 break;
28850 }
28851 }
28852
28853 if (offset < 0) {
28854 // then the layer has nonexistant elements and is invalid
28855 this.invalidateLayer(layer);
28856 continue;
28857 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28858
28859
28860 var o = offset;
28861
28862 for (var j = 0; j < layer.eles.length; j++) {
28863 if (layer.eles[j] !== eles[o + j]) {
28864 // log('invalidate based on ordering', layer.id);
28865 this.invalidateLayer(layer);
28866 break;
28867 }
28868 }
28869 }
28870 };
28871
28872 LTCp.updateElementsInLayers = function (eles, update) {
28873 var self = this;
28874 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28875 // layer itself along the way
28876
28877 for (var i = 0; i < eles.length; i++) {
28878 var req = isEles ? null : eles[i];
28879 var ele = isEles ? eles[i] : eles[i].ele;
28880 var rs = ele._private.rscratch;
28881 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28882
28883 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28884 var layer = caches[l];
28885
28886 if (!layer) {
28887 continue;
28888 } // if update is a request from the ele cache, then it affects only
28889 // the matching level
28890
28891
28892 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28893 continue;
28894 }
28895
28896 update(layer, ele, req);
28897 }
28898 }
28899 };
28900
28901 LTCp.haveLayers = function () {
28902 var self = this;
28903 var haveLayers = false;
28904
28905 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28906 var layers = self.layersByLevel[l];
28907
28908 if (layers && layers.length > 0) {
28909 haveLayers = true;
28910 break;
28911 }
28912 }
28913
28914 return haveLayers;
28915 };
28916
28917 LTCp.invalidateElements = function (eles) {
28918 var self = this;
28919
28920 if (eles.length === 0) {
28921 return;
28922 }
28923
28924 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28925
28926 if (eles.length === 0 || !self.haveLayers()) {
28927 return;
28928 }
28929
28930 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28931 self.invalidateLayer(layer);
28932 });
28933 };
28934
28935 LTCp.invalidateLayer = function (layer) {
28936 // log('update invalidate layer time');
28937 this.lastInvalidationTime = performanceNow();
28938
28939 if (layer.invalid) {
28940 return;
28941 } // save cycles
28942
28943
28944 var lvl = layer.level;
28945 var eles = layer.eles;
28946 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28947
28948 removeFromArray(layers, layer); // layer.eles = [];
28949
28950 layer.elesQueue = [];
28951 layer.invalid = true;
28952
28953 if (layer.replacement) {
28954 layer.replacement.invalid = true;
28955 }
28956
28957 for (var i = 0; i < eles.length; i++) {
28958 var caches = eles[i]._private.rscratch.imgLayerCaches;
28959
28960 if (caches) {
28961 caches[lvl] = null;
28962 }
28963 }
28964 };
28965
28966 LTCp.refineElementTextures = function (eles) {
28967 var self = this; // log('refine', eles.length);
28968
28969 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28970 var rLyr = layer.replacement;
28971
28972 if (!rLyr) {
28973 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28974 rLyr.replaces = layer;
28975 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28976 }
28977
28978 if (!rLyr.reqs) {
28979 for (var i = 0; i < rLyr.eles.length; i++) {
28980 self.queueLayer(rLyr, rLyr.eles[i]);
28981 } // log('queue replacement layer refinement', rLyr.id);
28982
28983 }
28984 });
28985 };
28986
28987 LTCp.enqueueElementRefinement = function (ele) {
28988
28989 this.eleTxrDeqs.merge(ele);
28990 this.scheduleElementRefinement();
28991 };
28992
28993 LTCp.queueLayer = function (layer, ele) {
28994 var self = this;
28995 var q = self.layersQueue;
28996 var elesQ = layer.elesQueue;
28997 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28998
28999 if (layer.replacement) {
29000 return;
29001 }
29002
29003 if (ele) {
29004 if (hasId[ele.id()]) {
29005 return;
29006 }
29007
29008 elesQ.push(ele);
29009 hasId[ele.id()] = true;
29010 }
29011
29012 if (layer.reqs) {
29013 layer.reqs++;
29014 q.updateItem(layer);
29015 } else {
29016 layer.reqs = 1;
29017 q.push(layer);
29018 }
29019 };
29020
29021 LTCp.dequeue = function (pxRatio) {
29022 var self = this;
29023 var q = self.layersQueue;
29024 var deqd = [];
29025 var eleDeqs = 0;
29026
29027 while (eleDeqs < maxDeqSize$1) {
29028 if (q.size() === 0) {
29029 break;
29030 }
29031
29032 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
29033
29034 if (layer.replacement) {
29035 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
29036 q.pop();
29037 continue;
29038 } // if this is a replacement layer that has been superceded, then forget it
29039
29040
29041 if (layer.replaces && layer !== layer.replaces.replacement) {
29042 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
29043 q.pop();
29044 continue;
29045 }
29046
29047 if (layer.invalid) {
29048 // log('replacement layer %s is invalid; dequeued', layer.id);
29049 q.pop();
29050 continue;
29051 }
29052
29053 var ele = layer.elesQueue.shift();
29054
29055 if (ele) {
29056 // log('dequeue layer %s', layer.id);
29057 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
29058 eleDeqs++;
29059 }
29060
29061 if (deqd.length === 0) {
29062 // we need only one entry in deqd to queue redrawing etc
29063 deqd.push(true);
29064 } // if the layer has all its eles done, then remove from the queue
29065
29066
29067 if (layer.elesQueue.length === 0) {
29068 q.pop();
29069 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
29070 // when a replacement layer is dequeued, it replaces the old layer in the level
29071
29072 if (layer.replaces) {
29073 self.applyLayerReplacement(layer);
29074 }
29075
29076 self.requestRedraw();
29077 }
29078 }
29079
29080 return deqd;
29081 };
29082
29083 LTCp.applyLayerReplacement = function (layer) {
29084 var self = this;
29085 var layersInLevel = self.layersByLevel[layer.level];
29086 var replaced = layer.replaces;
29087 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
29088 // refs would be a mistake (i.e. overwriting the true active layer)
29089
29090 if (index < 0 || replaced.invalid) {
29091 // log('replacement layer would have no effect', layer.id);
29092 return;
29093 }
29094
29095 layersInLevel[index] = layer; // replace level ref
29096 // replace refs in eles
29097
29098 for (var i = 0; i < layer.eles.length; i++) {
29099 var _p = layer.eles[i]._private;
29100 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
29101
29102 if (cache) {
29103 cache[layer.level] = layer;
29104 }
29105 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
29106
29107
29108 self.requestRedraw();
29109 };
29110
29111 LTCp.requestRedraw = lodash_debounce(function () {
29112 var r = this.renderer;
29113 r.redrawHint('eles', true);
29114 r.redrawHint('drag', true);
29115 r.redraw();
29116 }, 100);
29117 LTCp.setupDequeueing = defs.setupDequeueing({
29118 deqRedrawThreshold: deqRedrawThreshold$1,
29119 deqCost: deqCost$1,
29120 deqAvgCost: deqAvgCost$1,
29121 deqNoDrawCost: deqNoDrawCost$1,
29122 deqFastCost: deqFastCost$1,
29123 deq: function deq(self, pxRatio) {
29124 return self.dequeue(pxRatio);
29125 },
29126 onDeqd: noop,
29127 shouldRedraw: trueify,
29128 priority: function priority(self) {
29129 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
29130 }
29131 });
29132
29133 var CRp = {};
29134 var impl;
29135
29136 function polygon(context, points) {
29137 for (var i = 0; i < points.length; i++) {
29138 var pt = points[i];
29139 context.lineTo(pt.x, pt.y);
29140 }
29141 }
29142
29143 function triangleBackcurve(context, points, controlPoint) {
29144 var firstPt;
29145
29146 for (var i = 0; i < points.length; i++) {
29147 var pt = points[i];
29148
29149 if (i === 0) {
29150 firstPt = pt;
29151 }
29152
29153 context.lineTo(pt.x, pt.y);
29154 }
29155
29156 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
29157 }
29158
29159 function triangleTee(context, trianglePoints, teePoints) {
29160 if (context.beginPath) {
29161 context.beginPath();
29162 }
29163
29164 var triPts = trianglePoints;
29165
29166 for (var i = 0; i < triPts.length; i++) {
29167 var pt = triPts[i];
29168 context.lineTo(pt.x, pt.y);
29169 }
29170
29171 var teePts = teePoints;
29172 var firstTeePt = teePoints[0];
29173 context.moveTo(firstTeePt.x, firstTeePt.y);
29174
29175 for (var i = 1; i < teePts.length; i++) {
29176 var pt = teePts[i];
29177 context.lineTo(pt.x, pt.y);
29178 }
29179
29180 if (context.closePath) {
29181 context.closePath();
29182 }
29183 }
29184
29185 function circleTriangle(context, trianglePoints, rx, ry, r) {
29186 if (context.beginPath) {
29187 context.beginPath();
29188 }
29189
29190 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29191 var triPts = trianglePoints;
29192 var firstTrPt = triPts[0];
29193 context.moveTo(firstTrPt.x, firstTrPt.y);
29194
29195 for (var i = 0; i < triPts.length; i++) {
29196 var pt = triPts[i];
29197 context.lineTo(pt.x, pt.y);
29198 }
29199
29200 if (context.closePath) {
29201 context.closePath();
29202 }
29203 }
29204
29205 function circle(context, rx, ry, r) {
29206 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29207 }
29208
29209 CRp.arrowShapeImpl = function (name) {
29210 return (impl || (impl = {
29211 'polygon': polygon,
29212 'triangle-backcurve': triangleBackcurve,
29213 'triangle-tee': triangleTee,
29214 'circle-triangle': circleTriangle,
29215 'triangle-cross': triangleTee,
29216 'circle': circle
29217 }))[name];
29218 };
29219
29220 var CRp$1 = {};
29221
29222 CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
29223 var r = this;
29224
29225 if (ele.isNode()) {
29226 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29227 } else {
29228 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29229 }
29230 };
29231
29232 CRp$1.drawElementOverlay = function (context, ele) {
29233 var r = this;
29234
29235 if (ele.isNode()) {
29236 r.drawNodeOverlay(context, ele);
29237 } else {
29238 r.drawEdgeOverlay(context, ele);
29239 }
29240 };
29241
29242 CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
29243 var r = this;
29244 var bb = eleTxrCache.getBoundingBox(ele);
29245
29246 if (bb.w === 0 || bb.h === 0) {
29247 return;
29248 } // ignore zero size case
29249
29250
29251 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
29252
29253 if (eleCache != null) {
29254 var opacity = getOpacity(r, ele);
29255
29256 if (opacity === 0) {
29257 return;
29258 }
29259
29260 var theta = getRotation(r, ele);
29261 var x1 = bb.x1,
29262 y1 = bb.y1,
29263 w = bb.w,
29264 h = bb.h;
29265 var x, y, sx, sy, smooth;
29266
29267 if (theta !== 0) {
29268 var rotPt = eleTxrCache.getRotationPoint(ele);
29269 sx = rotPt.x;
29270 sy = rotPt.y;
29271 context.translate(sx, sy);
29272 context.rotate(theta);
29273 smooth = r.getImgSmoothing(context);
29274
29275 if (!smooth) {
29276 r.setImgSmoothing(context, true);
29277 }
29278
29279 var off = eleTxrCache.getRotationOffset(ele);
29280 x = off.x;
29281 y = off.y;
29282 } else {
29283 x = x1;
29284 y = y1;
29285 }
29286
29287 var oldGlobalAlpha;
29288
29289 if (opacity !== 1) {
29290 oldGlobalAlpha = context.globalAlpha;
29291 context.globalAlpha = oldGlobalAlpha * opacity;
29292 }
29293
29294 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
29295
29296 if (opacity !== 1) {
29297 context.globalAlpha = oldGlobalAlpha;
29298 }
29299
29300 if (theta !== 0) {
29301 context.rotate(-theta);
29302 context.translate(-sx, -sy);
29303
29304 if (!smooth) {
29305 r.setImgSmoothing(context, false);
29306 }
29307 }
29308 } else {
29309 eleTxrCache.drawElement(context, ele); // direct draw fallback
29310 }
29311 };
29312
29313 var getZeroRotation = function getZeroRotation() {
29314 return 0;
29315 };
29316
29317 var getLabelRotation = function getLabelRotation(r, ele) {
29318 return r.getTextAngle(ele, null);
29319 };
29320
29321 var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
29322 return r.getTextAngle(ele, 'source');
29323 };
29324
29325 var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
29326 return r.getTextAngle(ele, 'target');
29327 };
29328
29329 var getOpacity = function getOpacity(r, ele) {
29330 return ele.effectiveOpacity();
29331 };
29332
29333 var getTextOpacity = function getTextOpacity(e, ele) {
29334 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
29335 };
29336
29337 CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
29338 var r = this;
29339 var _r$data = r.data,
29340 eleTxrCache = _r$data.eleTxrCache,
29341 lblTxrCache = _r$data.lblTxrCache,
29342 slbTxrCache = _r$data.slbTxrCache,
29343 tlbTxrCache = _r$data.tlbTxrCache;
29344 var bb = ele.boundingBox();
29345 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
29346
29347 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
29348 return;
29349 }
29350
29351 if (!extent || boundingBoxesIntersect(bb, extent)) {
29352 var isEdge = ele.isEdge();
29353
29354 var badLine = ele.element()._private.rscratch.badLine;
29355
29356 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
29357
29358 if (!isEdge || !badLine) {
29359 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
29360 }
29361
29362 if (isEdge && !badLine) {
29363 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
29364 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
29365 }
29366
29367 r.drawElementOverlay(context, ele);
29368 }
29369 };
29370
29371 CRp$1.drawElements = function (context, eles) {
29372 var r = this;
29373
29374 for (var i = 0; i < eles.length; i++) {
29375 var ele = eles[i];
29376 r.drawElement(context, ele);
29377 }
29378 };
29379
29380 CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
29381 var r = this;
29382
29383 for (var i = 0; i < eles.length; i++) {
29384 var ele = eles[i];
29385 r.drawCachedElement(context, ele, pxRatio, extent);
29386 }
29387 };
29388
29389 CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
29390 var r = this;
29391
29392 for (var i = 0; i < eles.length; i++) {
29393 var ele = eles[i];
29394
29395 if (!ele.isNode()) {
29396 continue;
29397 }
29398
29399 r.drawCachedElement(context, ele, pxRatio, extent);
29400 }
29401 };
29402
29403 CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
29404 var r = this;
29405 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
29406
29407 if (layers) {
29408 for (var i = 0; i < layers.length; i++) {
29409 var layer = layers[i];
29410 var bb = layer.bb;
29411
29412 if (bb.w === 0 || bb.h === 0) {
29413 continue;
29414 }
29415
29416 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
29417 }
29418 } else {
29419 // fall back on plain caching if no layers
29420 r.drawCachedElements(context, eles, pxRatio, extent);
29421 }
29422 };
29423
29424 /* global Path2D */
29425 var CRp$2 = {};
29426
29427 CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
29428 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29429 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29430 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29431 var r = this;
29432 var rs = edge._private.rscratch;
29433
29434 if (shouldDrawOpacity && !edge.visible()) {
29435 return;
29436 } // if bezier ctrl pts can not be calculated, then die
29437
29438
29439 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
29440 // isNaN in case edge is impossible and browser bugs (e.g. safari)
29441 return;
29442 }
29443
29444 var bb;
29445
29446 if (shiftToOriginWithBb) {
29447 bb = shiftToOriginWithBb;
29448 context.translate(-bb.x1, -bb.y1);
29449 }
29450
29451 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
29452 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
29453 var lineStyle = edge.pstyle('line-style').value;
29454 var edgeWidth = edge.pstyle('width').pfValue;
29455 var lineCap = edge.pstyle('line-cap').value;
29456 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
29457
29458 var effectiveArrowOpacity = opacity * lineOpacity;
29459
29460 var drawLine = function drawLine() {
29461 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
29462 context.lineWidth = edgeWidth;
29463 context.lineCap = lineCap;
29464 r.eleStrokeStyle(context, edge, strokeOpacity);
29465 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
29466 context.lineCap = 'butt'; // reset for other drawing functions
29467 };
29468
29469 var drawOverlay = function drawOverlay() {
29470 if (!shouldDrawOverlay) {
29471 return;
29472 }
29473
29474 r.drawEdgeOverlay(context, edge);
29475 };
29476
29477 var drawArrows = function drawArrows() {
29478 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
29479 r.drawArrowheads(context, edge, arrowOpacity);
29480 };
29481
29482 var drawText = function drawText() {
29483 r.drawElementText(context, edge, null, drawLabel);
29484 };
29485
29486 context.lineJoin = 'round';
29487 var ghost = edge.pstyle('ghost').value === 'yes';
29488
29489 if (ghost) {
29490 var gx = edge.pstyle('ghost-offset-x').pfValue;
29491 var gy = edge.pstyle('ghost-offset-y').pfValue;
29492 var ghostOpacity = edge.pstyle('ghost-opacity').value;
29493 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
29494 context.translate(gx, gy);
29495 drawLine(effectiveGhostOpacity);
29496 drawArrows(effectiveGhostOpacity);
29497 context.translate(-gx, -gy);
29498 }
29499
29500 drawLine();
29501 drawArrows();
29502 drawOverlay();
29503 drawText();
29504
29505 if (shiftToOriginWithBb) {
29506 context.translate(bb.x1, bb.y1);
29507 }
29508 };
29509
29510 CRp$2.drawEdgeOverlay = function (context, edge) {
29511 if (!edge.visible()) {
29512 return;
29513 }
29514
29515 var overlayOpacity = edge.pstyle('overlay-opacity').value;
29516
29517 if (overlayOpacity === 0) {
29518 return;
29519 }
29520
29521 var r = this;
29522 var usePaths = r.usePaths();
29523 var rs = edge._private.rscratch;
29524 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
29525 var overlayWidth = 2 * overlayPadding;
29526 var overlayColor = edge.pstyle('overlay-color').value;
29527 context.lineWidth = overlayWidth;
29528
29529 if (rs.edgeType === 'self' && !usePaths) {
29530 context.lineCap = 'butt';
29531 } else {
29532 context.lineCap = 'round';
29533 }
29534
29535 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29536 r.drawEdgePath(edge, context, rs.allpts, 'solid');
29537 };
29538
29539 CRp$2.drawEdgePath = function (edge, context, pts, type) {
29540 var rs = edge._private.rscratch;
29541 var canvasCxt = context;
29542 var path;
29543 var pathCacheHit = false;
29544 var usePaths = this.usePaths();
29545 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
29546 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
29547
29548 if (usePaths) {
29549 var pathCacheKey = pts.join('$');
29550 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
29551
29552 if (keyMatches) {
29553 path = context = rs.pathCache;
29554 pathCacheHit = true;
29555 } else {
29556 path = context = new Path2D();
29557 rs.pathCacheKey = pathCacheKey;
29558 rs.pathCache = path;
29559 }
29560 }
29561
29562 if (canvasCxt.setLineDash) {
29563 // for very outofdate browsers
29564 switch (type) {
29565 case 'dotted':
29566 canvasCxt.setLineDash([1, 1]);
29567 break;
29568
29569 case 'dashed':
29570 canvasCxt.setLineDash(lineDashPattern);
29571 canvasCxt.lineDashOffset = lineDashOffset;
29572 break;
29573
29574 case 'solid':
29575 canvasCxt.setLineDash([]);
29576 break;
29577 }
29578 }
29579
29580 if (!pathCacheHit && !rs.badLine) {
29581 if (context.beginPath) {
29582 context.beginPath();
29583 }
29584
29585 context.moveTo(pts[0], pts[1]);
29586
29587 switch (rs.edgeType) {
29588 case 'bezier':
29589 case 'self':
29590 case 'compound':
29591 case 'multibezier':
29592 for (var i = 2; i + 3 < pts.length; i += 4) {
29593 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29594 }
29595
29596 break;
29597
29598 case 'straight':
29599 case 'segments':
29600 case 'haystack':
29601 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29602 context.lineTo(pts[_i], pts[_i + 1]);
29603 }
29604
29605 break;
29606 }
29607 }
29608
29609 context = canvasCxt;
29610
29611 if (usePaths) {
29612 context.stroke(path);
29613 } else {
29614 context.stroke();
29615 } // reset any line dashes
29616
29617
29618 if (context.setLineDash) {
29619 // for very outofdate browsers
29620 context.setLineDash([]);
29621 }
29622 };
29623
29624 CRp$2.drawArrowheads = function (context, edge, opacity) {
29625 var rs = edge._private.rscratch;
29626 var isHaystack = rs.edgeType === 'haystack';
29627
29628 if (!isHaystack) {
29629 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29630 }
29631
29632 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29633 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29634
29635 if (!isHaystack) {
29636 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29637 }
29638 };
29639
29640 CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29641 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29642 return;
29643 }
29644
29645 var self = this;
29646 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29647
29648 if (arrowShape === 'none') {
29649 return;
29650 }
29651
29652 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29653 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29654 var edgeWidth = edge.pstyle('width').pfValue;
29655 var edgeOpacity = edge.pstyle('opacity').value;
29656
29657 if (opacity === undefined) {
29658 opacity = edgeOpacity;
29659 }
29660
29661 var gco = context.globalCompositeOperation;
29662
29663 if (opacity !== 1 || arrowFill === 'hollow') {
29664 // then extra clear is needed
29665 context.globalCompositeOperation = 'destination-out';
29666 self.colorFillStyle(context, 255, 255, 255, 1);
29667 self.colorStrokeStyle(context, 255, 255, 255, 1);
29668 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29669 context.globalCompositeOperation = gco;
29670 } // otherwise, the opaque arrow clears it for free :)
29671
29672
29673 var color = edge.pstyle(prefix + '-arrow-color').value;
29674 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29675 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29676 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29677 };
29678
29679 CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29680 var r = this;
29681 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29682 var pathCacheHit = false;
29683 var path;
29684 var canvasContext = context;
29685 var translation = {
29686 x: x,
29687 y: y
29688 };
29689 var scale = edge.pstyle('arrow-scale').value;
29690 var size = this.getArrowWidth(edgeWidth, scale);
29691 var shapeImpl = r.arrowShapes[shape];
29692
29693 if (usePaths) {
29694 var cache = r.arrowPathCache = r.arrowPathCache || [];
29695 var key = hashString(shape);
29696 var cachedPath = cache[key];
29697
29698 if (cachedPath != null) {
29699 path = context = cachedPath;
29700 pathCacheHit = true;
29701 } else {
29702 path = context = new Path2D();
29703 cache[key] = path;
29704 }
29705 }
29706
29707 if (!pathCacheHit) {
29708 if (context.beginPath) {
29709 context.beginPath();
29710 }
29711
29712 if (usePaths) {
29713 // store in the path cache with values easily manipulated later
29714 shapeImpl.draw(context, 1, 0, {
29715 x: 0,
29716 y: 0
29717 }, 1);
29718 } else {
29719 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29720 }
29721
29722 if (context.closePath) {
29723 context.closePath();
29724 }
29725 }
29726
29727 context = canvasContext;
29728
29729 if (usePaths) {
29730 // set transform to arrow position/orientation
29731 context.translate(x, y);
29732 context.rotate(angle);
29733 context.scale(size, size);
29734 }
29735
29736 if (fill === 'filled' || fill === 'both') {
29737 if (usePaths) {
29738 context.fill(path);
29739 } else {
29740 context.fill();
29741 }
29742 }
29743
29744 if (fill === 'hollow' || fill === 'both') {
29745 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29746 context.lineJoin = 'miter';
29747
29748 if (usePaths) {
29749 context.stroke(path);
29750 } else {
29751 context.stroke();
29752 }
29753 }
29754
29755 if (usePaths) {
29756 // reset transform by applying inverse
29757 context.scale(1 / size, 1 / size);
29758 context.rotate(-angle);
29759 context.translate(-x, -y);
29760 }
29761 };
29762
29763 var CRp$3 = {};
29764
29765 CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29766 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29767 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29768 return;
29769 }
29770
29771 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29772 };
29773
29774 CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29775 var r = this;
29776 var pos = node.position();
29777 var nodeX = pos.x;
29778 var nodeY = pos.y;
29779 var styleObj = node.cy().style();
29780 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29781 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29782 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29783 var nodeW = node.width();
29784 var nodeH = node.height();
29785 var paddingX2 = node.padding() * 2;
29786 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29787 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29788 var rs = node._private.rscratch;
29789 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29790 var shouldClip = clip === 'node';
29791 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29792 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29793 var imgW = img.width || img.cachedW;
29794 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29795
29796 if (null == imgW || null == imgH) {
29797 document.body.appendChild(img); // eslint-disable-line no-undef
29798
29799 imgW = img.cachedW = img.width || img.offsetWidth;
29800 imgH = img.cachedH = img.height || img.offsetHeight;
29801 document.body.removeChild(img); // eslint-disable-line no-undef
29802 }
29803
29804 var w = imgW;
29805 var h = imgH;
29806
29807 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29808 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29809 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29810 } else {
29811 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29812 }
29813 }
29814
29815 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29816 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29817 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29818 } else {
29819 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29820 }
29821 }
29822
29823 if (w === 0 || h === 0) {
29824 return; // no point in drawing empty image (and chrome is broken in this case)
29825 }
29826
29827 if (fit === 'contain') {
29828 var scale = Math.min(nodeTW / w, nodeTH / h);
29829 w *= scale;
29830 h *= scale;
29831 } else if (fit === 'cover') {
29832 var scale = Math.max(nodeTW / w, nodeTH / h);
29833 w *= scale;
29834 h *= scale;
29835 }
29836
29837 var x = nodeX - nodeTW / 2; // left
29838
29839 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29840 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29841
29842 if (posXUnits === '%') {
29843 x += (nodeTW - w) * posXPfVal;
29844 } else {
29845 x += posXPfVal;
29846 }
29847
29848 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29849 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29850
29851 if (offXUnits === '%') {
29852 x += (nodeTW - w) * offXPfVal;
29853 } else {
29854 x += offXPfVal;
29855 }
29856
29857 var y = nodeY - nodeTH / 2; // top
29858
29859 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29860 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29861
29862 if (posYUnits === '%') {
29863 y += (nodeTH - h) * posYPfVal;
29864 } else {
29865 y += posYPfVal;
29866 }
29867
29868 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29869 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29870
29871 if (offYUnits === '%') {
29872 y += (nodeTH - h) * offYPfVal;
29873 } else {
29874 y += offYPfVal;
29875 }
29876
29877 if (rs.pathCache) {
29878 x -= nodeX;
29879 y -= nodeY;
29880 nodeX = 0;
29881 nodeY = 0;
29882 }
29883
29884 var gAlpha = context.globalAlpha;
29885 context.globalAlpha = imgOpacity;
29886 var smoothingEnabled = r.getImgSmoothing(context);
29887 var isSmoothingSwitched = false;
29888
29889 if (smooth === 'no' && smoothingEnabled) {
29890 r.setImgSmoothing(context, false);
29891 isSmoothingSwitched = true;
29892 } else if (smooth === 'yes' && !smoothingEnabled) {
29893 r.setImgSmoothing(context, true);
29894 isSmoothingSwitched = true;
29895 }
29896
29897 if (repeat === 'no-repeat') {
29898 if (shouldClip) {
29899 context.save();
29900
29901 if (rs.pathCache) {
29902 context.clip(rs.pathCache);
29903 } else {
29904 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29905 context.clip();
29906 }
29907 }
29908
29909 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29910
29911 if (shouldClip) {
29912 context.restore();
29913 }
29914 } else {
29915 var pattern = context.createPattern(img, repeat);
29916 context.fillStyle = pattern;
29917 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29918 context.translate(x, y);
29919 context.fill();
29920 context.translate(-x, -y);
29921 }
29922
29923 context.globalAlpha = gAlpha;
29924
29925 if (isSmoothingSwitched) {
29926 r.setImgSmoothing(context, smoothingEnabled);
29927 }
29928 };
29929
29930 var CRp$4 = {};
29931
29932 CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29933 if (!scale) {
29934 var zoom = ele.cy().zoom();
29935 var pxRatio = this.getPixelRatio();
29936 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29937
29938 scale = Math.pow(2, lvl);
29939 }
29940
29941 var computedSize = ele.pstyle('font-size').pfValue * scale;
29942 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29943
29944 if (computedSize < minSize) {
29945 return false;
29946 }
29947
29948 return true;
29949 };
29950
29951 CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29952 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29953 var r = this;
29954
29955 if (force == null) {
29956 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29957 return;
29958 }
29959 } else if (force === false) {
29960 return;
29961 }
29962
29963 if (ele.isNode()) {
29964 var label = ele.pstyle('label');
29965
29966 if (!label || !label.value) {
29967 return;
29968 }
29969
29970 var justification = r.getLabelJustification(ele);
29971 context.textAlign = justification;
29972 context.textBaseline = 'bottom';
29973 } else {
29974 var badLine = ele.element()._private.rscratch.badLine;
29975
29976 var _label = ele.pstyle('label');
29977
29978 var srcLabel = ele.pstyle('source-label');
29979 var tgtLabel = ele.pstyle('target-label');
29980
29981 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29982 return;
29983 }
29984
29985 context.textAlign = 'center';
29986 context.textBaseline = 'bottom';
29987 }
29988
29989 var applyRotation = !shiftToOriginWithBb;
29990 var bb;
29991
29992 if (shiftToOriginWithBb) {
29993 bb = shiftToOriginWithBb;
29994 context.translate(-bb.x1, -bb.y1);
29995 }
29996
29997 if (prefix == null) {
29998 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29999
30000 if (ele.isEdge()) {
30001 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
30002 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
30003 }
30004 } else {
30005 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
30006 }
30007
30008 if (shiftToOriginWithBb) {
30009 context.translate(bb.x1, bb.y1);
30010 }
30011 };
30012
30013 CRp$4.getFontCache = function (context) {
30014 var cache;
30015 this.fontCaches = this.fontCaches || [];
30016
30017 for (var i = 0; i < this.fontCaches.length; i++) {
30018 cache = this.fontCaches[i];
30019
30020 if (cache.context === context) {
30021 return cache;
30022 }
30023 }
30024
30025 cache = {
30026 context: context
30027 };
30028 this.fontCaches.push(cache);
30029 return cache;
30030 }; // set up canvas context with font
30031 // returns transformed text string
30032
30033
30034 CRp$4.setupTextStyle = function (context, ele) {
30035 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
30036 // Font style
30037 var labelStyle = ele.pstyle('font-style').strValue;
30038 var labelSize = ele.pstyle('font-size').pfValue + 'px';
30039 var labelFamily = ele.pstyle('font-family').strValue;
30040 var labelWeight = ele.pstyle('font-weight').strValue;
30041 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
30042 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
30043 var color = ele.pstyle('color').value;
30044 var outlineColor = ele.pstyle('text-outline-color').value;
30045 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
30046 context.lineJoin = 'round'; // so text outlines aren't jagged
30047
30048 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30049 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
30050 }; // TODO ensure re-used
30051
30052
30053 function roundRect(ctx, x, y, width, height) {
30054 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
30055 ctx.beginPath();
30056 ctx.moveTo(x + radius, y);
30057 ctx.lineTo(x + width - radius, y);
30058 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
30059 ctx.lineTo(x + width, y + height - radius);
30060 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
30061 ctx.lineTo(x + radius, y + height);
30062 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
30063 ctx.lineTo(x, y + radius);
30064 ctx.quadraticCurveTo(x, y, x + radius, y);
30065 ctx.closePath();
30066 ctx.fill();
30067 }
30068
30069 CRp$4.getTextAngle = function (ele, prefix) {
30070 var theta;
30071 var _p = ele._private;
30072 var rscratch = _p.rscratch;
30073 var pdash = prefix ? prefix + '-' : '';
30074 var rotation = ele.pstyle(pdash + 'text-rotation');
30075 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
30076
30077 if (rotation.strValue === 'autorotate') {
30078 theta = ele.isEdge() ? textAngle : 0;
30079 } else if (rotation.strValue === 'none') {
30080 theta = 0;
30081 } else {
30082 theta = rotation.pfValue;
30083 }
30084
30085 return theta;
30086 };
30087
30088 CRp$4.drawText = function (context, ele, prefix) {
30089 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30090 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30091 var _p = ele._private;
30092 var rscratch = _p.rscratch;
30093 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
30094
30095 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
30096 return;
30097 } // use 'main' as an alias for the main label (i.e. null prefix)
30098
30099
30100 if (prefix === 'main') {
30101 prefix = null;
30102 }
30103
30104 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
30105 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
30106 var orgTextX, orgTextY; // used for rotation
30107
30108 var text = this.getLabelText(ele, prefix);
30109
30110 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
30111 this.setupTextStyle(context, ele, useEleOpacity);
30112 var pdash = prefix ? prefix + '-' : '';
30113 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
30114 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
30115 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
30116 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
30117 var isEdge = ele.isEdge();
30118 var halign = ele.pstyle('text-halign').value;
30119 var valign = ele.pstyle('text-valign').value;
30120
30121 if (isEdge) {
30122 halign = 'center';
30123 valign = 'center';
30124 }
30125
30126 textX += marginX;
30127 textY += marginY;
30128 var theta;
30129
30130 if (!applyRotation) {
30131 theta = 0;
30132 } else {
30133 theta = this.getTextAngle(ele, prefix);
30134 }
30135
30136 if (theta !== 0) {
30137 orgTextX = textX;
30138 orgTextY = textY;
30139 context.translate(orgTextX, orgTextY);
30140 context.rotate(theta);
30141 textX = 0;
30142 textY = 0;
30143 }
30144
30145 switch (valign) {
30146 case 'top':
30147 break;
30148
30149 case 'center':
30150 textY += textH / 2;
30151 break;
30152
30153 case 'bottom':
30154 textY += textH;
30155 break;
30156 }
30157
30158 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
30159 var borderOpacity = ele.pstyle('text-border-opacity').value;
30160 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
30161 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
30162
30163 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
30164 var bgX = textX - backgroundPadding;
30165
30166 switch (halign) {
30167 case 'left':
30168 bgX -= textW;
30169 break;
30170
30171 case 'center':
30172 bgX -= textW / 2;
30173 break;
30174 }
30175
30176 var bgY = textY - textH - backgroundPadding;
30177 var bgW = textW + 2 * backgroundPadding;
30178 var bgH = textH + 2 * backgroundPadding;
30179
30180 if (backgroundOpacity > 0) {
30181 var textFill = context.fillStyle;
30182 var textBackgroundColor = ele.pstyle('text-background-color').value;
30183 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
30184 var styleShape = ele.pstyle('text-background-shape').strValue;
30185
30186 if (styleShape.indexOf('round') === 0) {
30187 roundRect(context, bgX, bgY, bgW, bgH, 2);
30188 } else {
30189 context.fillRect(bgX, bgY, bgW, bgH);
30190 }
30191
30192 context.fillStyle = textFill;
30193 }
30194
30195 if (textBorderWidth > 0 && borderOpacity > 0) {
30196 var textStroke = context.strokeStyle;
30197 var textLineWidth = context.lineWidth;
30198 var textBorderColor = ele.pstyle('text-border-color').value;
30199 var textBorderStyle = ele.pstyle('text-border-style').value;
30200 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
30201 context.lineWidth = textBorderWidth;
30202
30203 if (context.setLineDash) {
30204 // for very outofdate browsers
30205 switch (textBorderStyle) {
30206 case 'dotted':
30207 context.setLineDash([1, 1]);
30208 break;
30209
30210 case 'dashed':
30211 context.setLineDash([4, 2]);
30212 break;
30213
30214 case 'double':
30215 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
30216
30217 context.setLineDash([]);
30218 break;
30219
30220 case 'solid':
30221 context.setLineDash([]);
30222 break;
30223 }
30224 }
30225
30226 context.strokeRect(bgX, bgY, bgW, bgH);
30227
30228 if (textBorderStyle === 'double') {
30229 var whiteWidth = textBorderWidth / 2;
30230 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
30231 }
30232
30233 if (context.setLineDash) {
30234 // for very outofdate browsers
30235 context.setLineDash([]);
30236 }
30237
30238 context.lineWidth = textLineWidth;
30239 context.strokeStyle = textStroke;
30240 }
30241 }
30242
30243 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
30244
30245 if (lineWidth > 0) {
30246 context.lineWidth = lineWidth;
30247 }
30248
30249 if (ele.pstyle('text-wrap').value === 'wrap') {
30250 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
30251 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
30252 var halfTextW = textW / 2;
30253 var justification = this.getLabelJustification(ele);
30254
30255 if (justification === 'auto') ; else if (halign === 'left') {
30256 // auto justification : right
30257 if (justification === 'left') {
30258 textX += -textW;
30259 } else if (justification === 'center') {
30260 textX += -halfTextW;
30261 } // else same as auto
30262
30263 } else if (halign === 'center') {
30264 // auto justfication : center
30265 if (justification === 'left') {
30266 textX += -halfTextW;
30267 } else if (justification === 'right') {
30268 textX += halfTextW;
30269 } // else same as auto
30270
30271 } else if (halign === 'right') {
30272 // auto justification : left
30273 if (justification === 'center') {
30274 textX += halfTextW;
30275 } else if (justification === 'right') {
30276 textX += textW;
30277 } // else same as auto
30278
30279 }
30280
30281 switch (valign) {
30282 case 'top':
30283 textY -= (lines.length - 1) * lineHeight;
30284 break;
30285
30286 case 'center':
30287 case 'bottom':
30288 textY -= (lines.length - 1) * lineHeight;
30289 break;
30290 }
30291
30292 for (var l = 0; l < lines.length; l++) {
30293 if (lineWidth > 0) {
30294 context.strokeText(lines[l], textX, textY);
30295 }
30296
30297 context.fillText(lines[l], textX, textY);
30298 textY += lineHeight;
30299 }
30300 } else {
30301 if (lineWidth > 0) {
30302 context.strokeText(text, textX, textY);
30303 }
30304
30305 context.fillText(text, textX, textY);
30306 }
30307
30308 if (theta !== 0) {
30309 context.rotate(-theta);
30310 context.translate(-orgTextX, -orgTextY);
30311 }
30312 }
30313 };
30314
30315 /* global Path2D */
30316 var CRp$5 = {};
30317
30318 CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
30319 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30320 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30321 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
30322 var r = this;
30323 var nodeWidth, nodeHeight;
30324 var _p = node._private;
30325 var rs = _p.rscratch;
30326 var pos = node.position();
30327
30328 if (!number(pos.x) || !number(pos.y)) {
30329 return; // can't draw node with undefined position
30330 }
30331
30332 if (shouldDrawOpacity && !node.visible()) {
30333 return;
30334 }
30335
30336 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
30337 var usePaths = r.usePaths();
30338 var path;
30339 var pathCacheHit = false;
30340 var padding = node.padding();
30341 nodeWidth = node.width() + 2 * padding;
30342 nodeHeight = node.height() + 2 * padding; //
30343 // setup shift
30344
30345 var bb;
30346
30347 if (shiftToOriginWithBb) {
30348 bb = shiftToOriginWithBb;
30349 context.translate(-bb.x1, -bb.y1);
30350 } //
30351 // load bg image
30352
30353
30354 var bgImgProp = node.pstyle('background-image');
30355 var urls = bgImgProp.value;
30356 var urlDefined = new Array(urls.length);
30357 var image = new Array(urls.length);
30358 var numImages = 0;
30359
30360 for (var i = 0; i < urls.length; i++) {
30361 var url = urls[i];
30362 var defd = urlDefined[i] = url != null && url !== 'none';
30363
30364 if (defd) {
30365 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
30366 numImages++; // get image, and if not loaded then ask to redraw when later loaded
30367
30368 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
30369 _p.backgroundTimestamp = Date.now();
30370 node.emitAndNotify('background');
30371 });
30372 }
30373 } //
30374 // setup styles
30375
30376
30377 var darkness = node.pstyle('background-blacken').value;
30378 var borderWidth = node.pstyle('border-width').pfValue;
30379 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
30380 var borderColor = node.pstyle('border-color').value;
30381 var borderStyle = node.pstyle('border-style').value;
30382 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
30383 context.lineJoin = 'miter'; // so borders are square with the node shape
30384
30385 var setupShapeColor = function setupShapeColor() {
30386 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
30387 r.eleFillStyle(context, node, bgOpy);
30388 };
30389
30390 var setupBorderColor = function setupBorderColor() {
30391 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
30392 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
30393 }; //
30394 // setup shape
30395
30396
30397 var styleShape = node.pstyle('shape').strValue;
30398 var shapePts = node.pstyle('shape-polygon-points').pfValue;
30399
30400 if (usePaths) {
30401 context.translate(pos.x, pos.y);
30402 var pathCache = r.nodePathCache = r.nodePathCache || [];
30403 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
30404 var cachedPath = pathCache[key];
30405
30406 if (cachedPath != null) {
30407 path = cachedPath;
30408 pathCacheHit = true;
30409 rs.pathCache = path;
30410 } else {
30411 path = new Path2D();
30412 pathCache[key] = rs.pathCache = path;
30413 }
30414 }
30415
30416 var drawShape = function drawShape() {
30417 if (!pathCacheHit) {
30418 var npos = pos;
30419
30420 if (usePaths) {
30421 npos = {
30422 x: 0,
30423 y: 0
30424 };
30425 }
30426
30427 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
30428 }
30429
30430 if (usePaths) {
30431 context.fill(path);
30432 } else {
30433 context.fill();
30434 }
30435 };
30436
30437 var drawImages = function drawImages() {
30438 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30439 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
30440 var prevBging = _p.backgrounding;
30441 var totalCompleted = 0;
30442
30443 for (var _i = 0; _i < image.length; _i++) {
30444 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
30445
30446 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
30447 totalCompleted++;
30448 continue;
30449 }
30450
30451 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
30452 totalCompleted++;
30453 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
30454 }
30455 }
30456
30457 _p.backgrounding = !(totalCompleted === numImages);
30458
30459 if (prevBging !== _p.backgrounding) {
30460 // update style b/c :backgrounding state changed
30461 node.updateStyle(false);
30462 }
30463 };
30464
30465 var drawPie = function drawPie() {
30466 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
30467 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
30468
30469 if (r.hasPie(node)) {
30470 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
30471
30472 if (redrawShape) {
30473 if (!usePaths) {
30474 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
30475 }
30476 }
30477 }
30478 };
30479
30480 var darken = function darken() {
30481 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30482 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
30483 var c = darkness > 0 ? 0 : 255;
30484
30485 if (darkness !== 0) {
30486 r.colorFillStyle(context, c, c, c, opacity);
30487
30488 if (usePaths) {
30489 context.fill(path);
30490 } else {
30491 context.fill();
30492 }
30493 }
30494 };
30495
30496 var drawBorder = function drawBorder() {
30497 if (borderWidth > 0) {
30498 context.lineWidth = borderWidth;
30499 context.lineCap = 'butt';
30500
30501 if (context.setLineDash) {
30502 // for very outofdate browsers
30503 switch (borderStyle) {
30504 case 'dotted':
30505 context.setLineDash([1, 1]);
30506 break;
30507
30508 case 'dashed':
30509 context.setLineDash([4, 2]);
30510 break;
30511
30512 case 'solid':
30513 case 'double':
30514 context.setLineDash([]);
30515 break;
30516 }
30517 }
30518
30519 if (usePaths) {
30520 context.stroke(path);
30521 } else {
30522 context.stroke();
30523 }
30524
30525 if (borderStyle === 'double') {
30526 context.lineWidth = borderWidth / 3;
30527 var gco = context.globalCompositeOperation;
30528 context.globalCompositeOperation = 'destination-out';
30529
30530 if (usePaths) {
30531 context.stroke(path);
30532 } else {
30533 context.stroke();
30534 }
30535
30536 context.globalCompositeOperation = gco;
30537 } // reset in case we changed the border style
30538
30539
30540 if (context.setLineDash) {
30541 // for very outofdate browsers
30542 context.setLineDash([]);
30543 }
30544 }
30545 };
30546
30547 var drawOverlay = function drawOverlay() {
30548 if (shouldDrawOverlay) {
30549 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30550 }
30551 };
30552
30553 var drawText = function drawText() {
30554 r.drawElementText(context, node, null, drawLabel);
30555 };
30556
30557 var ghost = node.pstyle('ghost').value === 'yes';
30558
30559 if (ghost) {
30560 var gx = node.pstyle('ghost-offset-x').pfValue;
30561 var gy = node.pstyle('ghost-offset-y').pfValue;
30562 var ghostOpacity = node.pstyle('ghost-opacity').value;
30563 var effGhostOpacity = ghostOpacity * eleOpacity;
30564 context.translate(gx, gy);
30565 setupShapeColor(ghostOpacity * bgOpacity);
30566 drawShape();
30567 drawImages(effGhostOpacity, true);
30568 setupBorderColor(ghostOpacity * borderOpacity);
30569 drawBorder();
30570 drawPie(darkness !== 0 || borderWidth !== 0);
30571 drawImages(effGhostOpacity, false);
30572 darken(effGhostOpacity);
30573 context.translate(-gx, -gy);
30574 }
30575
30576 setupShapeColor();
30577 drawShape();
30578 drawImages(eleOpacity, true);
30579 setupBorderColor();
30580 drawBorder();
30581 drawPie(darkness !== 0 || borderWidth !== 0);
30582 drawImages(eleOpacity, false);
30583 darken();
30584
30585 if (usePaths) {
30586 context.translate(-pos.x, -pos.y);
30587 }
30588
30589 drawText();
30590 drawOverlay(); //
30591 // clean up shift
30592
30593 if (shiftToOriginWithBb) {
30594 context.translate(bb.x1, bb.y1);
30595 }
30596 };
30597
30598 CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
30599 var r = this;
30600
30601 if (!node.visible()) {
30602 return;
30603 }
30604
30605 var overlayPadding = node.pstyle('overlay-padding').pfValue;
30606 var overlayOpacity = node.pstyle('overlay-opacity').value;
30607 var overlayColor = node.pstyle('overlay-color').value;
30608
30609 if (overlayOpacity > 0) {
30610 pos = pos || node.position();
30611
30612 if (nodeWidth == null || nodeHeight == null) {
30613 var padding = node.padding();
30614 nodeWidth = node.width() + 2 * padding;
30615 nodeHeight = node.height() + 2 * padding;
30616 }
30617
30618 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
30619 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
30620 context.fill();
30621 }
30622 }; // does the node have at least one pie piece?
30623
30624
30625 CRp$5.hasPie = function (node) {
30626 node = node[0]; // ensure ele ref
30627
30628 return node._private.hasPie;
30629 };
30630
30631 CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30632 node = node[0]; // ensure ele ref
30633
30634 pos = pos || node.position();
30635 var cyStyle = node.cy().style();
30636 var pieSize = node.pstyle('pie-size');
30637 var x = pos.x;
30638 var y = pos.y;
30639 var nodeW = node.width();
30640 var nodeH = node.height();
30641 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30642
30643 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30644
30645 var usePaths = this.usePaths();
30646
30647 if (usePaths) {
30648 x = 0;
30649 y = 0;
30650 }
30651
30652 if (pieSize.units === '%') {
30653 radius = radius * pieSize.pfValue;
30654 } else if (pieSize.pfValue !== undefined) {
30655 radius = pieSize.pfValue / 2;
30656 }
30657
30658 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30659 // 1..N
30660 var size = node.pstyle('pie-' + i + '-background-size').value;
30661 var color = node.pstyle('pie-' + i + '-background-color').value;
30662 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30663 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30664 // percent can't push beyond 1
30665
30666 if (percent + lastPercent > 1) {
30667 percent = 1 - lastPercent;
30668 }
30669
30670 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30671
30672 var angleDelta = 2 * Math.PI * percent;
30673 var angleEnd = angleStart + angleDelta; // ignore if
30674 // - zero size
30675 // - we're already beyond the full circle
30676 // - adding the current slice would go beyond the full circle
30677
30678 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30679 continue;
30680 }
30681
30682 context.beginPath();
30683 context.moveTo(x, y);
30684 context.arc(x, y, radius, angleStart, angleEnd);
30685 context.closePath();
30686 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30687 context.fill();
30688 lastPercent += percent;
30689 }
30690 };
30691
30692 var CRp$6 = {};
30693 var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30694
30695 CRp$6.getPixelRatio = function () {
30696 var context = this.data.contexts[0];
30697
30698 if (this.forcedPixelRatio != null) {
30699 return this.forcedPixelRatio;
30700 }
30701
30702 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30703 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30704 };
30705
30706 CRp$6.paintCache = function (context) {
30707 var caches = this.paintCaches = this.paintCaches || [];
30708 var needToCreateCache = true;
30709 var cache;
30710
30711 for (var i = 0; i < caches.length; i++) {
30712 cache = caches[i];
30713
30714 if (cache.context === context) {
30715 needToCreateCache = false;
30716 break;
30717 }
30718 }
30719
30720 if (needToCreateCache) {
30721 cache = {
30722 context: context
30723 };
30724 caches.push(cache);
30725 }
30726
30727 return cache;
30728 };
30729
30730 CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30731 var gradientStyle;
30732 var usePaths = this.usePaths();
30733 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30734 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30735
30736 if (fill === 'radial-gradient') {
30737 if (ele.isEdge()) {
30738 var start = ele.sourceEndpoint(),
30739 end = ele.targetEndpoint(),
30740 mid = ele.midpoint();
30741 var d1 = dist(start, mid);
30742 var d2 = dist(end, mid);
30743 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30744 } else {
30745 var pos = usePaths ? {
30746 x: 0,
30747 y: 0
30748 } : ele.position(),
30749 width = ele.paddedWidth(),
30750 height = ele.paddedHeight();
30751 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30752 }
30753 } else {
30754 if (ele.isEdge()) {
30755 var _start = ele.sourceEndpoint(),
30756 _end = ele.targetEndpoint();
30757
30758 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30759 } else {
30760 var _pos = usePaths ? {
30761 x: 0,
30762 y: 0
30763 } : ele.position(),
30764 _width = ele.paddedWidth(),
30765 _height = ele.paddedHeight(),
30766 halfWidth = _width / 2,
30767 halfHeight = _height / 2;
30768
30769 var direction = ele.pstyle('background-gradient-direction').value;
30770
30771 switch (direction) {
30772 case 'to-bottom':
30773 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30774 break;
30775
30776 case 'to-top':
30777 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30778 break;
30779
30780 case 'to-left':
30781 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30782 break;
30783
30784 case 'to-right':
30785 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30786 break;
30787
30788 case 'to-bottom-right':
30789 case 'to-right-bottom':
30790 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30791 break;
30792
30793 case 'to-top-right':
30794 case 'to-right-top':
30795 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30796 break;
30797
30798 case 'to-bottom-left':
30799 case 'to-left-bottom':
30800 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30801 break;
30802
30803 case 'to-top-left':
30804 case 'to-left-top':
30805 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30806 break;
30807 }
30808 }
30809 }
30810
30811 if (!gradientStyle) return null; // invalid gradient style
30812
30813 var hasPositions = positions.length === colors.length;
30814 var length = colors.length;
30815
30816 for (var i = 0; i < length; i++) {
30817 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30818 }
30819
30820 return gradientStyle;
30821 };
30822
30823 CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30824 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30825 if (!gradientStyle) return null; // error
30826
30827 context.fillStyle = gradientStyle;
30828 };
30829
30830 CRp$6.colorFillStyle = function (context, r, g, b, a) {
30831 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30832 // var cache = this.paintCache(context);
30833 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30834 // if( cache.fillStyle !== fillStyle ){
30835 // context.fillStyle = cache.fillStyle = fillStyle;
30836 // }
30837 };
30838
30839 CRp$6.eleFillStyle = function (context, ele, opacity) {
30840 var backgroundFill = ele.pstyle('background-fill').value;
30841
30842 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30843 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30844 } else {
30845 var backgroundColor = ele.pstyle('background-color').value;
30846 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30847 }
30848 };
30849
30850 CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30851 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30852 if (!gradientStyle) return null; // error
30853
30854 context.strokeStyle = gradientStyle;
30855 };
30856
30857 CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30858 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30859 // var cache = this.paintCache(context);
30860 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30861 // if( cache.strokeStyle !== strokeStyle ){
30862 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30863 // }
30864 };
30865
30866 CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30867 var lineFill = ele.pstyle('line-fill').value;
30868
30869 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30870 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30871 } else {
30872 var lineColor = ele.pstyle('line-color').value;
30873 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30874 }
30875 }; // Resize canvas
30876
30877
30878 CRp$6.matchCanvasSize = function (container) {
30879 var r = this;
30880 var data = r.data;
30881 var bb = r.findContainerClientCoords();
30882 var width = bb[2];
30883 var height = bb[3];
30884 var pixelRatio = r.getPixelRatio();
30885 var mbPxRatio = r.motionBlurPxRatio;
30886
30887 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30888 pixelRatio = mbPxRatio;
30889 }
30890
30891 var canvasWidth = width * pixelRatio;
30892 var canvasHeight = height * pixelRatio;
30893 var canvas;
30894
30895 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30896 return; // save cycles if same
30897 }
30898
30899 r.fontCaches = null; // resizing resets the style
30900
30901 var canvasContainer = data.canvasContainer;
30902 canvasContainer.style.width = width + 'px';
30903 canvasContainer.style.height = height + 'px';
30904
30905 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30906 canvas = data.canvases[i];
30907 canvas.width = canvasWidth;
30908 canvas.height = canvasHeight;
30909 canvas.style.width = width + 'px';
30910 canvas.style.height = height + 'px';
30911 }
30912
30913 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30914 canvas = data.bufferCanvases[i];
30915 canvas.width = canvasWidth;
30916 canvas.height = canvasHeight;
30917 canvas.style.width = width + 'px';
30918 canvas.style.height = height + 'px';
30919 }
30920
30921 r.textureMult = 1;
30922
30923 if (pixelRatio <= 1) {
30924 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30925 r.textureMult = 2;
30926 canvas.width = canvasWidth * r.textureMult;
30927 canvas.height = canvasHeight * r.textureMult;
30928 }
30929
30930 r.canvasWidth = canvasWidth;
30931 r.canvasHeight = canvasHeight;
30932 };
30933
30934 CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30935 this.render({
30936 forcedContext: cxt,
30937 forcedZoom: zoom,
30938 forcedPan: pan,
30939 drawAllLayers: true,
30940 forcedPxRatio: pxRatio
30941 });
30942 };
30943
30944 CRp$6.render = function (options) {
30945 options = options || staticEmptyObject();
30946 var forcedContext = options.forcedContext;
30947 var drawAllLayers = options.drawAllLayers;
30948 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30949 var forcedZoom = options.forcedZoom;
30950 var forcedPan = options.forcedPan;
30951 var r = this;
30952 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30953 var cy = r.cy;
30954 var data = r.data;
30955 var needDraw = data.canvasNeedsRedraw;
30956 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30957 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30958 var mbPxRatio = r.motionBlurPxRatio;
30959 var hasCompoundNodes = cy.hasCompoundNodes();
30960 var inNodeDragGesture = r.hoverData.draggingEles;
30961 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30962 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30963 var motionBlurFadeEffect = motionBlur;
30964
30965 if (!forcedContext) {
30966 if (r.prevPxRatio !== pixelRatio) {
30967 r.invalidateContainerClientCoordsCache();
30968 r.matchCanvasSize(r.container);
30969 r.redrawHint('eles', true);
30970 r.redrawHint('drag', true);
30971 }
30972
30973 r.prevPxRatio = pixelRatio;
30974 }
30975
30976 if (!forcedContext && r.motionBlurTimeout) {
30977 clearTimeout(r.motionBlurTimeout);
30978 }
30979
30980 if (motionBlur) {
30981 if (r.mbFrames == null) {
30982 r.mbFrames = 0;
30983 }
30984
30985 r.mbFrames++;
30986
30987 if (r.mbFrames < 3) {
30988 // need several frames before even high quality motionblur
30989 motionBlurFadeEffect = false;
30990 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30991
30992
30993 if (r.mbFrames > r.minMbLowQualFrames) {
30994 //r.fullQualityMb = false;
30995 r.motionBlurPxRatio = r.mbPxRBlurry;
30996 }
30997 }
30998
30999 if (r.clearingMotionBlur) {
31000 r.motionBlurPxRatio = 1;
31001 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
31002 // because a rogue async texture frame would clear needDraw
31003
31004
31005 if (r.textureDrawLastFrame && !textureDraw) {
31006 needDraw[r.NODE] = true;
31007 needDraw[r.SELECT_BOX] = true;
31008 }
31009
31010 var style = cy.style();
31011 var zoom = cy.zoom();
31012 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
31013 var pan = cy.pan();
31014 var effectivePan = {
31015 x: pan.x,
31016 y: pan.y
31017 };
31018 var vp = {
31019 zoom: zoom,
31020 pan: {
31021 x: pan.x,
31022 y: pan.y
31023 }
31024 };
31025 var prevVp = r.prevViewport;
31026 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)
31027
31028 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
31029 r.motionBlurPxRatio = 1;
31030 }
31031
31032 if (forcedPan) {
31033 effectivePan = forcedPan;
31034 } // apply pixel ratio
31035
31036
31037 effectiveZoom *= pixelRatio;
31038 effectivePan.x *= pixelRatio;
31039 effectivePan.y *= pixelRatio;
31040 var eles = r.getCachedZSortedEles();
31041
31042 function mbclear(context, x, y, w, h) {
31043 var gco = context.globalCompositeOperation;
31044 context.globalCompositeOperation = 'destination-out';
31045 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
31046 context.fillRect(x, y, w, h);
31047 context.globalCompositeOperation = gco;
31048 }
31049
31050 function setContextTransform(context, clear) {
31051 var ePan, eZoom, w, h;
31052
31053 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
31054 ePan = {
31055 x: pan.x * mbPxRatio,
31056 y: pan.y * mbPxRatio
31057 };
31058 eZoom = zoom * mbPxRatio;
31059 w = r.canvasWidth * mbPxRatio;
31060 h = r.canvasHeight * mbPxRatio;
31061 } else {
31062 ePan = effectivePan;
31063 eZoom = effectiveZoom;
31064 w = r.canvasWidth;
31065 h = r.canvasHeight;
31066 }
31067
31068 context.setTransform(1, 0, 0, 1, 0, 0);
31069
31070 if (clear === 'motionBlur') {
31071 mbclear(context, 0, 0, w, h);
31072 } else if (!forcedContext && (clear === undefined || clear)) {
31073 context.clearRect(0, 0, w, h);
31074 }
31075
31076 if (!drawAllLayers) {
31077 context.translate(ePan.x, ePan.y);
31078 context.scale(eZoom, eZoom);
31079 }
31080
31081 if (forcedPan) {
31082 context.translate(forcedPan.x, forcedPan.y);
31083 }
31084
31085 if (forcedZoom) {
31086 context.scale(forcedZoom, forcedZoom);
31087 }
31088 }
31089
31090 if (!textureDraw) {
31091 r.textureDrawLastFrame = false;
31092 }
31093
31094 if (textureDraw) {
31095 r.textureDrawLastFrame = true;
31096
31097 if (!r.textureCache) {
31098 r.textureCache = {};
31099 r.textureCache.bb = cy.mutableElements().boundingBox();
31100 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
31101 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
31102 cxt.setTransform(1, 0, 0, 1, 0, 0);
31103 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
31104 r.render({
31105 forcedContext: cxt,
31106 drawOnlyNodeLayer: true,
31107 forcedPxRatio: pixelRatio * r.textureMult
31108 });
31109 var vp = r.textureCache.viewport = {
31110 zoom: cy.zoom(),
31111 pan: cy.pan(),
31112 width: r.canvasWidth,
31113 height: r.canvasHeight
31114 };
31115 vp.mpan = {
31116 x: (0 - vp.pan.x) / vp.zoom,
31117 y: (0 - vp.pan.y) / vp.zoom
31118 };
31119 }
31120
31121 needDraw[r.DRAG] = false;
31122 needDraw[r.NODE] = false;
31123 var context = data.contexts[r.NODE];
31124 var texture = r.textureCache.texture;
31125 var vp = r.textureCache.viewport;
31126 context.setTransform(1, 0, 0, 1, 0, 0);
31127
31128 if (motionBlur) {
31129 mbclear(context, 0, 0, vp.width, vp.height);
31130 } else {
31131 context.clearRect(0, 0, vp.width, vp.height);
31132 }
31133
31134 var outsideBgColor = style.core('outside-texture-bg-color').value;
31135 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
31136 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
31137 context.fillRect(0, 0, vp.width, vp.height);
31138 var zoom = cy.zoom();
31139 setContextTransform(context, false);
31140 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
31141 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
31142 } else if (r.textureOnViewport && !forcedContext) {
31143 // clear the cache since we don't need it
31144 r.textureCache = null;
31145 }
31146
31147 var extent = cy.extent();
31148 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
31149 var hideEdges = r.hideEdgesOnViewport && vpManip;
31150 var needMbClear = [];
31151 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
31152
31153 if (needMbClear[r.NODE]) {
31154 r.clearedForMotionBlur[r.NODE] = true;
31155 }
31156
31157 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
31158
31159 if (needMbClear[r.DRAG]) {
31160 r.clearedForMotionBlur[r.DRAG] = true;
31161 }
31162
31163 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
31164 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
31165 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
31166 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
31167 setContextTransform(context, clear);
31168
31169 if (hideEdges) {
31170 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
31171 } else {
31172 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
31173 }
31174
31175 if (r.debug) {
31176 r.drawDebugPoints(context, eles.nondrag);
31177 }
31178
31179 if (!drawAllLayers && !motionBlur) {
31180 needDraw[r.NODE] = false;
31181 }
31182 }
31183
31184 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
31185 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
31186 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
31187 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
31188
31189 if (hideEdges) {
31190 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
31191 } else {
31192 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
31193 }
31194
31195 if (r.debug) {
31196 r.drawDebugPoints(context, eles.drag);
31197 }
31198
31199 if (!drawAllLayers && !motionBlur) {
31200 needDraw[r.DRAG] = false;
31201 }
31202 }
31203
31204 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
31205 var context = forcedContext || data.contexts[r.SELECT_BOX];
31206 setContextTransform(context);
31207
31208 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
31209 var zoom = r.cy.zoom();
31210 var borderWidth = style.core('selection-box-border-width').value / zoom;
31211 context.lineWidth = borderWidth;
31212 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 + ')';
31213 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31214
31215 if (borderWidth > 0) {
31216 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 + ')';
31217 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31218 }
31219 }
31220
31221 if (data.bgActivePosistion && !r.hoverData.selecting) {
31222 var zoom = r.cy.zoom();
31223 var pos = data.bgActivePosistion;
31224 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 + ')';
31225 context.beginPath();
31226 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
31227 context.fill();
31228 }
31229
31230 var timeToRender = r.lastRedrawTime;
31231
31232 if (r.showFps && timeToRender) {
31233 timeToRender = Math.round(timeToRender);
31234 var fps = Math.round(1000 / timeToRender);
31235 context.setTransform(1, 0, 0, 1, 0, 0);
31236 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
31237 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
31238 context.lineWidth = 1;
31239 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
31240 var maxFps = 60;
31241 context.strokeRect(0, 30, 250, 20);
31242 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
31243 }
31244
31245 if (!drawAllLayers) {
31246 needDraw[r.SELECT_BOX] = false;
31247 }
31248 } // motionblur: blit rendered blurry frames
31249
31250
31251 if (motionBlur && mbPxRatio !== 1) {
31252 var cxtNode = data.contexts[r.NODE];
31253 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
31254 var cxtDrag = data.contexts[r.DRAG];
31255 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
31256
31257 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
31258 cxt.setTransform(1, 0, 0, 1, 0, 0);
31259
31260 if (needClear || !motionBlurFadeEffect) {
31261 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
31262 } else {
31263 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
31264 }
31265
31266 var pxr = mbPxRatio;
31267 cxt.drawImage(txt, // img
31268 0, 0, // sx, sy
31269 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
31270 0, 0, // x, y
31271 r.canvasWidth, r.canvasHeight // w, h
31272 );
31273 };
31274
31275 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
31276 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
31277 needDraw[r.NODE] = false;
31278 }
31279
31280 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
31281 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
31282 needDraw[r.DRAG] = false;
31283 }
31284 }
31285
31286 r.prevViewport = vp;
31287
31288 if (r.clearingMotionBlur) {
31289 r.clearingMotionBlur = false;
31290 r.motionBlurCleared = true;
31291 r.motionBlur = true;
31292 }
31293
31294 if (motionBlur) {
31295 r.motionBlurTimeout = setTimeout(function () {
31296 r.motionBlurTimeout = null;
31297 r.clearedForMotionBlur[r.NODE] = false;
31298 r.clearedForMotionBlur[r.DRAG] = false;
31299 r.motionBlur = false;
31300 r.clearingMotionBlur = !textureDraw;
31301 r.mbFrames = 0;
31302 needDraw[r.NODE] = true;
31303 needDraw[r.DRAG] = true;
31304 r.redraw();
31305 }, motionBlurDelay);
31306 }
31307
31308 if (!forcedContext) {
31309 cy.emit('render');
31310 }
31311 };
31312
31313 var CRp$7 = {}; // @O Polygon drawing
31314
31315 CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
31316 var halfW = width / 2;
31317 var halfH = height / 2;
31318
31319 if (context.beginPath) {
31320 context.beginPath();
31321 }
31322
31323 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
31324
31325 for (var i = 1; i < points.length / 2; i++) {
31326 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
31327 }
31328
31329 context.closePath();
31330 };
31331
31332 CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
31333 var halfW = width / 2;
31334 var halfH = height / 2;
31335 var cornerRadius = getRoundPolygonRadius(width, height);
31336
31337 if (context.beginPath) {
31338 context.beginPath();
31339 }
31340
31341 for (var _i = 0; _i < points.length / 4; _i++) {
31342 var sourceUv = void 0,
31343 destUv = void 0;
31344
31345 if (_i === 0) {
31346 sourceUv = points.length - 2;
31347 } else {
31348 sourceUv = _i * 4 - 2;
31349 }
31350
31351 destUv = _i * 4 + 2;
31352 var px = x + halfW * points[_i * 4];
31353 var py = y + halfH * points[_i * 4 + 1];
31354 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
31355 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
31356 var cp0x = px - offset * points[sourceUv];
31357 var cp0y = py - offset * points[sourceUv + 1];
31358 var cp1x = px + offset * points[destUv];
31359 var cp1y = py + offset * points[destUv + 1];
31360
31361 if (_i === 0) {
31362 context.moveTo(cp0x, cp0y);
31363 } else {
31364 context.lineTo(cp0x, cp0y);
31365 }
31366
31367 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
31368 }
31369
31370 context.closePath();
31371 }; // Round rectangle drawing
31372
31373
31374 CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
31375 var halfWidth = width / 2;
31376 var halfHeight = height / 2;
31377 var cornerRadius = getRoundRectangleRadius(width, height);
31378
31379 if (context.beginPath) {
31380 context.beginPath();
31381 } // Start at top middle
31382
31383
31384 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
31385
31386 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
31387
31388 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
31389
31390 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
31391
31392 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
31393
31394 context.lineTo(x, y - halfHeight);
31395 context.closePath();
31396 };
31397
31398 CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
31399 var halfWidth = width / 2;
31400 var halfHeight = height / 2;
31401 var cornerRadius = getRoundRectangleRadius(width, height);
31402
31403 if (context.beginPath) {
31404 context.beginPath();
31405 } // Start at top middle
31406
31407
31408 context.moveTo(x, y - halfHeight);
31409 context.lineTo(x + halfWidth, y - halfHeight);
31410 context.lineTo(x + halfWidth, y);
31411 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
31412 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
31413 context.lineTo(x - halfWidth, y - halfHeight);
31414 context.lineTo(x, y - halfHeight);
31415 context.closePath();
31416 };
31417
31418 CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
31419 var halfWidth = width / 2;
31420 var halfHeight = height / 2;
31421 var cornerLength = getCutRectangleCornerLength();
31422
31423 if (context.beginPath) {
31424 context.beginPath();
31425 }
31426
31427 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
31428 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
31429 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
31430 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
31431 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
31432 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
31433 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
31434 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
31435 context.closePath();
31436 };
31437
31438 CRp$7.drawBarrelPath = function (context, x, y, width, height) {
31439 var halfWidth = width / 2;
31440 var halfHeight = height / 2;
31441 var xBegin = x - halfWidth;
31442 var xEnd = x + halfWidth;
31443 var yBegin = y - halfHeight;
31444 var yEnd = y + halfHeight;
31445 var barrelCurveConstants = getBarrelCurveConstants(width, height);
31446 var wOffset = barrelCurveConstants.widthOffset;
31447 var hOffset = barrelCurveConstants.heightOffset;
31448 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
31449
31450 if (context.beginPath) {
31451 context.beginPath();
31452 }
31453
31454 context.moveTo(xBegin, yBegin + hOffset);
31455 context.lineTo(xBegin, yEnd - hOffset);
31456 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
31457 context.lineTo(xEnd - wOffset, yEnd);
31458 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
31459 context.lineTo(xEnd, yBegin + hOffset);
31460 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
31461 context.lineTo(xBegin + wOffset, yBegin);
31462 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
31463 context.closePath();
31464 };
31465
31466 var sin0 = Math.sin(0);
31467 var cos0 = Math.cos(0);
31468 var sin = {};
31469 var cos = {};
31470 var ellipseStepSize = Math.PI / 40;
31471
31472 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31473 sin[i] = Math.sin(i);
31474 cos[i] = Math.cos(i);
31475 }
31476
31477 CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
31478 if (context.beginPath) {
31479 context.beginPath();
31480 }
31481
31482 if (context.ellipse) {
31483 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
31484 } else {
31485 var xPos, yPos;
31486 var rw = width / 2;
31487 var rh = height / 2;
31488
31489 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31490 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
31491 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
31492
31493 if (i === 0) {
31494 context.moveTo(xPos, yPos);
31495 } else {
31496 context.lineTo(xPos, yPos);
31497 }
31498 }
31499 }
31500
31501 context.closePath();
31502 };
31503
31504 /* global atob, ArrayBuffer, Uint8Array, Blob */
31505 var CRp$8 = {};
31506
31507 CRp$8.createBuffer = function (w, h) {
31508 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
31509
31510 buffer.width = w;
31511 buffer.height = h;
31512 return [buffer, buffer.getContext('2d')];
31513 };
31514
31515 CRp$8.bufferCanvasImage = function (options) {
31516 var cy = this.cy;
31517 var eles = cy.mutableElements();
31518 var bb = eles.boundingBox();
31519 var ctrRect = this.findContainerClientCoords();
31520 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31521 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31522 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
31523 var pxRatio = this.getPixelRatio();
31524 var scale = 1;
31525
31526 if (options.scale !== undefined) {
31527 width *= options.scale;
31528 height *= options.scale;
31529 scale = options.scale;
31530 } else if (specdMaxDims) {
31531 var maxScaleW = Infinity;
31532 var maxScaleH = Infinity;
31533
31534 if (number(options.maxWidth)) {
31535 maxScaleW = scale * options.maxWidth / width;
31536 }
31537
31538 if (number(options.maxHeight)) {
31539 maxScaleH = scale * options.maxHeight / height;
31540 }
31541
31542 scale = Math.min(maxScaleW, maxScaleH);
31543 width *= scale;
31544 height *= scale;
31545 }
31546
31547 if (!specdMaxDims) {
31548 width *= pxRatio;
31549 height *= pxRatio;
31550 scale *= pxRatio;
31551 }
31552
31553 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31554
31555 buffCanvas.width = width;
31556 buffCanvas.height = height;
31557 buffCanvas.style.width = width + 'px';
31558 buffCanvas.style.height = height + 'px';
31559 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31560
31561 if (width > 0 && height > 0) {
31562 buffCxt.clearRect(0, 0, width, height);
31563 buffCxt.globalCompositeOperation = 'source-over';
31564 var zsortedEles = this.getCachedZSortedEles();
31565
31566 if (options.full) {
31567 // draw the full bounds of the graph
31568 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31569 buffCxt.scale(scale, scale);
31570 this.drawElements(buffCxt, zsortedEles);
31571 buffCxt.scale(1 / scale, 1 / scale);
31572 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31573 } else {
31574 // draw the current view
31575 var pan = cy.pan();
31576 var translation = {
31577 x: pan.x * scale,
31578 y: pan.y * scale
31579 };
31580 scale *= cy.zoom();
31581 buffCxt.translate(translation.x, translation.y);
31582 buffCxt.scale(scale, scale);
31583 this.drawElements(buffCxt, zsortedEles);
31584 buffCxt.scale(1 / scale, 1 / scale);
31585 buffCxt.translate(-translation.x, -translation.y);
31586 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31587
31588
31589 if (options.bg) {
31590 buffCxt.globalCompositeOperation = 'destination-over';
31591 buffCxt.fillStyle = options.bg;
31592 buffCxt.rect(0, 0, width, height);
31593 buffCxt.fill();
31594 }
31595 }
31596
31597 return buffCanvas;
31598 };
31599
31600 function b64ToBlob(b64, mimeType) {
31601 var bytes = atob(b64);
31602 var buff = new ArrayBuffer(bytes.length);
31603 var buffUint8 = new Uint8Array(buff);
31604
31605 for (var i = 0; i < bytes.length; i++) {
31606 buffUint8[i] = bytes.charCodeAt(i);
31607 }
31608
31609 return new Blob([buff], {
31610 type: mimeType
31611 });
31612 }
31613
31614 function b64UriToB64(b64uri) {
31615 var i = b64uri.indexOf(',');
31616 return b64uri.substr(i + 1);
31617 }
31618
31619 function output(options, canvas, mimeType) {
31620 var getB64Uri = function getB64Uri() {
31621 return canvas.toDataURL(mimeType, options.quality);
31622 };
31623
31624 switch (options.output) {
31625 case 'blob-promise':
31626 return new Promise$1(function (resolve, reject) {
31627 try {
31628 canvas.toBlob(function (blob) {
31629 if (blob != null) {
31630 resolve(blob);
31631 } else {
31632 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31633 }
31634 }, mimeType, options.quality);
31635 } catch (err) {
31636 reject(err);
31637 }
31638 });
31639
31640 case 'blob':
31641 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31642
31643 case 'base64':
31644 return b64UriToB64(getB64Uri());
31645
31646 case 'base64uri':
31647 default:
31648 return getB64Uri();
31649 }
31650 }
31651
31652 CRp$8.png = function (options) {
31653 return output(options, this.bufferCanvasImage(options), 'image/png');
31654 };
31655
31656 CRp$8.jpg = function (options) {
31657 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31658 };
31659
31660 var CRp$9 = {};
31661
31662 CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31663 switch (name) {
31664 case 'ellipse':
31665 return this.drawEllipsePath(context, centerX, centerY, width, height);
31666
31667 case 'polygon':
31668 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31669
31670 case 'round-polygon':
31671 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31672
31673 case 'roundrectangle':
31674 case 'round-rectangle':
31675 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31676
31677 case 'cutrectangle':
31678 case 'cut-rectangle':
31679 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31680
31681 case 'bottomroundrectangle':
31682 case 'bottom-round-rectangle':
31683 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31684
31685 case 'barrel':
31686 return this.drawBarrelPath(context, centerX, centerY, width, height);
31687 }
31688 };
31689
31690 var CR = CanvasRenderer;
31691 var CRp$a = CanvasRenderer.prototype;
31692 CRp$a.CANVAS_LAYERS = 3; //
31693
31694 CRp$a.SELECT_BOX = 0;
31695 CRp$a.DRAG = 1;
31696 CRp$a.NODE = 2;
31697 CRp$a.BUFFER_COUNT = 3; //
31698
31699 CRp$a.TEXTURE_BUFFER = 0;
31700 CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
31701 CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
31702
31703 function CanvasRenderer(options) {
31704 var r = this;
31705 r.data = {
31706 canvases: new Array(CRp$a.CANVAS_LAYERS),
31707 contexts: new Array(CRp$a.CANVAS_LAYERS),
31708 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
31709 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
31710 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
31711 };
31712 var tapHlOffAttr = '-webkit-tap-highlight-color';
31713 var tapHlOffStyle = 'rgba(0,0,0,0)';
31714 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31715
31716 var containerStyle = r.data.canvasContainer.style;
31717 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31718 containerStyle.position = 'relative';
31719 containerStyle.zIndex = '0';
31720 containerStyle.overflow = 'hidden';
31721 var container = options.cy.container();
31722 container.appendChild(r.data.canvasContainer);
31723 container.style[tapHlOffAttr] = tapHlOffStyle;
31724 var styleMap = {
31725 '-webkit-user-select': 'none',
31726 '-moz-user-select': '-moz-none',
31727 'user-select': 'none',
31728 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31729 'outline-style': 'none'
31730 };
31731
31732 if (ms()) {
31733 styleMap['-ms-touch-action'] = 'none';
31734 styleMap['touch-action'] = 'none';
31735 }
31736
31737 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
31738 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31739
31740 r.data.contexts[i] = canvas.getContext('2d');
31741 Object.keys(styleMap).forEach(function (k) {
31742 canvas.style[k] = styleMap[k];
31743 });
31744 canvas.style.position = 'absolute';
31745 canvas.setAttribute('data-id', 'layer' + i);
31746 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
31747 r.data.canvasContainer.appendChild(canvas);
31748 r.data.canvasNeedsRedraw[i] = false;
31749 }
31750
31751 r.data.topCanvas = r.data.canvases[0];
31752 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
31753 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
31754 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
31755
31756 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
31757 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31758
31759 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31760 r.data.bufferCanvases[i].style.position = 'absolute';
31761 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31762 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31763 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31764 }
31765
31766 r.pathsEnabled = true;
31767 var emptyBb = makeBoundingBox();
31768
31769 var getBoxCenter = function getBoxCenter(bb) {
31770 return {
31771 x: (bb.x1 + bb.x2) / 2,
31772 y: (bb.y1 + bb.y2) / 2
31773 };
31774 };
31775
31776 var getCenterOffset = function getCenterOffset(bb) {
31777 return {
31778 x: -bb.w / 2,
31779 y: -bb.h / 2
31780 };
31781 };
31782
31783 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31784 var _p = ele[0]._private;
31785 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31786 return !same;
31787 };
31788
31789 var getStyleKey = function getStyleKey(ele) {
31790 return ele[0]._private.nodeKey;
31791 };
31792
31793 var getLabelKey = function getLabelKey(ele) {
31794 return ele[0]._private.labelStyleKey;
31795 };
31796
31797 var getSourceLabelKey = function getSourceLabelKey(ele) {
31798 return ele[0]._private.sourceLabelStyleKey;
31799 };
31800
31801 var getTargetLabelKey = function getTargetLabelKey(ele) {
31802 return ele[0]._private.targetLabelStyleKey;
31803 };
31804
31805 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31806 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31807 };
31808
31809 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31810 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31811 };
31812
31813 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31814 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31815 };
31816
31817 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31818 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31819 };
31820
31821 var getElementBox = function getElementBox(ele) {
31822 ele.boundingBox();
31823 return ele[0]._private.bodyBounds;
31824 };
31825
31826 var getLabelBox = function getLabelBox(ele) {
31827 ele.boundingBox();
31828 return ele[0]._private.labelBounds.main || emptyBb;
31829 };
31830
31831 var getSourceLabelBox = function getSourceLabelBox(ele) {
31832 ele.boundingBox();
31833 return ele[0]._private.labelBounds.source || emptyBb;
31834 };
31835
31836 var getTargetLabelBox = function getTargetLabelBox(ele) {
31837 ele.boundingBox();
31838 return ele[0]._private.labelBounds.target || emptyBb;
31839 };
31840
31841 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31842 return scaledLabelShown;
31843 };
31844
31845 var getElementRotationPoint = function getElementRotationPoint(ele) {
31846 return getBoxCenter(getElementBox(ele));
31847 };
31848
31849 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31850 var pre = prefix ? prefix + '-' : '';
31851 return {
31852 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31853 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31854 };
31855 };
31856
31857 var getRsPt = function getRsPt(ele, x, y) {
31858 var rs = ele[0]._private.rscratch;
31859 return {
31860 x: rs[x],
31861 y: rs[y]
31862 };
31863 };
31864
31865 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31866 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31867 };
31868
31869 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31870 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31871 };
31872
31873 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31874 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31875 };
31876
31877 var getElementRotationOffset = function getElementRotationOffset(ele) {
31878 return getCenterOffset(getElementBox(ele));
31879 };
31880
31881 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31882 return getCenterOffset(getSourceLabelBox(ele));
31883 };
31884
31885 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31886 return getCenterOffset(getTargetLabelBox(ele));
31887 };
31888
31889 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31890 var bb = getLabelBox(ele);
31891 var p = getCenterOffset(getLabelBox(ele));
31892
31893 if (ele.isNode()) {
31894 switch (ele.pstyle('text-halign').value) {
31895 case 'left':
31896 p.x = -bb.w;
31897 break;
31898
31899 case 'right':
31900 p.x = 0;
31901 break;
31902 }
31903
31904 switch (ele.pstyle('text-valign').value) {
31905 case 'top':
31906 p.y = -bb.h;
31907 break;
31908
31909 case 'bottom':
31910 p.y = 0;
31911 break;
31912 }
31913 }
31914
31915 return p;
31916 };
31917
31918 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31919 getKey: getStyleKey,
31920 doesEleInvalidateKey: backgroundTimestampHasChanged,
31921 drawElement: drawElement,
31922 getBoundingBox: getElementBox,
31923 getRotationPoint: getElementRotationPoint,
31924 getRotationOffset: getElementRotationOffset,
31925 allowEdgeTxrCaching: false,
31926 allowParentTxrCaching: false
31927 });
31928 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31929 getKey: getLabelKey,
31930 drawElement: drawLabel,
31931 getBoundingBox: getLabelBox,
31932 getRotationPoint: getLabelRotationPoint,
31933 getRotationOffset: getLabelRotationOffset,
31934 isVisible: isLabelVisibleAtScale
31935 });
31936 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31937 getKey: getSourceLabelKey,
31938 drawElement: drawSourceLabel,
31939 getBoundingBox: getSourceLabelBox,
31940 getRotationPoint: getSourceLabelRotationPoint,
31941 getRotationOffset: getSourceLabelRotationOffset,
31942 isVisible: isLabelVisibleAtScale
31943 });
31944 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31945 getKey: getTargetLabelKey,
31946 drawElement: drawTargetLabel,
31947 getBoundingBox: getTargetLabelBox,
31948 getRotationPoint: getTargetLabelRotationPoint,
31949 getRotationOffset: getTargetLabelRotationOffset,
31950 isVisible: isLabelVisibleAtScale
31951 });
31952 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31953 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31954 // each cache should check for sub-key diff to see that the update affects that cache particularly
31955 eleTxrCache.invalidateElements(eles);
31956 lblTxrCache.invalidateElements(eles);
31957 slbTxrCache.invalidateElements(eles);
31958 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31959
31960 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31961
31962 for (var _i = 0; _i < eles.length; _i++) {
31963 var _p = eles[_i]._private;
31964 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31965 }
31966 });
31967
31968 var refineInLayers = function refineInLayers(reqs) {
31969 for (var i = 0; i < reqs.length; i++) {
31970 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31971 }
31972 };
31973
31974 eleTxrCache.onDequeue(refineInLayers);
31975 lblTxrCache.onDequeue(refineInLayers);
31976 slbTxrCache.onDequeue(refineInLayers);
31977 tlbTxrCache.onDequeue(refineInLayers);
31978 }
31979
31980 CRp$a.redrawHint = function (group, bool) {
31981 var r = this;
31982
31983 switch (group) {
31984 case 'eles':
31985 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31986 break;
31987
31988 case 'drag':
31989 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31990 break;
31991
31992 case 'select':
31993 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31994 break;
31995 }
31996 }; // whether to use Path2D caching for drawing
31997
31998
31999 var pathsImpld = typeof Path2D !== 'undefined';
32000
32001 CRp$a.path2dEnabled = function (on) {
32002 if (on === undefined) {
32003 return this.pathsEnabled;
32004 }
32005
32006 this.pathsEnabled = on ? true : false;
32007 };
32008
32009 CRp$a.usePaths = function () {
32010 return pathsImpld && this.pathsEnabled;
32011 };
32012
32013 CRp$a.setImgSmoothing = function (context, bool) {
32014 if (context.imageSmoothingEnabled != null) {
32015 context.imageSmoothingEnabled = bool;
32016 } else {
32017 context.webkitImageSmoothingEnabled = bool;
32018 context.mozImageSmoothingEnabled = bool;
32019 context.msImageSmoothingEnabled = bool;
32020 }
32021 };
32022
32023 CRp$a.getImgSmoothing = function (context) {
32024 if (context.imageSmoothingEnabled != null) {
32025 return context.imageSmoothingEnabled;
32026 } else {
32027 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
32028 }
32029 };
32030
32031 CRp$a.makeOffscreenCanvas = function (width, height) {
32032 var canvas;
32033
32034 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
32035 canvas = new OffscreenCanvas(width, height);
32036 } else {
32037 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
32038
32039 canvas.width = width;
32040 canvas.height = height;
32041 }
32042
32043 return canvas;
32044 };
32045
32046 [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
32047 extend(CRp$a, props);
32048 });
32049
32050 var renderer = [{
32051 name: 'null',
32052 impl: NullRenderer
32053 }, {
32054 name: 'base',
32055 impl: BR
32056 }, {
32057 name: 'canvas',
32058 impl: CR
32059 }];
32060
32061 var incExts = [{
32062 type: 'layout',
32063 extensions: layout
32064 }, {
32065 type: 'renderer',
32066 extensions: renderer
32067 }];
32068
32069 var extensions = {}; // registered modules for extensions, indexed by name
32070
32071 var modules = {};
32072
32073 function setExtension(type, name, registrant) {
32074 var ext = registrant;
32075
32076 var overrideErr = function overrideErr(field) {
32077 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
32078 };
32079
32080 if (type === 'core') {
32081 if (Core.prototype[name]) {
32082 return overrideErr(name);
32083 } else {
32084 Core.prototype[name] = registrant;
32085 }
32086 } else if (type === 'collection') {
32087 if (Collection.prototype[name]) {
32088 return overrideErr(name);
32089 } else {
32090 Collection.prototype[name] = registrant;
32091 }
32092 } else if (type === 'layout') {
32093 // fill in missing layout functions in the prototype
32094 var Layout = function Layout(options) {
32095 this.options = options;
32096 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
32097
32098 if (!plainObject(this._private)) {
32099 this._private = {};
32100 }
32101
32102 this._private.cy = options.cy;
32103 this._private.listeners = [];
32104 this.createEmitter();
32105 };
32106
32107 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
32108 var optLayoutFns = [];
32109
32110 for (var i = 0; i < optLayoutFns.length; i++) {
32111 var fnName = optLayoutFns[i];
32112
32113 layoutProto[fnName] = layoutProto[fnName] || function () {
32114 return this;
32115 };
32116 } // either .start() or .run() is defined, so autogen the other
32117
32118
32119 if (layoutProto.start && !layoutProto.run) {
32120 layoutProto.run = function () {
32121 this.start();
32122 return this;
32123 };
32124 } else if (!layoutProto.start && layoutProto.run) {
32125 layoutProto.start = function () {
32126 this.run();
32127 return this;
32128 };
32129 }
32130
32131 var regStop = registrant.prototype.stop;
32132
32133 layoutProto.stop = function () {
32134 var opts = this.options;
32135
32136 if (opts && opts.animate) {
32137 var anis = this.animations;
32138
32139 if (anis) {
32140 for (var _i = 0; _i < anis.length; _i++) {
32141 anis[_i].stop();
32142 }
32143 }
32144 }
32145
32146 if (regStop) {
32147 regStop.call(this);
32148 } else {
32149 this.emit('layoutstop');
32150 }
32151
32152 return this;
32153 };
32154
32155 if (!layoutProto.destroy) {
32156 layoutProto.destroy = function () {
32157 return this;
32158 };
32159 }
32160
32161 layoutProto.cy = function () {
32162 return this._private.cy;
32163 };
32164
32165 var getCy = function getCy(layout) {
32166 return layout._private.cy;
32167 };
32168
32169 var emitterOpts = {
32170 addEventFields: function addEventFields(layout, evt) {
32171 evt.layout = layout;
32172 evt.cy = getCy(layout);
32173 evt.target = layout;
32174 },
32175 bubble: function bubble() {
32176 return true;
32177 },
32178 parent: function parent(layout) {
32179 return getCy(layout);
32180 }
32181 };
32182 extend(layoutProto, {
32183 createEmitter: function createEmitter() {
32184 this._private.emitter = new Emitter(emitterOpts, this);
32185 return this;
32186 },
32187 emitter: function emitter() {
32188 return this._private.emitter;
32189 },
32190 on: function on(evt, cb) {
32191 this.emitter().on(evt, cb);
32192 return this;
32193 },
32194 one: function one(evt, cb) {
32195 this.emitter().one(evt, cb);
32196 return this;
32197 },
32198 once: function once(evt, cb) {
32199 this.emitter().one(evt, cb);
32200 return this;
32201 },
32202 removeListener: function removeListener(evt, cb) {
32203 this.emitter().removeListener(evt, cb);
32204 return this;
32205 },
32206 removeAllListeners: function removeAllListeners() {
32207 this.emitter().removeAllListeners();
32208 return this;
32209 },
32210 emit: function emit(evt, params) {
32211 this.emitter().emit(evt, params);
32212 return this;
32213 }
32214 });
32215 define$3.eventAliasesOn(layoutProto);
32216 ext = Layout; // replace with our wrapped layout
32217 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
32218 // user registered renderers inherit from base
32219 var BaseRenderer = getExtension('renderer', 'base');
32220 var bProto = BaseRenderer.prototype;
32221 var RegistrantRenderer = registrant;
32222 var rProto = registrant.prototype;
32223
32224 var Renderer = function Renderer() {
32225 BaseRenderer.apply(this, arguments);
32226 RegistrantRenderer.apply(this, arguments);
32227 };
32228
32229 var proto = Renderer.prototype;
32230
32231 for (var pName in bProto) {
32232 var pVal = bProto[pName];
32233 var existsInR = rProto[pName] != null;
32234
32235 if (existsInR) {
32236 return overrideErr(pName);
32237 }
32238
32239 proto[pName] = pVal; // take impl from base
32240 }
32241
32242 for (var _pName in rProto) {
32243 proto[_pName] = rProto[_pName]; // take impl from registrant
32244 }
32245
32246 bProto.clientFunctions.forEach(function (name) {
32247 proto[name] = proto[name] || function () {
32248 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
32249 };
32250 });
32251 ext = Renderer;
32252 }
32253
32254 return setMap({
32255 map: extensions,
32256 keys: [type, name],
32257 value: ext
32258 });
32259 }
32260
32261 function getExtension(type, name) {
32262 return getMap({
32263 map: extensions,
32264 keys: [type, name]
32265 });
32266 }
32267
32268 function setModule(type, name, moduleType, moduleName, registrant) {
32269 return setMap({
32270 map: modules,
32271 keys: [type, name, moduleType, moduleName],
32272 value: registrant
32273 });
32274 }
32275
32276 function getModule(type, name, moduleType, moduleName) {
32277 return getMap({
32278 map: modules,
32279 keys: [type, name, moduleType, moduleName]
32280 });
32281 }
32282
32283 var extension = function extension() {
32284 // e.g. extension('renderer', 'svg')
32285 if (arguments.length === 2) {
32286 return getExtension.apply(null, arguments);
32287 } // e.g. extension('renderer', 'svg', { ... })
32288 else if (arguments.length === 3) {
32289 return setExtension.apply(null, arguments);
32290 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
32291 else if (arguments.length === 4) {
32292 return getModule.apply(null, arguments);
32293 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
32294 else if (arguments.length === 5) {
32295 return setModule.apply(null, arguments);
32296 } else {
32297 error('Invalid extension access syntax');
32298 }
32299 }; // allows a core instance to access extensions internally
32300
32301
32302 Core.prototype.extension = extension; // included extensions
32303
32304 incExts.forEach(function (group) {
32305 group.extensions.forEach(function (ext) {
32306 setExtension(group.type, ext.name, ext.impl);
32307 });
32308 });
32309
32310 // (useful for init)
32311
32312 var Stylesheet = function Stylesheet() {
32313 if (!(this instanceof Stylesheet)) {
32314 return new Stylesheet();
32315 }
32316
32317 this.length = 0;
32318 };
32319
32320 var sheetfn = Stylesheet.prototype;
32321
32322 sheetfn.instanceString = function () {
32323 return 'stylesheet';
32324 }; // just store the selector to be parsed later
32325
32326
32327 sheetfn.selector = function (selector) {
32328 var i = this.length++;
32329 this[i] = {
32330 selector: selector,
32331 properties: []
32332 };
32333 return this; // chaining
32334 }; // just store the property to be parsed later
32335
32336
32337 sheetfn.css = function (name, value) {
32338 var i = this.length - 1;
32339
32340 if (string(name)) {
32341 this[i].properties.push({
32342 name: name,
32343 value: value
32344 });
32345 } else if (plainObject(name)) {
32346 var map = name;
32347 var propNames = Object.keys(map);
32348
32349 for (var j = 0; j < propNames.length; j++) {
32350 var key = propNames[j];
32351 var mapVal = map[key];
32352
32353 if (mapVal == null) {
32354 continue;
32355 }
32356
32357 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
32358
32359 if (prop == null) {
32360 continue;
32361 }
32362
32363 var _name = prop.name;
32364 var _value = mapVal;
32365 this[i].properties.push({
32366 name: _name,
32367 value: _value
32368 });
32369 }
32370 }
32371
32372 return this; // chaining
32373 };
32374
32375 sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
32376
32377 sheetfn.generateStyle = function (cy) {
32378 var style = new Style(cy);
32379 return this.appendToStyle(style);
32380 }; // append a dummy stylesheet object on a real style object
32381
32382
32383 sheetfn.appendToStyle = function (style) {
32384 for (var i = 0; i < this.length; i++) {
32385 var context = this[i];
32386 var selector = context.selector;
32387 var props = context.properties;
32388 style.selector(selector); // apply selector
32389
32390 for (var j = 0; j < props.length; j++) {
32391 var prop = props[j];
32392 style.css(prop.name, prop.value); // apply property
32393 }
32394 }
32395
32396 return style;
32397 };
32398
32399 var version = "3.19.1";
32400
32401 var cytoscape = function cytoscape(options) {
32402 // if no options specified, use default
32403 if (options === undefined) {
32404 options = {};
32405 } // create instance
32406
32407
32408 if (plainObject(options)) {
32409 return new Core(options);
32410 } // allow for registration of extensions
32411 else if (string(options)) {
32412 return extension.apply(extension, arguments);
32413 }
32414 }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
32415
32416
32417 cytoscape.use = function (ext) {
32418 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
32419
32420 args.unshift(cytoscape); // cytoscape is first arg to ext
32421
32422 ext.apply(null, args);
32423 return this;
32424 };
32425
32426 cytoscape.warnings = function (bool) {
32427 return warnings(bool);
32428 }; // replaced by build system
32429
32430
32431 cytoscape.version = version; // expose public apis (mostly for extensions)
32432
32433 cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
32434
32435 return cytoscape;
32436
32437})));