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 r.hoverData.dragged = true;
25522 } // Needs reproject due to pan changing viewport
25523
25524
25525 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
25526 } else if (select[4] == 1 && (down == null || down.pannable())) {
25527 if (isOverThresholdDrag) {
25528 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
25529 goIntoBoxMode();
25530 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
25531 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
25532
25533 if (allowPassthrough) {
25534 r.hoverData.dragging = true;
25535 r.hoverData.justStartedPan = true;
25536 select[4] = 0;
25537 r.data.bgActivePosistion = array2point(mdownPos);
25538 r.redrawHint('select', true);
25539 r.redraw();
25540 }
25541 }
25542
25543 if (down && down.pannable() && down.active()) {
25544 down.unactivate();
25545 }
25546 }
25547 } else {
25548 if (down && down.pannable() && down.active()) {
25549 down.unactivate();
25550 }
25551
25552 if ((!down || !down.grabbed()) && near != last) {
25553 if (last) {
25554 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
25555 x: pos[0],
25556 y: pos[1]
25557 });
25558 }
25559
25560 if (near) {
25561 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
25562 x: pos[0],
25563 y: pos[1]
25564 });
25565 }
25566
25567 r.hoverData.last = near;
25568 }
25569
25570 if (down) {
25571 if (isOverThresholdDrag) {
25572 // then we can take action
25573 if (cy.boxSelectionEnabled() && multSelKeyDown) {
25574 // then selection overrides
25575 if (down && down.grabbed()) {
25576 freeDraggedElements(draggedElements);
25577 down.emit('freeon');
25578 draggedElements.emit('free');
25579
25580 if (r.dragData.didDrag) {
25581 down.emit('dragfreeon');
25582 draggedElements.emit('dragfree');
25583 }
25584 }
25585
25586 goIntoBoxMode();
25587 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
25588 // drag node
25589 var justStartedDrag = !r.dragData.didDrag;
25590
25591 if (justStartedDrag) {
25592 r.redrawHint('eles', true);
25593 }
25594
25595 r.dragData.didDrag = true; // indicate that we actually did drag the node
25596
25597 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
25598
25599 if (!r.hoverData.draggingEles) {
25600 addNodesToDrag(draggedElements, {
25601 inDragLayer: true
25602 });
25603 }
25604
25605 var totalShift = {
25606 x: 0,
25607 y: 0
25608 };
25609
25610 if (number(disp[0]) && number(disp[1])) {
25611 totalShift.x += disp[0];
25612 totalShift.y += disp[1];
25613
25614 if (justStartedDrag) {
25615 var dragDelta = r.hoverData.dragDelta;
25616
25617 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25618 totalShift.x += dragDelta[0];
25619 totalShift.y += dragDelta[1];
25620 }
25621 }
25622 }
25623
25624 for (var i = 0; i < draggedElements.length; i++) {
25625 var dEle = draggedElements[i];
25626
25627 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
25628 toTrigger.push(dEle);
25629 }
25630 }
25631
25632 r.hoverData.draggingEles = true;
25633 toTrigger.silentShift(totalShift).emit('position drag');
25634 r.redrawHint('drag', true);
25635 r.redraw();
25636 }
25637 } else {
25638 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25639 updateDragDelta();
25640 }
25641 } // prevent the dragging from triggering text selection on the page
25642
25643
25644 preventDefault = true;
25645 }
25646
25647 select[2] = pos[0];
25648 select[3] = pos[1];
25649
25650 if (preventDefault) {
25651 if (e.stopPropagation) e.stopPropagation();
25652 if (e.preventDefault) e.preventDefault();
25653 return false;
25654 }
25655 }, false);
25656 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
25657 // eslint-disable-line no-undef
25658 var capture = r.hoverData.capture;
25659
25660 if (!capture) {
25661 return;
25662 }
25663
25664 r.hoverData.capture = false;
25665 var cy = r.cy;
25666 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25667 var select = r.selection;
25668 var near = r.findNearestElement(pos[0], pos[1], true, false);
25669 var draggedElements = r.dragData.possibleDragElements;
25670 var down = r.hoverData.down;
25671 var multSelKeyDown = isMultSelKeyDown(e);
25672
25673 if (r.data.bgActivePosistion) {
25674 r.redrawHint('select', true);
25675 r.redraw();
25676 }
25677
25678 r.hoverData.tapholdCancelled = true;
25679 r.data.bgActivePosistion = undefined; // not active bg now
25680
25681 if (down) {
25682 down.unactivate();
25683 }
25684
25685 if (r.hoverData.which === 3) {
25686 var cxtEvt = {
25687 originalEvent: e,
25688 type: 'cxttapend',
25689 position: {
25690 x: pos[0],
25691 y: pos[1]
25692 }
25693 };
25694
25695 if (down) {
25696 down.emit(cxtEvt);
25697 } else {
25698 cy.emit(cxtEvt);
25699 }
25700
25701 if (!r.hoverData.cxtDragged) {
25702 var cxtTap = {
25703 originalEvent: e,
25704 type: 'cxttap',
25705 position: {
25706 x: pos[0],
25707 y: pos[1]
25708 }
25709 };
25710
25711 if (down) {
25712 down.emit(cxtTap);
25713 } else {
25714 cy.emit(cxtTap);
25715 }
25716 }
25717
25718 r.hoverData.cxtDragged = false;
25719 r.hoverData.which = null;
25720 } else if (r.hoverData.which === 1) {
25721 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25722 x: pos[0],
25723 y: pos[1]
25724 });
25725
25726 if (!r.dragData.didDrag // didn't move a node around
25727 && !r.hoverData.dragged // didn't pan
25728 && !r.hoverData.selecting // not box selection
25729 && !r.hoverData.isOverThresholdDrag // didn't move too much
25730 ) {
25731 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
25732 x: pos[0],
25733 y: pos[1]
25734 });
25735 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25736
25737
25738 if (down == null && // not mousedown on node
25739 !r.dragData.didDrag // didn't move the node around
25740 && !r.hoverData.selecting // not box selection
25741 && !r.hoverData.dragged // didn't pan
25742 && !isMultSelKeyDown(e)) {
25743 cy.$(isSelected).unselect(['tapunselect']);
25744
25745 if (draggedElements.length > 0) {
25746 r.redrawHint('eles', true);
25747 }
25748
25749 r.dragData.possibleDragElements = draggedElements = cy.collection();
25750 } // Single selection
25751
25752
25753 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25754 if (near != null && near._private.selectable) {
25755 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25756 if (near.selected()) {
25757 near.unselect(['tapunselect']);
25758 } else {
25759 near.select(['tapselect']);
25760 }
25761 } else {
25762 if (!multSelKeyDown) {
25763 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25764 near.select(['tapselect']);
25765 }
25766 }
25767
25768 r.redrawHint('eles', true);
25769 }
25770 }
25771
25772 if (r.hoverData.selecting) {
25773 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25774 r.redrawHint('select', true);
25775
25776 if (box.length > 0) {
25777 r.redrawHint('eles', true);
25778 }
25779
25780 cy.emit({
25781 type: 'boxend',
25782 originalEvent: e,
25783 position: {
25784 x: pos[0],
25785 y: pos[1]
25786 }
25787 });
25788
25789 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25790 return ele.selectable() && !ele.selected();
25791 };
25792
25793 if (cy.selectionType() === 'additive') {
25794 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25795 } else {
25796 if (!multSelKeyDown) {
25797 cy.$(isSelected).unmerge(box).unselect();
25798 }
25799
25800 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25801 } // always need redraw in case eles unselectable
25802
25803
25804 r.redraw();
25805 } // Cancel drag pan
25806
25807
25808 if (r.hoverData.dragging) {
25809 r.hoverData.dragging = false;
25810 r.redrawHint('select', true);
25811 r.redrawHint('eles', true);
25812 r.redraw();
25813 }
25814
25815 if (!select[4]) {
25816 r.redrawHint('drag', true);
25817 r.redrawHint('eles', true);
25818 var downWasGrabbed = down && down.grabbed();
25819 freeDraggedElements(draggedElements);
25820
25821 if (downWasGrabbed) {
25822 down.emit('freeon');
25823 draggedElements.emit('free');
25824
25825 if (r.dragData.didDrag) {
25826 down.emit('dragfreeon');
25827 draggedElements.emit('dragfree');
25828 }
25829 }
25830 }
25831 } // else not right mouse
25832
25833
25834 select[4] = 0;
25835 r.hoverData.down = null;
25836 r.hoverData.cxtStarted = false;
25837 r.hoverData.draggingEles = false;
25838 r.hoverData.selecting = false;
25839 r.hoverData.isOverThresholdDrag = false;
25840 r.dragData.didDrag = false;
25841 r.hoverData.dragged = false;
25842 r.hoverData.dragDelta = [];
25843 r.hoverData.mdownPos = null;
25844 r.hoverData.mdownGPos = null;
25845 }, false);
25846
25847 var wheelHandler = function wheelHandler(e) {
25848 if (r.scrollingPage) {
25849 return;
25850 } // while scrolling, ignore wheel-to-zoom
25851
25852
25853 var cy = r.cy;
25854 var zoom = cy.zoom();
25855 var pan = cy.pan();
25856 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25857 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25858
25859 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25860 // if pan dragging or cxt dragging, wheel movements make no zoom
25861 e.preventDefault();
25862 return;
25863 }
25864
25865 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25866 e.preventDefault();
25867 r.data.wheelZooming = true;
25868 clearTimeout(r.data.wheelTimeout);
25869 r.data.wheelTimeout = setTimeout(function () {
25870 r.data.wheelZooming = false;
25871 r.redrawHint('eles', true);
25872 r.redraw();
25873 }, 150);
25874 var diff;
25875
25876 if (e.deltaY != null) {
25877 diff = e.deltaY / -250;
25878 } else if (e.wheelDeltaY != null) {
25879 diff = e.wheelDeltaY / 1000;
25880 } else {
25881 diff = e.wheelDelta / 1000;
25882 }
25883
25884 diff = diff * r.wheelSensitivity;
25885 var needsWheelFix = e.deltaMode === 1;
25886
25887 if (needsWheelFix) {
25888 // fixes slow wheel events on ff/linux and ff/windows
25889 diff *= 33;
25890 }
25891
25892 var newZoom = cy.zoom() * Math.pow(10, diff);
25893
25894 if (e.type === 'gesturechange') {
25895 newZoom = r.gestureStartZoom * e.scale;
25896 }
25897
25898 cy.zoom({
25899 level: newZoom,
25900 renderedPosition: {
25901 x: rpos[0],
25902 y: rpos[1]
25903 }
25904 });
25905 }
25906 }; // Functions to help with whether mouse wheel should trigger zooming
25907 // --
25908
25909
25910 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25911 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25912 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25913 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25914
25915 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25916 // eslint-disable-line no-unused-vars
25917 r.scrollingPage = true;
25918 clearTimeout(r.scrollingPageTimeout);
25919 r.scrollingPageTimeout = setTimeout(function () {
25920 r.scrollingPage = false;
25921 }, 250);
25922 }, true); // desktop safari pinch to zoom start
25923
25924 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25925 r.gestureStartZoom = r.cy.zoom();
25926
25927 if (!r.hasTouchStarted) {
25928 // don't affect touch devices like iphone
25929 e.preventDefault();
25930 }
25931 }, true);
25932 r.registerBinding(r.container, 'gesturechange', function (e) {
25933 if (!r.hasTouchStarted) {
25934 // don't affect touch devices like iphone
25935 wheelHandler(e);
25936 }
25937 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25938 // Handle mouseout on Cytoscape container
25939
25940 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25941 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25942 r.cy.emit({
25943 originalEvent: e,
25944 type: 'mouseout',
25945 position: {
25946 x: pos[0],
25947 y: pos[1]
25948 }
25949 });
25950 }, false);
25951 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25952 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25953 r.cy.emit({
25954 originalEvent: e,
25955 type: 'mouseover',
25956 position: {
25957 x: pos[0],
25958 y: pos[1]
25959 }
25960 });
25961 }, false);
25962 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25963
25964 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25965
25966 var center1, modelCenter1; // center point on start pinch to zoom
25967
25968 var offsetLeft, offsetTop;
25969 var containerWidth, containerHeight;
25970 var twoFingersStartInside;
25971
25972 var distance = function distance(x1, y1, x2, y2) {
25973 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25974 };
25975
25976 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25977 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25978 };
25979
25980 var touchstartHandler;
25981 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25982 r.hasTouchStarted = true;
25983
25984 if (!eventInContainer(e)) {
25985 return;
25986 }
25987
25988 blurActiveDomElement();
25989 r.touchData.capture = true;
25990 r.data.bgActivePosistion = undefined;
25991 var cy = r.cy;
25992 var now = r.touchData.now;
25993 var earlier = r.touchData.earlier;
25994
25995 if (e.touches[0]) {
25996 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25997 now[0] = pos[0];
25998 now[1] = pos[1];
25999 }
26000
26001 if (e.touches[1]) {
26002 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26003 now[2] = pos[0];
26004 now[3] = pos[1];
26005 }
26006
26007 if (e.touches[2]) {
26008 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26009 now[4] = pos[0];
26010 now[5] = pos[1];
26011 } // record starting points for pinch-to-zoom
26012
26013
26014 if (e.touches[1]) {
26015 r.touchData.singleTouchMoved = true;
26016 freeDraggedElements(r.dragData.touchDragEles);
26017 var offsets = r.findContainerClientCoords();
26018 offsetLeft = offsets[0];
26019 offsetTop = offsets[1];
26020 containerWidth = offsets[2];
26021 containerHeight = offsets[3];
26022 f1x1 = e.touches[0].clientX - offsetLeft;
26023 f1y1 = e.touches[0].clientY - offsetTop;
26024 f2x1 = e.touches[1].clientX - offsetLeft;
26025 f2y1 = e.touches[1].clientY - offsetTop;
26026 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
26027 var pan = cy.pan();
26028 var zoom = cy.zoom();
26029 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
26030 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
26031 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
26032 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
26033
26034 var cxtDistThreshold = 200;
26035 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
26036
26037 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
26038 var near1 = r.findNearestElement(now[0], now[1], true, true);
26039 var near2 = r.findNearestElement(now[2], now[3], true, true);
26040
26041 if (near1 && near1.isNode()) {
26042 near1.activate().emit({
26043 originalEvent: e,
26044 type: 'cxttapstart',
26045 position: {
26046 x: now[0],
26047 y: now[1]
26048 }
26049 });
26050 r.touchData.start = near1;
26051 } else if (near2 && near2.isNode()) {
26052 near2.activate().emit({
26053 originalEvent: e,
26054 type: 'cxttapstart',
26055 position: {
26056 x: now[0],
26057 y: now[1]
26058 }
26059 });
26060 r.touchData.start = near2;
26061 } else {
26062 cy.emit({
26063 originalEvent: e,
26064 type: 'cxttapstart',
26065 position: {
26066 x: now[0],
26067 y: now[1]
26068 }
26069 });
26070 }
26071
26072 if (r.touchData.start) {
26073 r.touchData.start._private.grabbed = false;
26074 }
26075
26076 r.touchData.cxt = true;
26077 r.touchData.cxtDragged = false;
26078 r.data.bgActivePosistion = undefined;
26079 r.redraw();
26080 return;
26081 }
26082 }
26083
26084 if (e.touches[2]) {
26085 // ignore
26086 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
26087 if (cy.boxSelectionEnabled()) {
26088 e.preventDefault();
26089 }
26090 } else if (e.touches[1]) ; else if (e.touches[0]) {
26091 var nears = r.findNearestElements(now[0], now[1], true, true);
26092 var near = nears[0];
26093
26094 if (near != null) {
26095 near.activate();
26096 r.touchData.start = near;
26097 r.touchData.starts = nears;
26098
26099 if (r.nodeIsGrabbable(near)) {
26100 var draggedEles = r.dragData.touchDragEles = cy.collection();
26101 var selectedNodes = null;
26102 r.redrawHint('eles', true);
26103 r.redrawHint('drag', true);
26104
26105 if (near.selected()) {
26106 // reset drag elements, since near will be added again
26107 selectedNodes = cy.$(function (ele) {
26108 return ele.selected() && r.nodeIsGrabbable(ele);
26109 });
26110 addNodesToDrag(selectedNodes, {
26111 addToList: draggedEles
26112 });
26113 } else {
26114 addNodeToDrag(near, {
26115 addToList: draggedEles
26116 });
26117 }
26118
26119 setGrabTarget(near);
26120
26121 var makeEvent = function makeEvent(type) {
26122 return {
26123 originalEvent: e,
26124 type: type,
26125 position: {
26126 x: now[0],
26127 y: now[1]
26128 }
26129 };
26130 };
26131
26132 near.emit(makeEvent('grabon'));
26133
26134 if (selectedNodes) {
26135 selectedNodes.forEach(function (n) {
26136 n.emit(makeEvent('grab'));
26137 });
26138 } else {
26139 near.emit(makeEvent('grab'));
26140 }
26141 }
26142 }
26143
26144 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
26145 x: now[0],
26146 y: now[1]
26147 });
26148
26149 if (near == null) {
26150 r.data.bgActivePosistion = {
26151 x: pos[0],
26152 y: pos[1]
26153 };
26154 r.redrawHint('select', true);
26155 r.redraw();
26156 } // Tap, taphold
26157 // -----
26158
26159
26160 r.touchData.singleTouchMoved = false;
26161 r.touchData.singleTouchStartTime = +new Date();
26162 clearTimeout(r.touchData.tapholdTimeout);
26163 r.touchData.tapholdTimeout = setTimeout(function () {
26164 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
26165 && !r.touchData.selecting // box selection shouldn't allow taphold through
26166 ) {
26167 triggerEvents(r.touchData.start, ['taphold'], e, {
26168 x: now[0],
26169 y: now[1]
26170 });
26171 }
26172 }, r.tapholdDuration);
26173 }
26174
26175 if (e.touches.length >= 1) {
26176 var sPos = r.touchData.startPosition = [];
26177
26178 for (var i = 0; i < now.length; i++) {
26179 sPos[i] = earlier[i] = now[i];
26180 }
26181
26182 var touch0 = e.touches[0];
26183 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
26184 }
26185 }, false);
26186 var touchmoveHandler;
26187 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
26188 // eslint-disable-line no-undef
26189 var capture = r.touchData.capture;
26190
26191 if (!capture && !eventInContainer(e)) {
26192 return;
26193 }
26194
26195 var select = r.selection;
26196 var cy = r.cy;
26197 var now = r.touchData.now;
26198 var earlier = r.touchData.earlier;
26199 var zoom = cy.zoom();
26200
26201 if (e.touches[0]) {
26202 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26203 now[0] = pos[0];
26204 now[1] = pos[1];
26205 }
26206
26207 if (e.touches[1]) {
26208 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26209 now[2] = pos[0];
26210 now[3] = pos[1];
26211 }
26212
26213 if (e.touches[2]) {
26214 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26215 now[4] = pos[0];
26216 now[5] = pos[1];
26217 }
26218
26219 var startGPos = r.touchData.startGPosition;
26220 var isOverThresholdDrag;
26221
26222 if (capture && e.touches[0] && startGPos) {
26223 var disp = [];
26224
26225 for (var j = 0; j < now.length; j++) {
26226 disp[j] = now[j] - earlier[j];
26227 }
26228
26229 var dx = e.touches[0].clientX - startGPos[0];
26230 var dx2 = dx * dx;
26231 var dy = e.touches[0].clientY - startGPos[1];
26232 var dy2 = dy * dy;
26233 var dist2 = dx2 + dy2;
26234 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
26235 } // context swipe cancelling
26236
26237
26238 if (capture && r.touchData.cxt) {
26239 e.preventDefault();
26240 var f1x2 = e.touches[0].clientX - offsetLeft,
26241 f1y2 = e.touches[0].clientY - offsetTop;
26242 var f2x2 = e.touches[1].clientX - offsetLeft,
26243 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
26244
26245 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
26246 var factorSq = distance2Sq / distance1Sq;
26247 var distThreshold = 150;
26248 var distThresholdSq = distThreshold * distThreshold;
26249 var factorThreshold = 1.5;
26250 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
26251
26252 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
26253 r.touchData.cxt = false;
26254 r.data.bgActivePosistion = undefined;
26255 r.redrawHint('select', true);
26256 var cxtEvt = {
26257 originalEvent: e,
26258 type: 'cxttapend',
26259 position: {
26260 x: now[0],
26261 y: now[1]
26262 }
26263 };
26264
26265 if (r.touchData.start) {
26266 r.touchData.start.unactivate().emit(cxtEvt);
26267 r.touchData.start = null;
26268 } else {
26269 cy.emit(cxtEvt);
26270 }
26271 }
26272 } // context swipe
26273
26274
26275 if (capture && r.touchData.cxt) {
26276 var cxtEvt = {
26277 originalEvent: e,
26278 type: 'cxtdrag',
26279 position: {
26280 x: now[0],
26281 y: now[1]
26282 }
26283 };
26284 r.data.bgActivePosistion = undefined;
26285 r.redrawHint('select', true);
26286
26287 if (r.touchData.start) {
26288 r.touchData.start.emit(cxtEvt);
26289 } else {
26290 cy.emit(cxtEvt);
26291 }
26292
26293 if (r.touchData.start) {
26294 r.touchData.start._private.grabbed = false;
26295 }
26296
26297 r.touchData.cxtDragged = true;
26298 var near = r.findNearestElement(now[0], now[1], true, true);
26299
26300 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
26301 if (r.touchData.cxtOver) {
26302 r.touchData.cxtOver.emit({
26303 originalEvent: e,
26304 type: 'cxtdragout',
26305 position: {
26306 x: now[0],
26307 y: now[1]
26308 }
26309 });
26310 }
26311
26312 r.touchData.cxtOver = near;
26313
26314 if (near) {
26315 near.emit({
26316 originalEvent: e,
26317 type: 'cxtdragover',
26318 position: {
26319 x: now[0],
26320 y: now[1]
26321 }
26322 });
26323 }
26324 } // box selection
26325
26326 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
26327 e.preventDefault();
26328 r.data.bgActivePosistion = undefined;
26329 this.lastThreeTouch = +new Date();
26330
26331 if (!r.touchData.selecting) {
26332 cy.emit({
26333 originalEvent: e,
26334 type: 'boxstart',
26335 position: {
26336 x: now[0],
26337 y: now[1]
26338 }
26339 });
26340 }
26341
26342 r.touchData.selecting = true;
26343 r.touchData.didSelect = true;
26344 select[4] = 1;
26345
26346 if (!select || select.length === 0 || select[0] === undefined) {
26347 select[0] = (now[0] + now[2] + now[4]) / 3;
26348 select[1] = (now[1] + now[3] + now[5]) / 3;
26349 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
26350 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
26351 } else {
26352 select[2] = (now[0] + now[2] + now[4]) / 3;
26353 select[3] = (now[1] + now[3] + now[5]) / 3;
26354 }
26355
26356 r.redrawHint('select', true);
26357 r.redraw(); // pinch to zoom
26358 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
26359 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
26360 // two fingers => pinch to zoom
26361 e.preventDefault();
26362 r.data.bgActivePosistion = undefined;
26363 r.redrawHint('select', true);
26364 var draggedEles = r.dragData.touchDragEles;
26365
26366 if (draggedEles) {
26367 r.redrawHint('drag', true);
26368
26369 for (var i = 0; i < draggedEles.length; i++) {
26370 var de_p = draggedEles[i]._private;
26371 de_p.grabbed = false;
26372 de_p.rscratch.inDragLayer = false;
26373 }
26374 }
26375
26376 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
26377
26378 var f1x2 = e.touches[0].clientX - offsetLeft,
26379 f1y2 = e.touches[0].clientY - offsetTop;
26380 var f2x2 = e.touches[1].clientX - offsetLeft,
26381 f2y2 = e.touches[1].clientY - offsetTop;
26382 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
26383 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
26384
26385 var factor = distance2 / distance1;
26386
26387 if (twoFingersStartInside) {
26388 // delta finger1
26389 var df1x = f1x2 - f1x1;
26390 var df1y = f1y2 - f1y1; // delta finger 2
26391
26392 var df2x = f2x2 - f2x1;
26393 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
26394 // i.e. so pinching cancels out and moving together pans
26395
26396 var tx = (df1x + df2x) / 2;
26397 var ty = (df1y + df2y) / 2; // now calculate the zoom
26398
26399 var zoom1 = cy.zoom();
26400 var zoom2 = zoom1 * factor;
26401 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
26402
26403 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
26404 var ctry = modelCenter1[1] * zoom1 + pan1.y;
26405 var pan2 = {
26406 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
26407 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
26408 }; // remove dragged eles
26409
26410 if (_start && _start.active()) {
26411 var draggedEles = r.dragData.touchDragEles;
26412 freeDraggedElements(draggedEles);
26413 r.redrawHint('drag', true);
26414 r.redrawHint('eles', true);
26415
26416 _start.unactivate().emit('freeon');
26417
26418 draggedEles.emit('free');
26419
26420 if (r.dragData.didDrag) {
26421 _start.emit('dragfreeon');
26422
26423 draggedEles.emit('dragfree');
26424 }
26425 }
26426
26427 cy.viewport({
26428 zoom: zoom2,
26429 pan: pan2,
26430 cancelOnFailedZoom: true
26431 });
26432 distance1 = distance2;
26433 f1x1 = f1x2;
26434 f1y1 = f1y2;
26435 f2x1 = f2x2;
26436 f2y1 = f2y2;
26437 r.pinching = true;
26438 } // Re-project
26439
26440
26441 if (e.touches[0]) {
26442 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26443 now[0] = pos[0];
26444 now[1] = pos[1];
26445 }
26446
26447 if (e.touches[1]) {
26448 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26449 now[2] = pos[0];
26450 now[3] = pos[1];
26451 }
26452
26453 if (e.touches[2]) {
26454 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26455 now[4] = pos[0];
26456 now[5] = pos[1];
26457 }
26458 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
26459 ) {
26460 var start = r.touchData.start;
26461 var last = r.touchData.last;
26462 var near;
26463
26464 if (!r.hoverData.draggingEles && !r.swipePanning) {
26465 near = r.findNearestElement(now[0], now[1], true, true);
26466 }
26467
26468 if (capture && start != null) {
26469 e.preventDefault();
26470 } // dragging nodes
26471
26472
26473 if (capture && start != null && r.nodeIsDraggable(start)) {
26474 if (isOverThresholdDrag) {
26475 // then dragging can happen
26476 var draggedEles = r.dragData.touchDragEles;
26477 var justStartedDrag = !r.dragData.didDrag;
26478
26479 if (justStartedDrag) {
26480 addNodesToDrag(draggedEles, {
26481 inDragLayer: true
26482 });
26483 }
26484
26485 r.dragData.didDrag = true;
26486 var totalShift = {
26487 x: 0,
26488 y: 0
26489 };
26490
26491 if (number(disp[0]) && number(disp[1])) {
26492 totalShift.x += disp[0];
26493 totalShift.y += disp[1];
26494
26495 if (justStartedDrag) {
26496 r.redrawHint('eles', true);
26497 var dragDelta = r.touchData.dragDelta;
26498
26499 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
26500 totalShift.x += dragDelta[0];
26501 totalShift.y += dragDelta[1];
26502 }
26503 }
26504 }
26505
26506 r.hoverData.draggingEles = true;
26507 draggedEles.silentShift(totalShift).emit('position drag');
26508 r.redrawHint('drag', true);
26509
26510 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
26511 r.redrawHint('eles', true);
26512 }
26513
26514 r.redraw();
26515 } else {
26516 // otherise keep track of drag delta for later
26517 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
26518
26519 if (dragDelta.length === 0) {
26520 dragDelta.push(disp[0]);
26521 dragDelta.push(disp[1]);
26522 } else {
26523 dragDelta[0] += disp[0];
26524 dragDelta[1] += disp[1];
26525 }
26526 }
26527 } // touchmove
26528
26529
26530 {
26531 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
26532 x: now[0],
26533 y: now[1]
26534 });
26535
26536 if ((!start || !start.grabbed()) && near != last) {
26537 if (last) {
26538 last.emit({
26539 originalEvent: e,
26540 type: 'tapdragout',
26541 position: {
26542 x: now[0],
26543 y: now[1]
26544 }
26545 });
26546 }
26547
26548 if (near) {
26549 near.emit({
26550 originalEvent: e,
26551 type: 'tapdragover',
26552 position: {
26553 x: now[0],
26554 y: now[1]
26555 }
26556 });
26557 }
26558 }
26559
26560 r.touchData.last = near;
26561 } // check to cancel taphold
26562
26563 if (capture) {
26564 for (var i = 0; i < now.length; i++) {
26565 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
26566 r.touchData.singleTouchMoved = true;
26567 }
26568 }
26569 } // panning
26570
26571
26572 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
26573 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
26574
26575 if (allowPassthrough) {
26576 e.preventDefault();
26577
26578 if (!r.data.bgActivePosistion) {
26579 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
26580 }
26581
26582 if (r.swipePanning) {
26583 cy.panBy({
26584 x: disp[0] * zoom,
26585 y: disp[1] * zoom
26586 });
26587 } else if (isOverThresholdDrag) {
26588 r.swipePanning = true;
26589 cy.panBy({
26590 x: dx * zoom,
26591 y: dy * zoom
26592 });
26593
26594 if (start) {
26595 start.unactivate();
26596 r.redrawHint('select', true);
26597 r.touchData.start = null;
26598 }
26599 }
26600 } // Re-project
26601
26602
26603 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26604 now[0] = pos[0];
26605 now[1] = pos[1];
26606 }
26607 }
26608
26609 for (var j = 0; j < now.length; j++) {
26610 earlier[j] = now[j];
26611 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
26612
26613
26614 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
26615 r.data.bgActivePosistion = undefined;
26616 r.redrawHint('select', true);
26617 r.redraw();
26618 }
26619 }, false);
26620 var touchcancelHandler;
26621 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
26622 // eslint-disable-line no-unused-vars
26623 var start = r.touchData.start;
26624 r.touchData.capture = false;
26625
26626 if (start) {
26627 start.unactivate();
26628 }
26629 });
26630 var touchendHandler;
26631 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
26632 // eslint-disable-line no-unused-vars
26633 var start = r.touchData.start;
26634 var capture = r.touchData.capture;
26635
26636 if (capture) {
26637 if (e.touches.length === 0) {
26638 r.touchData.capture = false;
26639 }
26640
26641 e.preventDefault();
26642 } else {
26643 return;
26644 }
26645
26646 var select = r.selection;
26647 r.swipePanning = false;
26648 r.hoverData.draggingEles = false;
26649 var cy = r.cy;
26650 var zoom = cy.zoom();
26651 var now = r.touchData.now;
26652 var earlier = r.touchData.earlier;
26653
26654 if (e.touches[0]) {
26655 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
26656 now[0] = pos[0];
26657 now[1] = pos[1];
26658 }
26659
26660 if (e.touches[1]) {
26661 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
26662 now[2] = pos[0];
26663 now[3] = pos[1];
26664 }
26665
26666 if (e.touches[2]) {
26667 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
26668 now[4] = pos[0];
26669 now[5] = pos[1];
26670 }
26671
26672 if (start) {
26673 start.unactivate();
26674 }
26675
26676 var ctxTapend;
26677
26678 if (r.touchData.cxt) {
26679 ctxTapend = {
26680 originalEvent: e,
26681 type: 'cxttapend',
26682 position: {
26683 x: now[0],
26684 y: now[1]
26685 }
26686 };
26687
26688 if (start) {
26689 start.emit(ctxTapend);
26690 } else {
26691 cy.emit(ctxTapend);
26692 }
26693
26694 if (!r.touchData.cxtDragged) {
26695 var ctxTap = {
26696 originalEvent: e,
26697 type: 'cxttap',
26698 position: {
26699 x: now[0],
26700 y: now[1]
26701 }
26702 };
26703
26704 if (start) {
26705 start.emit(ctxTap);
26706 } else {
26707 cy.emit(ctxTap);
26708 }
26709 }
26710
26711 if (r.touchData.start) {
26712 r.touchData.start._private.grabbed = false;
26713 }
26714
26715 r.touchData.cxt = false;
26716 r.touchData.start = null;
26717 r.redraw();
26718 return;
26719 } // no more box selection if we don't have three fingers
26720
26721
26722 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26723 r.touchData.selecting = false;
26724 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26725 select[0] = undefined;
26726 select[1] = undefined;
26727 select[2] = undefined;
26728 select[3] = undefined;
26729 select[4] = 0;
26730 r.redrawHint('select', true);
26731 cy.emit({
26732 type: 'boxend',
26733 originalEvent: e,
26734 position: {
26735 x: now[0],
26736 y: now[1]
26737 }
26738 });
26739
26740 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26741 return ele.selectable() && !ele.selected();
26742 };
26743
26744 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26745
26746 if (box.nonempty()) {
26747 r.redrawHint('eles', true);
26748 }
26749
26750 r.redraw();
26751 }
26752
26753 if (start != null) {
26754 start.unactivate();
26755 }
26756
26757 if (e.touches[2]) {
26758 r.data.bgActivePosistion = undefined;
26759 r.redrawHint('select', true);
26760 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26761 r.data.bgActivePosistion = undefined;
26762 r.redrawHint('select', true);
26763 var draggedEles = r.dragData.touchDragEles;
26764
26765 if (start != null) {
26766 var startWasGrabbed = start._private.grabbed;
26767 freeDraggedElements(draggedEles);
26768 r.redrawHint('drag', true);
26769 r.redrawHint('eles', true);
26770
26771 if (startWasGrabbed) {
26772 start.emit('freeon');
26773 draggedEles.emit('free');
26774
26775 if (r.dragData.didDrag) {
26776 start.emit('dragfreeon');
26777 draggedEles.emit('dragfree');
26778 }
26779 }
26780
26781 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26782 x: now[0],
26783 y: now[1]
26784 });
26785 start.unactivate();
26786 r.touchData.start = null;
26787 } else {
26788 var near = r.findNearestElement(now[0], now[1], true, true);
26789 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26790 x: now[0],
26791 y: now[1]
26792 });
26793 }
26794
26795 var dx = r.touchData.startPosition[0] - now[0];
26796 var dx2 = dx * dx;
26797 var dy = r.touchData.startPosition[1] - now[1];
26798 var dy2 = dy * dy;
26799 var dist2 = dx2 + dy2;
26800 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26801
26802 if (!r.touchData.singleTouchMoved) {
26803 if (!start) {
26804 cy.$(':selected').unselect(['tapunselect']);
26805 }
26806
26807 triggerEvents(start, ['tap', 'vclick'], e, {
26808 x: now[0],
26809 y: now[1]
26810 });
26811 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26812
26813
26814 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26815 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26816 ) {
26817 if (cy.selectionType() === 'single') {
26818 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26819 start.select(['tapselect']);
26820 } else {
26821 if (start.selected()) {
26822 start.unselect(['tapunselect']);
26823 } else {
26824 start.select(['tapselect']);
26825 }
26826 }
26827
26828 r.redrawHint('eles', true);
26829 }
26830
26831 r.touchData.singleTouchMoved = true;
26832 }
26833
26834 for (var j = 0; j < now.length; j++) {
26835 earlier[j] = now[j];
26836 }
26837
26838 r.dragData.didDrag = false; // reset for next touchstart
26839
26840 if (e.touches.length === 0) {
26841 r.touchData.dragDelta = [];
26842 r.touchData.startPosition = null;
26843 r.touchData.startGPosition = null;
26844 r.touchData.didSelect = false;
26845 }
26846
26847 if (e.touches.length < 2) {
26848 if (e.touches.length === 1) {
26849 // the old start global pos'n may not be the same finger that remains
26850 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26851 }
26852
26853 r.pinching = false;
26854 r.redrawHint('eles', true);
26855 r.redraw();
26856 } //r.redraw();
26857
26858 }, false); // fallback compatibility layer for ms pointer events
26859
26860 if (typeof TouchEvent === 'undefined') {
26861 var pointers = [];
26862
26863 var makeTouch = function makeTouch(e) {
26864 return {
26865 clientX: e.clientX,
26866 clientY: e.clientY,
26867 force: 1,
26868 identifier: e.pointerId,
26869 pageX: e.pageX,
26870 pageY: e.pageY,
26871 radiusX: e.width / 2,
26872 radiusY: e.height / 2,
26873 screenX: e.screenX,
26874 screenY: e.screenY,
26875 target: e.target
26876 };
26877 };
26878
26879 var makePointer = function makePointer(e) {
26880 return {
26881 event: e,
26882 touch: makeTouch(e)
26883 };
26884 };
26885
26886 var addPointer = function addPointer(e) {
26887 pointers.push(makePointer(e));
26888 };
26889
26890 var removePointer = function removePointer(e) {
26891 for (var i = 0; i < pointers.length; i++) {
26892 var p = pointers[i];
26893
26894 if (p.event.pointerId === e.pointerId) {
26895 pointers.splice(i, 1);
26896 return;
26897 }
26898 }
26899 };
26900
26901 var updatePointer = function updatePointer(e) {
26902 var p = pointers.filter(function (p) {
26903 return p.event.pointerId === e.pointerId;
26904 })[0];
26905 p.event = e;
26906 p.touch = makeTouch(e);
26907 };
26908
26909 var addTouchesToEvent = function addTouchesToEvent(e) {
26910 e.touches = pointers.map(function (p) {
26911 return p.touch;
26912 });
26913 };
26914
26915 var pointerIsMouse = function pointerIsMouse(e) {
26916 return e.pointerType === 'mouse' || e.pointerType === 4;
26917 };
26918
26919 r.registerBinding(r.container, 'pointerdown', function (e) {
26920 if (pointerIsMouse(e)) {
26921 return;
26922 } // mouse already handled
26923
26924
26925 e.preventDefault();
26926 addPointer(e);
26927 addTouchesToEvent(e);
26928 touchstartHandler(e);
26929 });
26930 r.registerBinding(r.container, 'pointerup', function (e) {
26931 if (pointerIsMouse(e)) {
26932 return;
26933 } // mouse already handled
26934
26935
26936 removePointer(e);
26937 addTouchesToEvent(e);
26938 touchendHandler(e);
26939 });
26940 r.registerBinding(r.container, 'pointercancel', function (e) {
26941 if (pointerIsMouse(e)) {
26942 return;
26943 } // mouse already handled
26944
26945
26946 removePointer(e);
26947 addTouchesToEvent(e);
26948 touchcancelHandler(e);
26949 });
26950 r.registerBinding(r.container, 'pointermove', function (e) {
26951 if (pointerIsMouse(e)) {
26952 return;
26953 } // mouse already handled
26954
26955
26956 e.preventDefault();
26957 updatePointer(e);
26958 addTouchesToEvent(e);
26959 touchmoveHandler(e);
26960 });
26961 }
26962 };
26963
26964 var BRp$d = {};
26965
26966 BRp$d.generatePolygon = function (name, points) {
26967 return this.nodeShapes[name] = {
26968 renderer: this,
26969 name: name,
26970 points: points,
26971 draw: function draw(context, centerX, centerY, width, height) {
26972 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26973 },
26974 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26975 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26976 },
26977 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26978 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26979 }
26980 };
26981 };
26982
26983 BRp$d.generateEllipse = function () {
26984 return this.nodeShapes['ellipse'] = {
26985 renderer: this,
26986 name: 'ellipse',
26987 draw: function draw(context, centerX, centerY, width, height) {
26988 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26989 },
26990 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26991 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26992 },
26993 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26994 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26995 }
26996 };
26997 };
26998
26999 BRp$d.generateRoundPolygon = function (name, points) {
27000 // Pre-compute control points
27001 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
27002 // the unit vectors.
27003 // For simplicity the layout will be:
27004 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
27005 var allPoints = new Array(points.length * 2);
27006
27007 for (var i = 0; i < points.length / 2; i++) {
27008 var sourceIndex = i * 2;
27009 var destIndex = void 0;
27010
27011 if (i < points.length / 2 - 1) {
27012 destIndex = (i + 1) * 2;
27013 } else {
27014 destIndex = 0;
27015 }
27016
27017 allPoints[i * 4] = points[sourceIndex];
27018 allPoints[i * 4 + 1] = points[sourceIndex + 1];
27019 var xDest = points[destIndex] - points[sourceIndex];
27020 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
27021 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
27022 allPoints[i * 4 + 2] = xDest / norm;
27023 allPoints[i * 4 + 3] = yDest / norm;
27024 }
27025
27026 return this.nodeShapes[name] = {
27027 renderer: this,
27028 name: name,
27029 points: allPoints,
27030 draw: function draw(context, centerX, centerY, width, height) {
27031 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
27032 },
27033 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27034 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
27035 },
27036 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27037 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
27038 }
27039 };
27040 };
27041
27042 BRp$d.generateRoundRectangle = function () {
27043 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
27044 renderer: this,
27045 name: 'round-rectangle',
27046 points: generateUnitNgonPointsFitToSquare(4, 0),
27047 draw: function draw(context, centerX, centerY, width, height) {
27048 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27049 },
27050 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27051 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27052 },
27053 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27054 var cornerRadius = getRoundRectangleRadius(width, height);
27055 var diam = cornerRadius * 2; // Check hBox
27056
27057 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27058 return true;
27059 } // Check vBox
27060
27061
27062 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27063 return true;
27064 } // Check top left quarter circle
27065
27066
27067 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
27068 return true;
27069 } // Check top right 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 bottom 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 left quarter circle
27080
27081
27082 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27083 return true;
27084 }
27085
27086 return false;
27087 }
27088 };
27089 };
27090
27091 BRp$d.generateCutRectangle = function () {
27092 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
27093 renderer: this,
27094 name: 'cut-rectangle',
27095 cornerLength: getCutRectangleCornerLength(),
27096 points: generateUnitNgonPointsFitToSquare(4, 0),
27097 draw: function draw(context, centerX, centerY, width, height) {
27098 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27099 },
27100 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
27101 var cl = this.cornerLength;
27102 var hh = height / 2;
27103 var hw = width / 2;
27104 var xBegin = centerX - hw;
27105 var xEnd = centerX + hw;
27106 var yBegin = centerY - hh;
27107 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
27108
27109 return {
27110 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
27111 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
27112 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
27113 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
27114 };
27115 },
27116 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27117 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27118 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
27119 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27120 },
27121 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27122 // Check hBox
27123 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
27124 return true;
27125 } // Check vBox
27126
27127
27128 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
27129 return true;
27130 }
27131
27132 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
27133 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
27134 }
27135 };
27136 };
27137
27138 BRp$d.generateBarrel = function () {
27139 return this.nodeShapes['barrel'] = {
27140 renderer: this,
27141 name: 'barrel',
27142 points: generateUnitNgonPointsFitToSquare(4, 0),
27143 draw: function draw(context, centerX, centerY, width, height) {
27144 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27145 },
27146 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27147 // use two fixed t values for the bezier curve approximation
27148 var t0 = 0.15;
27149 var t1 = 0.5;
27150 var t2 = 0.85;
27151 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
27152
27153 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
27154 // approximate curve pts based on the two t values
27155 var m0 = qbezierPtAt({
27156 x: pts[0],
27157 y: pts[1]
27158 }, {
27159 x: pts[2],
27160 y: pts[3]
27161 }, {
27162 x: pts[4],
27163 y: pts[5]
27164 }, t0);
27165 var m1 = qbezierPtAt({
27166 x: pts[0],
27167 y: pts[1]
27168 }, {
27169 x: pts[2],
27170 y: pts[3]
27171 }, {
27172 x: pts[4],
27173 y: pts[5]
27174 }, t1);
27175 var m2 = qbezierPtAt({
27176 x: pts[0],
27177 y: pts[1]
27178 }, {
27179 x: pts[2],
27180 y: pts[3]
27181 }, {
27182 x: pts[4],
27183 y: pts[5]
27184 }, t2);
27185 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
27186 };
27187
27188 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
27189 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
27190 },
27191 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
27192 var hh = height / 2;
27193 var hw = width / 2;
27194 var xBegin = centerX - hw;
27195 var xEnd = centerX + hw;
27196 var yBegin = centerY - hh;
27197 var yEnd = centerY + hh;
27198 var curveConstants = getBarrelCurveConstants(width, height);
27199 var hOffset = curveConstants.heightOffset;
27200 var wOffset = curveConstants.widthOffset;
27201 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
27202
27203 var pts = {
27204 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
27205 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
27206 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
27207 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
27208 };
27209 pts.topLeft.isTop = true;
27210 pts.topRight.isTop = true;
27211 pts.bottomLeft.isBottom = true;
27212 pts.bottomRight.isBottom = true;
27213 return pts;
27214 },
27215 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27216 var curveConstants = getBarrelCurveConstants(width, height);
27217 var hOffset = curveConstants.heightOffset;
27218 var wOffset = curveConstants.widthOffset; // Check hBox
27219
27220 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
27221 return true;
27222 } // Check vBox
27223
27224
27225 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
27226 return true;
27227 }
27228
27229 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
27230
27231 var getCurveT = function getCurveT(x, y, curvePts) {
27232 var x0 = curvePts[4];
27233 var x1 = curvePts[2];
27234 var x2 = curvePts[0];
27235 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
27236
27237 var y2 = curvePts[1];
27238 var xMin = Math.min(x0, x2);
27239 var xMax = Math.max(x0, x2);
27240 var yMin = Math.min(y0, y2);
27241 var yMax = Math.max(y0, y2);
27242
27243 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
27244 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
27245 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
27246 var validRoots = roots.filter(function (r) {
27247 return 0 <= r && r <= 1;
27248 });
27249
27250 if (validRoots.length > 0) {
27251 return validRoots[0];
27252 }
27253 }
27254
27255 return null;
27256 };
27257
27258 var curveRegions = Object.keys(barrelCurvePts);
27259
27260 for (var i = 0; i < curveRegions.length; i++) {
27261 var corner = curveRegions[i];
27262 var cornerPts = barrelCurvePts[corner];
27263 var t = getCurveT(x, y, cornerPts);
27264
27265 if (t == null) {
27266 continue;
27267 }
27268
27269 var y0 = cornerPts[5];
27270 var y1 = cornerPts[3];
27271 var y2 = cornerPts[1];
27272 var bezY = qbezierAt(y0, y1, y2, t);
27273
27274 if (cornerPts.isTop && bezY <= y) {
27275 return true;
27276 }
27277
27278 if (cornerPts.isBottom && y <= bezY) {
27279 return true;
27280 }
27281 }
27282
27283 return false;
27284 }
27285 };
27286 };
27287
27288 BRp$d.generateBottomRoundrectangle = function () {
27289 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
27290 renderer: this,
27291 name: 'bottom-round-rectangle',
27292 points: generateUnitNgonPointsFitToSquare(4, 0),
27293 draw: function draw(context, centerX, centerY, width, height) {
27294 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
27295 },
27296 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
27297 var topStartX = nodeX - (width / 2 + padding);
27298 var topStartY = nodeY - (height / 2 + padding);
27299 var topEndY = topStartY;
27300 var topEndX = nodeX + (width / 2 + padding);
27301 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
27302
27303 if (topIntersections.length > 0) {
27304 return topIntersections;
27305 }
27306
27307 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
27308 },
27309 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
27310 var cornerRadius = getRoundRectangleRadius(width, height);
27311 var diam = 2 * cornerRadius; // Check hBox
27312
27313 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
27314 return true;
27315 } // Check vBox
27316
27317
27318 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
27319 return true;
27320 } // check non-rounded top side
27321
27322
27323 var outerWidth = width / 2 + 2 * padding;
27324 var outerHeight = height / 2 + 2 * padding;
27325 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
27326
27327 if (pointInsidePolygonPoints(x, y, points)) {
27328 return true;
27329 } // Check bottom right quarter circle
27330
27331
27332 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27333 return true;
27334 } // Check bottom left quarter circle
27335
27336
27337 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
27338 return true;
27339 }
27340
27341 return false;
27342 }
27343 };
27344 };
27345
27346 BRp$d.registerNodeShapes = function () {
27347 var nodeShapes = this.nodeShapes = {};
27348 var renderer = this;
27349 this.generateEllipse();
27350 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
27351 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
27352 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
27353 nodeShapes['square'] = nodeShapes['rectangle'];
27354 this.generateRoundRectangle();
27355 this.generateCutRectangle();
27356 this.generateBarrel();
27357 this.generateBottomRoundrectangle();
27358 {
27359 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
27360 this.generatePolygon('diamond', diamondPoints);
27361 this.generateRoundPolygon('round-diamond', diamondPoints);
27362 }
27363 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27364 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
27365 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27366 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
27367 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27368 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
27369 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
27370 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
27371 var star5Points = new Array(20);
27372 {
27373 var outerPoints = generateUnitNgonPoints(5, 0);
27374 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
27375
27376 var innerRadius = 0.5 * (3 - Math.sqrt(5));
27377 innerRadius *= 1.57;
27378
27379 for (var i = 0; i < innerPoints.length / 2; i++) {
27380 innerPoints[i * 2] *= innerRadius;
27381 innerPoints[i * 2 + 1] *= innerRadius;
27382 }
27383
27384 for (var i = 0; i < 20 / 4; i++) {
27385 star5Points[i * 4] = outerPoints[i * 2];
27386 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
27387 star5Points[i * 4 + 2] = innerPoints[i * 2];
27388 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
27389 }
27390 }
27391 star5Points = fitPolygonToSquare(star5Points);
27392 this.generatePolygon('star', star5Points);
27393 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
27394 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
27395 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]);
27396 {
27397 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
27398 this.generatePolygon('tag', tagPoints);
27399 this.generateRoundPolygon('round-tag', tagPoints);
27400 }
27401
27402 nodeShapes.makePolygon = function (points) {
27403 // use caching on user-specified polygons so they are as fast as native shapes
27404 var key = points.join('$');
27405 var name = 'polygon-' + key;
27406 var shape;
27407
27408 if (shape = this[name]) {
27409 // got cached shape
27410 return shape;
27411 } // create and cache new shape
27412
27413
27414 return renderer.generatePolygon(name, points);
27415 };
27416 };
27417
27418 var BRp$e = {};
27419
27420 BRp$e.timeToRender = function () {
27421 return this.redrawTotalTime / this.redrawCount;
27422 };
27423
27424 BRp$e.redraw = function (options) {
27425 options = options || staticEmptyObject();
27426 var r = this;
27427
27428 if (r.averageRedrawTime === undefined) {
27429 r.averageRedrawTime = 0;
27430 }
27431
27432 if (r.lastRedrawTime === undefined) {
27433 r.lastRedrawTime = 0;
27434 }
27435
27436 if (r.lastDrawTime === undefined) {
27437 r.lastDrawTime = 0;
27438 }
27439
27440 r.requestedFrame = true;
27441 r.renderOptions = options;
27442 };
27443
27444 BRp$e.beforeRender = function (fn, priority) {
27445 // the renderer can't add tick callbacks when destroyed
27446 if (this.destroyed) {
27447 return;
27448 }
27449
27450 if (priority == null) {
27451 error('Priority is not optional for beforeRender');
27452 }
27453
27454 var cbs = this.beforeRenderCallbacks;
27455 cbs.push({
27456 fn: fn,
27457 priority: priority
27458 }); // higher priority callbacks executed first
27459
27460 cbs.sort(function (a, b) {
27461 return b.priority - a.priority;
27462 });
27463 };
27464
27465 var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
27466 var cbs = r.beforeRenderCallbacks;
27467
27468 for (var i = 0; i < cbs.length; i++) {
27469 cbs[i].fn(willDraw, startTime);
27470 }
27471 };
27472
27473 BRp$e.startRenderLoop = function () {
27474 var r = this;
27475 var cy = r.cy;
27476
27477 if (r.renderLoopStarted) {
27478 return;
27479 } else {
27480 r.renderLoopStarted = true;
27481 }
27482
27483 var renderFn = function renderFn(requestTime) {
27484 if (r.destroyed) {
27485 return;
27486 }
27487
27488 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
27489 beforeRenderCallbacks(r, true, requestTime);
27490 var startTime = performanceNow();
27491 r.render(r.renderOptions);
27492 var endTime = r.lastDrawTime = performanceNow();
27493
27494 if (r.averageRedrawTime === undefined) {
27495 r.averageRedrawTime = endTime - startTime;
27496 }
27497
27498 if (r.redrawCount === undefined) {
27499 r.redrawCount = 0;
27500 }
27501
27502 r.redrawCount++;
27503
27504 if (r.redrawTotalTime === undefined) {
27505 r.redrawTotalTime = 0;
27506 }
27507
27508 var duration = endTime - startTime;
27509 r.redrawTotalTime += duration;
27510 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
27511
27512 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
27513 r.requestedFrame = false;
27514 } else {
27515 beforeRenderCallbacks(r, false, requestTime);
27516 }
27517
27518 r.skipFrame = false;
27519 requestAnimationFrame(renderFn);
27520 };
27521
27522 requestAnimationFrame(renderFn);
27523 };
27524
27525 var BaseRenderer = function BaseRenderer(options) {
27526 this.init(options);
27527 };
27528
27529 var BR = BaseRenderer;
27530 var BRp$f = BR.prototype;
27531 BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
27532
27533 BRp$f.init = function (options) {
27534 var r = this;
27535 r.options = options;
27536 r.cy = options.cy;
27537 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
27538
27539 if (window$1) {
27540 var document = window$1.document;
27541 var head = document.head;
27542 var stylesheetId = '__________cytoscape_stylesheet';
27543 var className = '__________cytoscape_container';
27544 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
27545
27546 if (ctr.className.indexOf(className) < 0) {
27547 ctr.className = (ctr.className || '') + ' ' + className;
27548 }
27549
27550 if (!stylesheetAlreadyExists) {
27551 var stylesheet = document.createElement('style');
27552 stylesheet.id = stylesheetId;
27553 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
27554 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
27555 }
27556
27557 var computedStyle = window$1.getComputedStyle(ctr);
27558 var position = computedStyle.getPropertyValue('position');
27559
27560 if (position === 'static') {
27561 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
27562 }
27563 }
27564
27565 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
27566
27567 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
27568
27569 r.hoverData = {
27570 down: null,
27571 last: null,
27572 downTime: null,
27573 triggerMode: null,
27574 dragging: false,
27575 initialPan: [null, null],
27576 capture: false
27577 };
27578 r.dragData = {
27579 possibleDragElements: []
27580 };
27581 r.touchData = {
27582 start: null,
27583 capture: false,
27584 // These 3 fields related to tap, taphold events
27585 startPosition: [null, null, null, null, null, null],
27586 singleTouchStartTime: null,
27587 singleTouchMoved: true,
27588 now: [null, null, null, null, null, null],
27589 earlier: [null, null, null, null, null, null]
27590 };
27591 r.redraws = 0;
27592 r.showFps = options.showFps;
27593 r.debug = options.debug;
27594 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
27595 r.textureOnViewport = options.textureOnViewport;
27596 r.wheelSensitivity = options.wheelSensitivity;
27597 r.motionBlurEnabled = options.motionBlur; // on by default
27598
27599 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
27600 r.motionBlur = options.motionBlur; // for initial kick off
27601
27602 r.motionBlurOpacity = options.motionBlurOpacity;
27603 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
27604 r.motionBlurPxRatio = 1;
27605 r.mbPxRBlurry = 1; //0.8;
27606
27607 r.minMbLowQualFrames = 4;
27608 r.fullQualityMb = false;
27609 r.clearedForMotionBlur = [];
27610 r.desktopTapThreshold = options.desktopTapThreshold;
27611 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
27612 r.touchTapThreshold = options.touchTapThreshold;
27613 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
27614 r.tapholdDuration = 500;
27615 r.bindings = [];
27616 r.beforeRenderCallbacks = [];
27617 r.beforeRenderPriorities = {
27618 // higher priority execs before lower one
27619 animations: 400,
27620 eleCalcs: 300,
27621 eleTxrDeq: 200,
27622 lyrTxrDeq: 150,
27623 lyrTxrSkip: 100
27624 };
27625 r.registerNodeShapes();
27626 r.registerArrowShapes();
27627 r.registerCalculationListeners();
27628 };
27629
27630 BRp$f.notify = function (eventName, eles) {
27631 var r = this;
27632 var cy = r.cy; // the renderer can't be notified after it's destroyed
27633
27634 if (this.destroyed) {
27635 return;
27636 }
27637
27638 if (eventName === 'init') {
27639 r.load();
27640 return;
27641 }
27642
27643 if (eventName === 'destroy') {
27644 r.destroy();
27645 return;
27646 }
27647
27648 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
27649 r.invalidateCachedZSortedEles();
27650 }
27651
27652 if (eventName === 'viewport') {
27653 r.redrawHint('select', true);
27654 }
27655
27656 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
27657 r.invalidateContainerClientCoordsCache();
27658 r.matchCanvasSize(r.container);
27659 }
27660
27661 r.redrawHint('eles', true);
27662 r.redrawHint('drag', true);
27663 this.startRenderLoop();
27664 this.redraw();
27665 };
27666
27667 BRp$f.destroy = function () {
27668 var r = this;
27669 r.destroyed = true;
27670 r.cy.stopAnimationLoop();
27671
27672 for (var i = 0; i < r.bindings.length; i++) {
27673 var binding = r.bindings[i];
27674 var b = binding;
27675 var tgt = b.target;
27676 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
27677 }
27678
27679 r.bindings = [];
27680 r.beforeRenderCallbacks = [];
27681 r.onUpdateEleCalcsFns = [];
27682
27683 if (r.removeObserver) {
27684 r.removeObserver.disconnect();
27685 }
27686
27687 if (r.styleObserver) {
27688 r.styleObserver.disconnect();
27689 }
27690
27691 if (r.resizeObserver) {
27692 r.resizeObserver.disconnect();
27693 }
27694
27695 if (r.labelCalcDiv) {
27696 try {
27697 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
27698 } catch (e) {// ie10 issue #1014
27699 }
27700 }
27701 };
27702
27703 BRp$f.isHeadless = function () {
27704 return false;
27705 };
27706
27707 [BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
27708 extend(BRp$f, props);
27709 });
27710
27711 var fullFpsTime = 1000 / 60; // assume 60 frames per second
27712
27713 var defs = {
27714 setupDequeueing: function setupDequeueing(opts) {
27715 return function setupDequeueingImpl() {
27716 var self = this;
27717 var r = this.renderer;
27718
27719 if (self.dequeueingSetup) {
27720 return;
27721 } else {
27722 self.dequeueingSetup = true;
27723 }
27724
27725 var queueRedraw = lodash_debounce(function () {
27726 r.redrawHint('eles', true);
27727 r.redrawHint('drag', true);
27728 r.redraw();
27729 }, opts.deqRedrawThreshold);
27730
27731 var dequeue = function dequeue(willDraw, frameStartTime) {
27732 var startTime = performanceNow();
27733 var avgRenderTime = r.averageRedrawTime;
27734 var renderTime = r.lastRedrawTime;
27735 var deqd = [];
27736 var extent = r.cy.extent();
27737 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
27738 // queue won't automatically be flushed before dequeueing starts
27739
27740 if (!willDraw) {
27741 r.flushRenderedStyleQueue();
27742 }
27743
27744 while (true) {
27745 // eslint-disable-line no-constant-condition
27746 var now = performanceNow();
27747 var duration = now - startTime;
27748 var frameDuration = now - frameStartTime;
27749
27750 if (renderTime < fullFpsTime) {
27751 // if we're rendering faster than the ideal fps, then do dequeueing
27752 // during all of the remaining frame time
27753 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27754
27755 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27756 break;
27757 }
27758 } else {
27759 if (willDraw) {
27760 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27761 break;
27762 }
27763 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27764 break;
27765 }
27766 }
27767
27768 var thisDeqd = opts.deq(self, pixelRatio, extent);
27769
27770 if (thisDeqd.length > 0) {
27771 for (var i = 0; i < thisDeqd.length; i++) {
27772 deqd.push(thisDeqd[i]);
27773 }
27774 } else {
27775 break;
27776 }
27777 } // callbacks on dequeue
27778
27779
27780 if (deqd.length > 0) {
27781 opts.onDeqd(self, deqd);
27782
27783 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27784 queueRedraw();
27785 }
27786 }
27787 };
27788
27789 var priority = opts.priority || noop;
27790 r.beforeRender(dequeue, priority(self));
27791 };
27792 }
27793 };
27794
27795 // Uses keys so elements may share the same cache.
27796
27797 var ElementTextureCacheLookup =
27798 /*#__PURE__*/
27799 function () {
27800 function ElementTextureCacheLookup(getKey) {
27801 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27802
27803 _classCallCheck(this, ElementTextureCacheLookup);
27804
27805 this.idsByKey = new Map$1();
27806 this.keyForId = new Map$1();
27807 this.cachesByLvl = new Map$1();
27808 this.lvls = [];
27809 this.getKey = getKey;
27810 this.doesEleInvalidateKey = doesEleInvalidateKey;
27811 }
27812
27813 _createClass(ElementTextureCacheLookup, [{
27814 key: "getIdsFor",
27815 value: function getIdsFor(key) {
27816 if (key == null) {
27817 error("Can not get id list for null key");
27818 }
27819
27820 var idsByKey = this.idsByKey;
27821 var ids = this.idsByKey.get(key);
27822
27823 if (!ids) {
27824 ids = new Set$1();
27825 idsByKey.set(key, ids);
27826 }
27827
27828 return ids;
27829 }
27830 }, {
27831 key: "addIdForKey",
27832 value: function addIdForKey(key, id) {
27833 if (key != null) {
27834 this.getIdsFor(key).add(id);
27835 }
27836 }
27837 }, {
27838 key: "deleteIdForKey",
27839 value: function deleteIdForKey(key, id) {
27840 if (key != null) {
27841 this.getIdsFor(key)["delete"](id);
27842 }
27843 }
27844 }, {
27845 key: "getNumberOfIdsForKey",
27846 value: function getNumberOfIdsForKey(key) {
27847 if (key == null) {
27848 return 0;
27849 } else {
27850 return this.getIdsFor(key).size;
27851 }
27852 }
27853 }, {
27854 key: "updateKeyMappingFor",
27855 value: function updateKeyMappingFor(ele) {
27856 var id = ele.id();
27857 var prevKey = this.keyForId.get(id);
27858 var currKey = this.getKey(ele);
27859 this.deleteIdForKey(prevKey, id);
27860 this.addIdForKey(currKey, id);
27861 this.keyForId.set(id, currKey);
27862 }
27863 }, {
27864 key: "deleteKeyMappingFor",
27865 value: function deleteKeyMappingFor(ele) {
27866 var id = ele.id();
27867 var prevKey = this.keyForId.get(id);
27868 this.deleteIdForKey(prevKey, id);
27869 this.keyForId["delete"](id);
27870 }
27871 }, {
27872 key: "keyHasChangedFor",
27873 value: function keyHasChangedFor(ele) {
27874 var id = ele.id();
27875 var prevKey = this.keyForId.get(id);
27876 var newKey = this.getKey(ele);
27877 return prevKey !== newKey;
27878 }
27879 }, {
27880 key: "isInvalid",
27881 value: function isInvalid(ele) {
27882 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27883 }
27884 }, {
27885 key: "getCachesAt",
27886 value: function getCachesAt(lvl) {
27887 var cachesByLvl = this.cachesByLvl,
27888 lvls = this.lvls;
27889 var caches = cachesByLvl.get(lvl);
27890
27891 if (!caches) {
27892 caches = new Map$1();
27893 cachesByLvl.set(lvl, caches);
27894 lvls.push(lvl);
27895 }
27896
27897 return caches;
27898 }
27899 }, {
27900 key: "getCache",
27901 value: function getCache(key, lvl) {
27902 return this.getCachesAt(lvl).get(key);
27903 }
27904 }, {
27905 key: "get",
27906 value: function get(ele, lvl) {
27907 var key = this.getKey(ele);
27908 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27909
27910 if (cache != null) {
27911 this.updateKeyMappingFor(ele);
27912 }
27913
27914 return cache;
27915 }
27916 }, {
27917 key: "getForCachedKey",
27918 value: function getForCachedKey(ele, lvl) {
27919 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27920
27921 var cache = this.getCache(key, lvl);
27922 return cache;
27923 }
27924 }, {
27925 key: "hasCache",
27926 value: function hasCache(key, lvl) {
27927 return this.getCachesAt(lvl).has(key);
27928 }
27929 }, {
27930 key: "has",
27931 value: function has(ele, lvl) {
27932 var key = this.getKey(ele);
27933 return this.hasCache(key, lvl);
27934 }
27935 }, {
27936 key: "setCache",
27937 value: function setCache(key, lvl, cache) {
27938 cache.key = key;
27939 this.getCachesAt(lvl).set(key, cache);
27940 }
27941 }, {
27942 key: "set",
27943 value: function set(ele, lvl, cache) {
27944 var key = this.getKey(ele);
27945 this.setCache(key, lvl, cache);
27946 this.updateKeyMappingFor(ele);
27947 }
27948 }, {
27949 key: "deleteCache",
27950 value: function deleteCache(key, lvl) {
27951 this.getCachesAt(lvl)["delete"](key);
27952 }
27953 }, {
27954 key: "delete",
27955 value: function _delete(ele, lvl) {
27956 var key = this.getKey(ele);
27957 this.deleteCache(key, lvl);
27958 }
27959 }, {
27960 key: "invalidateKey",
27961 value: function invalidateKey(key) {
27962 var _this = this;
27963
27964 this.lvls.forEach(function (lvl) {
27965 return _this.deleteCache(key, lvl);
27966 });
27967 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27968
27969 }, {
27970 key: "invalidate",
27971 value: function invalidate(ele) {
27972 var id = ele.id();
27973 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27974
27975 this.deleteKeyMappingFor(ele);
27976 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27977
27978 if (entireKeyInvalidated) {
27979 // clear mapping for current key
27980 this.invalidateKey(key);
27981 }
27982
27983 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27984 }
27985 }]);
27986
27987 return ElementTextureCacheLookup;
27988 }();
27989
27990 var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27991
27992 var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27993
27994 var minLvl = -4; // when scaling smaller than that we don't need to re-render
27995
27996 var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27997
27998 var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27999
28000 var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
28001
28002 var defTxrWidth = 1024; // default/minimum texture width
28003
28004 var maxTxrW = 1024; // the maximum width of a texture
28005
28006 var maxTxrH = 1024; // the maximum height of a texture
28007
28008 var minUtility = 0.2; // if usage of texture is less than this, it is retired
28009
28010 var maxFullness = 0.8; // fullness of texture after which queue removal is checked
28011
28012 var maxFullnessChecks = 10; // dequeued after this many checks
28013
28014 var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
28015
28016 var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
28017
28018 var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
28019
28020 var deqFastCost = 0.9; // % of frame time to be used when >60fps
28021
28022 var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
28023
28024 var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
28025
28026 var getTxrReasons = {
28027 dequeue: 'dequeue',
28028 downscale: 'downscale',
28029 highQuality: 'highQuality'
28030 };
28031 var initDefaults = defaults({
28032 getKey: null,
28033 doesEleInvalidateKey: falsify,
28034 drawElement: null,
28035 getBoundingBox: null,
28036 getRotationPoint: null,
28037 getRotationOffset: null,
28038 isVisible: trueify,
28039 allowEdgeTxrCaching: true,
28040 allowParentTxrCaching: true
28041 });
28042
28043 var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
28044 var self = this;
28045 self.renderer = renderer;
28046 self.onDequeues = [];
28047 var opts = initDefaults(initOptions);
28048 extend(self, opts);
28049 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
28050 self.setupDequeueing();
28051 };
28052
28053 var ETCp = ElementTextureCache.prototype;
28054 ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
28055
28056 ETCp.getTextureQueue = function (txrH) {
28057 var self = this;
28058 self.eleImgCaches = self.eleImgCaches || {};
28059 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
28060 }; // the list of usused textures which can be recycled (in use in texture queue)
28061
28062
28063 ETCp.getRetiredTextureQueue = function (txrH) {
28064 var self = this;
28065 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
28066 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
28067 return rtxtrQ;
28068 }; // queue of element draw requests at different scale levels
28069
28070
28071 ETCp.getElementQueue = function () {
28072 var self = this;
28073 var q = self.eleCacheQueue = self.eleCacheQueue || new heap$1(function (a, b) {
28074 return b.reqs - a.reqs;
28075 });
28076 return q;
28077 }; // queue of element draw requests at different scale levels (element id lookup)
28078
28079
28080 ETCp.getElementKeyToQueue = function () {
28081 var self = this;
28082 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
28083 return k2q;
28084 };
28085
28086 ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
28087 var self = this;
28088 var r = this.renderer;
28089 var zoom = r.cy.zoom();
28090 var lookup = this.lookup;
28091
28092 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
28093 return null;
28094 }
28095
28096 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
28097 return null;
28098 }
28099
28100 if (lvl == null) {
28101 lvl = Math.ceil(log2(zoom * pxRatio));
28102 }
28103
28104 if (lvl < minLvl) {
28105 lvl = minLvl;
28106 } else if (zoom >= maxZoom || lvl > maxLvl) {
28107 return null;
28108 }
28109
28110 var scale = Math.pow(2, lvl);
28111 var eleScaledH = bb.h * scale;
28112 var eleScaledW = bb.w * scale;
28113 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
28114
28115 if (!this.isVisible(ele, scaledLabelShown)) {
28116 return null;
28117 }
28118
28119 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
28120
28121 if (eleCache && eleCache.invalidated) {
28122 eleCache.invalidated = false;
28123 eleCache.texture.invalidatedWidth -= eleCache.width;
28124 }
28125
28126 if (eleCache) {
28127 return eleCache;
28128 }
28129
28130 var txrH; // which texture height this ele belongs to
28131
28132 if (eleScaledH <= minTxrH) {
28133 txrH = minTxrH;
28134 } else if (eleScaledH <= txrStepH) {
28135 txrH = txrStepH;
28136 } else {
28137 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
28138 }
28139
28140 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
28141 return null; // caching large elements is not efficient
28142 }
28143
28144 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
28145
28146 var txr = txrQ[txrQ.length - 2];
28147
28148 var addNewTxr = function addNewTxr() {
28149 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
28150 }; // try the last one if there is no second last one
28151
28152
28153 if (!txr) {
28154 txr = txrQ[txrQ.length - 1];
28155 } // if the last one doesn't exist, we need a first one
28156
28157
28158 if (!txr) {
28159 txr = addNewTxr();
28160 } // if there's no room in the current texture, we need a new one
28161
28162
28163 if (txr.width - txr.usedWidth < eleScaledW) {
28164 txr = addNewTxr();
28165 }
28166
28167 var scalableFrom = function scalableFrom(otherCache) {
28168 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
28169 };
28170
28171 var deqing = reason && reason === getTxrReasons.dequeue;
28172 var highQualityReq = reason && reason === getTxrReasons.highQuality;
28173 var downscaleReq = reason && reason === getTxrReasons.downscale;
28174 var higherCache; // the nearest cache with a higher level
28175
28176 for (var l = lvl + 1; l <= maxLvl; l++) {
28177 var c = lookup.get(ele, l);
28178
28179 if (c) {
28180 higherCache = c;
28181 break;
28182 }
28183 }
28184
28185 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
28186
28187 var downscale = function downscale() {
28188 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
28189 }; // reset ele area in texture
28190
28191
28192 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28193 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
28194
28195 if (scalableFrom(oneUpCache)) {
28196 // then we can relatively cheaply rescale the existing image w/o rerendering
28197 downscale();
28198 } else if (scalableFrom(higherCache)) {
28199 // then use the higher cache for now and queue the next level down
28200 // to cheaply scale towards the smaller level
28201 if (highQualityReq) {
28202 for (var _l = higherCache.level; _l > lvl; _l--) {
28203 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
28204 }
28205
28206 downscale();
28207 } else {
28208 self.queueElement(ele, higherCache.level - 1);
28209 return higherCache;
28210 }
28211 } else {
28212 var lowerCache; // the nearest cache with a lower level
28213
28214 if (!deqing && !highQualityReq && !downscaleReq) {
28215 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
28216 var _c = lookup.get(ele, _l2);
28217
28218 if (_c) {
28219 lowerCache = _c;
28220 break;
28221 }
28222 }
28223 }
28224
28225 if (scalableFrom(lowerCache)) {
28226 // then use the lower quality cache for now and queue the better one for later
28227 self.queueElement(ele, lvl);
28228 return lowerCache;
28229 }
28230
28231 txr.context.translate(txr.usedWidth, 0);
28232 txr.context.scale(scale, scale);
28233 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
28234 txr.context.scale(1 / scale, 1 / scale);
28235 txr.context.translate(-txr.usedWidth, 0);
28236 }
28237
28238 eleCache = {
28239 x: txr.usedWidth,
28240 texture: txr,
28241 level: lvl,
28242 scale: scale,
28243 width: eleScaledW,
28244 height: eleScaledH,
28245 scaledLabelShown: scaledLabelShown
28246 };
28247 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
28248 txr.eleCaches.push(eleCache);
28249 lookup.set(ele, lvl, eleCache);
28250 self.checkTextureFullness(txr);
28251 return eleCache;
28252 };
28253
28254 ETCp.invalidateElements = function (eles) {
28255 for (var i = 0; i < eles.length; i++) {
28256 this.invalidateElement(eles[i]);
28257 }
28258 };
28259
28260 ETCp.invalidateElement = function (ele) {
28261 var self = this;
28262 var lookup = self.lookup;
28263 var caches = [];
28264 var invalid = lookup.isInvalid(ele);
28265
28266 if (!invalid) {
28267 return; // override the invalidation request if the element key has not changed
28268 }
28269
28270 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
28271 var cache = lookup.getForCachedKey(ele, lvl);
28272
28273 if (cache) {
28274 caches.push(cache);
28275 }
28276 }
28277
28278 var noOtherElesUseCache = lookup.invalidate(ele);
28279
28280 if (noOtherElesUseCache) {
28281 for (var i = 0; i < caches.length; i++) {
28282 var _cache = caches[i];
28283 var txr = _cache.texture; // remove space from the texture it belongs to
28284
28285 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
28286
28287 _cache.invalidated = true; // retire the texture if its utility is low
28288
28289 self.checkTextureUtility(txr);
28290 }
28291 } // remove from queue since the old req was for the old state
28292
28293
28294 self.removeFromQueue(ele);
28295 };
28296
28297 ETCp.checkTextureUtility = function (txr) {
28298 // invalidate all entries in the cache if the cache size is small
28299 if (txr.invalidatedWidth >= minUtility * txr.width) {
28300 this.retireTexture(txr);
28301 }
28302 };
28303
28304 ETCp.checkTextureFullness = function (txr) {
28305 // if texture has been mostly filled and passed over several times, remove
28306 // it from the queue so we don't need to waste time looking at it to put new things
28307 var self = this;
28308 var txrQ = self.getTextureQueue(txr.height);
28309
28310 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
28311 removeFromArray(txrQ, txr);
28312 } else {
28313 txr.fullnessChecks++;
28314 }
28315 };
28316
28317 ETCp.retireTexture = function (txr) {
28318 var self = this;
28319 var txrH = txr.height;
28320 var txrQ = self.getTextureQueue(txrH);
28321 var lookup = this.lookup; // retire the texture from the active / searchable queue:
28322
28323 removeFromArray(txrQ, txr);
28324 txr.retired = true; // remove the refs from the eles to the caches:
28325
28326 var eleCaches = txr.eleCaches;
28327
28328 for (var i = 0; i < eleCaches.length; i++) {
28329 var eleCache = eleCaches[i];
28330 lookup.deleteCache(eleCache.key, eleCache.level);
28331 }
28332
28333 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
28334
28335 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28336 rtxtrQ.push(txr);
28337 };
28338
28339 ETCp.addTexture = function (txrH, minW) {
28340 var self = this;
28341 var txrQ = self.getTextureQueue(txrH);
28342 var txr = {};
28343 txrQ.push(txr);
28344 txr.eleCaches = [];
28345 txr.height = txrH;
28346 txr.width = Math.max(defTxrWidth, minW);
28347 txr.usedWidth = 0;
28348 txr.invalidatedWidth = 0;
28349 txr.fullnessChecks = 0;
28350 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
28351 txr.context = txr.canvas.getContext('2d');
28352 return txr;
28353 };
28354
28355 ETCp.recycleTexture = function (txrH, minW) {
28356 var self = this;
28357 var txrQ = self.getTextureQueue(txrH);
28358 var rtxtrQ = self.getRetiredTextureQueue(txrH);
28359
28360 for (var i = 0; i < rtxtrQ.length; i++) {
28361 var txr = rtxtrQ[i];
28362
28363 if (txr.width >= minW) {
28364 txr.retired = false;
28365 txr.usedWidth = 0;
28366 txr.invalidatedWidth = 0;
28367 txr.fullnessChecks = 0;
28368 clearArray(txr.eleCaches);
28369 txr.context.setTransform(1, 0, 0, 1, 0, 0);
28370 txr.context.clearRect(0, 0, txr.width, txr.height);
28371 removeFromArray(rtxtrQ, txr);
28372 txrQ.push(txr);
28373 return txr;
28374 }
28375 }
28376 };
28377
28378 ETCp.queueElement = function (ele, lvl) {
28379 var self = this;
28380 var q = self.getElementQueue();
28381 var k2q = self.getElementKeyToQueue();
28382 var key = this.getKey(ele);
28383 var existingReq = k2q[key];
28384
28385 if (existingReq) {
28386 // use the max lvl b/c in between lvls are cheap to make
28387 existingReq.level = Math.max(existingReq.level, lvl);
28388 existingReq.eles.merge(ele);
28389 existingReq.reqs++;
28390 q.updateItem(existingReq);
28391 } else {
28392 var req = {
28393 eles: ele.spawn().merge(ele),
28394 level: lvl,
28395 reqs: 1,
28396 key: key
28397 };
28398 q.push(req);
28399 k2q[key] = req;
28400 }
28401 };
28402
28403 ETCp.dequeue = function (pxRatio
28404 /*, extent*/
28405 ) {
28406 var self = this;
28407 var q = self.getElementQueue();
28408 var k2q = self.getElementKeyToQueue();
28409 var dequeued = [];
28410 var lookup = self.lookup;
28411
28412 for (var i = 0; i < maxDeqSize; i++) {
28413 if (q.size() > 0) {
28414 var req = q.pop();
28415 var key = req.key;
28416 var ele = req.eles[0]; // all eles have the same key
28417
28418 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
28419
28420 k2q[key] = null; // dequeueing isn't necessary with an existing cache
28421
28422 if (cacheExists) {
28423 continue;
28424 }
28425
28426 dequeued.push(req);
28427 var bb = self.getBoundingBox(ele);
28428 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
28429 } else {
28430 break;
28431 }
28432 }
28433
28434 return dequeued;
28435 };
28436
28437 ETCp.removeFromQueue = function (ele) {
28438 var self = this;
28439 var q = self.getElementQueue();
28440 var k2q = self.getElementKeyToQueue();
28441 var key = this.getKey(ele);
28442 var req = k2q[key];
28443
28444 if (req != null) {
28445 if (req.eles.length === 1) {
28446 // remove if last ele in the req
28447 // bring to front of queue
28448 req.reqs = MAX_INT;
28449 q.updateItem(req);
28450 q.pop(); // remove from queue
28451
28452 k2q[key] = null; // remove from lookup map
28453 } else {
28454 // otherwise just remove ele from req
28455 req.eles.unmerge(ele);
28456 }
28457 }
28458 };
28459
28460 ETCp.onDequeue = function (fn) {
28461 this.onDequeues.push(fn);
28462 };
28463
28464 ETCp.offDequeue = function (fn) {
28465 removeFromArray(this.onDequeues, fn);
28466 };
28467
28468 ETCp.setupDequeueing = defs.setupDequeueing({
28469 deqRedrawThreshold: deqRedrawThreshold,
28470 deqCost: deqCost,
28471 deqAvgCost: deqAvgCost,
28472 deqNoDrawCost: deqNoDrawCost,
28473 deqFastCost: deqFastCost,
28474 deq: function deq(self, pxRatio, extent) {
28475 return self.dequeue(pxRatio, extent);
28476 },
28477 onDeqd: function onDeqd(self, deqd) {
28478 for (var i = 0; i < self.onDequeues.length; i++) {
28479 var fn = self.onDequeues[i];
28480 fn(deqd);
28481 }
28482 },
28483 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
28484 for (var i = 0; i < deqd.length; i++) {
28485 var eles = deqd[i].eles;
28486
28487 for (var j = 0; j < eles.length; j++) {
28488 var bb = eles[j].boundingBox();
28489
28490 if (boundingBoxesIntersect(bb, extent)) {
28491 return true;
28492 }
28493 }
28494 }
28495
28496 return false;
28497 },
28498 priority: function priority(self) {
28499 return self.renderer.beforeRenderPriorities.eleTxrDeq;
28500 }
28501 });
28502
28503 var defNumLayers = 1; // default number of layers to use
28504
28505 var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
28506
28507 var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
28508
28509 var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
28510
28511 var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
28512
28513 var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
28514
28515 var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
28516
28517 var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
28518
28519 var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
28520
28521 var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
28522
28523 var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
28524
28525 var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
28526
28527 var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
28528
28529 var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
28530 // var log = function(){ console.log.apply( console, arguments ); };
28531
28532 var LayeredTextureCache = function LayeredTextureCache(renderer) {
28533 var self = this;
28534 var r = self.renderer = renderer;
28535 var cy = r.cy;
28536 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
28537
28538 self.firstGet = true;
28539 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
28540 self.skipping = false;
28541 self.eleTxrDeqs = cy.collection();
28542 self.scheduleElementRefinement = lodash_debounce(function () {
28543 self.refineElementTextures(self.eleTxrDeqs);
28544 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
28545 }, refineEleDebounceTime);
28546 r.beforeRender(function (willDraw, now) {
28547 if (now - self.lastInvalidationTime <= invalidThreshold) {
28548 self.skipping = true;
28549 } else {
28550 self.skipping = false;
28551 }
28552 }, r.beforeRenderPriorities.lyrTxrSkip);
28553
28554 var qSort = function qSort(a, b) {
28555 return b.reqs - a.reqs;
28556 };
28557
28558 self.layersQueue = new heap$1(qSort);
28559 self.setupDequeueing();
28560 };
28561
28562 var LTCp = LayeredTextureCache.prototype;
28563 var layerIdPool = 0;
28564 var MAX_INT$1 = Math.pow(2, 53) - 1;
28565
28566 LTCp.makeLayer = function (bb, lvl) {
28567 var scale = Math.pow(2, lvl);
28568 var w = Math.ceil(bb.w * scale);
28569 var h = Math.ceil(bb.h * scale);
28570 var canvas = this.renderer.makeOffscreenCanvas(w, h);
28571 var layer = {
28572 id: layerIdPool = ++layerIdPool % MAX_INT$1,
28573 bb: bb,
28574 level: lvl,
28575 width: w,
28576 height: h,
28577 canvas: canvas,
28578 context: canvas.getContext('2d'),
28579 eles: [],
28580 elesQueue: [],
28581 reqs: 0
28582 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
28583
28584 var cxt = layer.context;
28585 var dx = -layer.bb.x1;
28586 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
28587
28588 cxt.scale(scale, scale);
28589 cxt.translate(dx, dy);
28590 return layer;
28591 };
28592
28593 LTCp.getLayers = function (eles, pxRatio, lvl) {
28594 var self = this;
28595 var r = self.renderer;
28596 var cy = r.cy;
28597 var zoom = cy.zoom();
28598 var firstGet = self.firstGet;
28599 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
28600 //log eles.map(function(ele){ return ele.id() }) );
28601
28602 if (lvl == null) {
28603 lvl = Math.ceil(log2(zoom * pxRatio));
28604
28605 if (lvl < minLvl$1) {
28606 lvl = minLvl$1;
28607 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
28608 return null;
28609 }
28610 }
28611
28612 self.validateLayersElesOrdering(lvl, eles);
28613 var layersByLvl = self.layersByLevel;
28614 var scale = Math.pow(2, lvl);
28615 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
28616 var bb;
28617 var lvlComplete = self.levelIsComplete(lvl, eles);
28618 var tmpLayers;
28619
28620 var checkTempLevels = function checkTempLevels() {
28621 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
28622 self.validateLayersElesOrdering(l, eles);
28623
28624 if (self.levelIsComplete(l, eles)) {
28625 tmpLayers = layersByLvl[l];
28626 return true;
28627 }
28628 };
28629
28630 var checkLvls = function checkLvls(dir) {
28631 if (tmpLayers) {
28632 return;
28633 }
28634
28635 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
28636 if (canUseAsTmpLvl(l)) {
28637 break;
28638 }
28639 }
28640 };
28641
28642 checkLvls(+1);
28643 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
28644
28645 for (var i = layers.length - 1; i >= 0; i--) {
28646 var layer = layers[i];
28647
28648 if (layer.invalid) {
28649 removeFromArray(layers, layer);
28650 }
28651 }
28652 };
28653
28654 if (!lvlComplete) {
28655 // if the current level is incomplete, then use the closest, best quality layerset temporarily
28656 // and later queue the current layerset so we can get the proper quality level soon
28657 checkTempLevels();
28658 } else {
28659 // log('level complete, using existing layers\n--');
28660 return layers;
28661 }
28662
28663 var getBb = function getBb() {
28664 if (!bb) {
28665 bb = makeBoundingBox();
28666
28667 for (var i = 0; i < eles.length; i++) {
28668 updateBoundingBox(bb, eles[i].boundingBox());
28669 }
28670 }
28671
28672 return bb;
28673 };
28674
28675 var makeLayer = function makeLayer(opts) {
28676 opts = opts || {};
28677 var after = opts.after;
28678 getBb();
28679 var area = bb.w * scale * (bb.h * scale);
28680
28681 if (area > maxLayerArea) {
28682 return null;
28683 }
28684
28685 var layer = self.makeLayer(bb, lvl);
28686
28687 if (after != null) {
28688 var index = layers.indexOf(after) + 1;
28689 layers.splice(index, 0, layer);
28690 } else if (opts.insert === undefined || opts.insert) {
28691 // no after specified => first layer made so put at start
28692 layers.unshift(layer);
28693 } // if( tmpLayers ){
28694 //self.queueLayer( layer );
28695 // }
28696
28697
28698 return layer;
28699 };
28700
28701 if (self.skipping && !firstGet) {
28702 // log('skip layers');
28703 return null;
28704 } // log('do layers');
28705
28706
28707 var layer = null;
28708 var maxElesPerLayer = eles.length / defNumLayers;
28709 var allowLazyQueueing = !firstGet;
28710
28711 for (var i = 0; i < eles.length; i++) {
28712 var ele = eles[i];
28713 var rs = ele._private.rscratch;
28714 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
28715
28716 var existingLayer = caches[lvl];
28717
28718 if (existingLayer) {
28719 // reuse layer for later eles
28720 // log('reuse layer for', ele.id());
28721 layer = existingLayer;
28722 continue;
28723 }
28724
28725 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
28726 // log('make new layer for ele %s', ele.id());
28727 layer = makeLayer({
28728 insert: true,
28729 after: layer
28730 }); // if now layer can be built then we can't use layers at this level
28731
28732 if (!layer) {
28733 return null;
28734 } // log('new layer with id %s', layer.id);
28735
28736 }
28737
28738 if (tmpLayers || allowLazyQueueing) {
28739 // log('queue ele %s in layer %s', ele.id(), layer.id);
28740 self.queueLayer(layer, ele);
28741 } else {
28742 // log('draw ele %s in layer %s', ele.id(), layer.id);
28743 self.drawEleInLayer(layer, ele, lvl, pxRatio);
28744 }
28745
28746 layer.eles.push(ele);
28747 caches[lvl] = layer;
28748 } // log('--');
28749
28750
28751 if (tmpLayers) {
28752 // then we only queued the current layerset and can't draw it yet
28753 return tmpLayers;
28754 }
28755
28756 if (allowLazyQueueing) {
28757 // log('lazy queue level', lvl);
28758 return null;
28759 }
28760
28761 return layers;
28762 }; // a layer may want to use an ele cache of a higher level to avoid blurriness
28763 // so the layer level might not equal the ele level
28764
28765
28766 LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28767 return lvl;
28768 };
28769
28770 LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28771 var self = this;
28772 var r = this.renderer;
28773 var context = layer.context;
28774 var bb = ele.boundingBox();
28775
28776 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28777 return;
28778 }
28779
28780 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28781
28782 {
28783 r.setImgSmoothing(context, false);
28784 }
28785
28786 {
28787 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28788 }
28789
28790 {
28791 r.setImgSmoothing(context, true);
28792 }
28793 };
28794
28795 LTCp.levelIsComplete = function (lvl, eles) {
28796 var self = this;
28797 var layers = self.layersByLevel[lvl];
28798
28799 if (!layers || layers.length === 0) {
28800 return false;
28801 }
28802
28803 var numElesInLayers = 0;
28804
28805 for (var i = 0; i < layers.length; i++) {
28806 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28807
28808 if (layer.reqs > 0) {
28809 return false;
28810 } // if the layer is invalid, the level is not complete
28811
28812
28813 if (layer.invalid) {
28814 return false;
28815 }
28816
28817 numElesInLayers += layer.eles.length;
28818 } // we should have exactly the number of eles passed in to be complete
28819
28820
28821 if (numElesInLayers !== eles.length) {
28822 return false;
28823 }
28824
28825 return true;
28826 };
28827
28828 LTCp.validateLayersElesOrdering = function (lvl, eles) {
28829 var layers = this.layersByLevel[lvl];
28830
28831 if (!layers) {
28832 return;
28833 } // if in a layer the eles are not in the same order, then the layer is invalid
28834 // (i.e. there is an ele in between the eles in the layer)
28835
28836
28837 for (var i = 0; i < layers.length; i++) {
28838 var layer = layers[i];
28839 var offset = -1; // find the offset
28840
28841 for (var j = 0; j < eles.length; j++) {
28842 if (layer.eles[0] === eles[j]) {
28843 offset = j;
28844 break;
28845 }
28846 }
28847
28848 if (offset < 0) {
28849 // then the layer has nonexistant elements and is invalid
28850 this.invalidateLayer(layer);
28851 continue;
28852 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28853
28854
28855 var o = offset;
28856
28857 for (var j = 0; j < layer.eles.length; j++) {
28858 if (layer.eles[j] !== eles[o + j]) {
28859 // log('invalidate based on ordering', layer.id);
28860 this.invalidateLayer(layer);
28861 break;
28862 }
28863 }
28864 }
28865 };
28866
28867 LTCp.updateElementsInLayers = function (eles, update) {
28868 var self = this;
28869 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28870 // layer itself along the way
28871
28872 for (var i = 0; i < eles.length; i++) {
28873 var req = isEles ? null : eles[i];
28874 var ele = isEles ? eles[i] : eles[i].ele;
28875 var rs = ele._private.rscratch;
28876 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28877
28878 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28879 var layer = caches[l];
28880
28881 if (!layer) {
28882 continue;
28883 } // if update is a request from the ele cache, then it affects only
28884 // the matching level
28885
28886
28887 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28888 continue;
28889 }
28890
28891 update(layer, ele, req);
28892 }
28893 }
28894 };
28895
28896 LTCp.haveLayers = function () {
28897 var self = this;
28898 var haveLayers = false;
28899
28900 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28901 var layers = self.layersByLevel[l];
28902
28903 if (layers && layers.length > 0) {
28904 haveLayers = true;
28905 break;
28906 }
28907 }
28908
28909 return haveLayers;
28910 };
28911
28912 LTCp.invalidateElements = function (eles) {
28913 var self = this;
28914
28915 if (eles.length === 0) {
28916 return;
28917 }
28918
28919 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28920
28921 if (eles.length === 0 || !self.haveLayers()) {
28922 return;
28923 }
28924
28925 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28926 self.invalidateLayer(layer);
28927 });
28928 };
28929
28930 LTCp.invalidateLayer = function (layer) {
28931 // log('update invalidate layer time');
28932 this.lastInvalidationTime = performanceNow();
28933
28934 if (layer.invalid) {
28935 return;
28936 } // save cycles
28937
28938
28939 var lvl = layer.level;
28940 var eles = layer.eles;
28941 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28942
28943 removeFromArray(layers, layer); // layer.eles = [];
28944
28945 layer.elesQueue = [];
28946 layer.invalid = true;
28947
28948 if (layer.replacement) {
28949 layer.replacement.invalid = true;
28950 }
28951
28952 for (var i = 0; i < eles.length; i++) {
28953 var caches = eles[i]._private.rscratch.imgLayerCaches;
28954
28955 if (caches) {
28956 caches[lvl] = null;
28957 }
28958 }
28959 };
28960
28961 LTCp.refineElementTextures = function (eles) {
28962 var self = this; // log('refine', eles.length);
28963
28964 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28965 var rLyr = layer.replacement;
28966
28967 if (!rLyr) {
28968 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28969 rLyr.replaces = layer;
28970 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28971 }
28972
28973 if (!rLyr.reqs) {
28974 for (var i = 0; i < rLyr.eles.length; i++) {
28975 self.queueLayer(rLyr, rLyr.eles[i]);
28976 } // log('queue replacement layer refinement', rLyr.id);
28977
28978 }
28979 });
28980 };
28981
28982 LTCp.enqueueElementRefinement = function (ele) {
28983
28984 this.eleTxrDeqs.merge(ele);
28985 this.scheduleElementRefinement();
28986 };
28987
28988 LTCp.queueLayer = function (layer, ele) {
28989 var self = this;
28990 var q = self.layersQueue;
28991 var elesQ = layer.elesQueue;
28992 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28993
28994 if (layer.replacement) {
28995 return;
28996 }
28997
28998 if (ele) {
28999 if (hasId[ele.id()]) {
29000 return;
29001 }
29002
29003 elesQ.push(ele);
29004 hasId[ele.id()] = true;
29005 }
29006
29007 if (layer.reqs) {
29008 layer.reqs++;
29009 q.updateItem(layer);
29010 } else {
29011 layer.reqs = 1;
29012 q.push(layer);
29013 }
29014 };
29015
29016 LTCp.dequeue = function (pxRatio) {
29017 var self = this;
29018 var q = self.layersQueue;
29019 var deqd = [];
29020 var eleDeqs = 0;
29021
29022 while (eleDeqs < maxDeqSize$1) {
29023 if (q.size() === 0) {
29024 break;
29025 }
29026
29027 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
29028
29029 if (layer.replacement) {
29030 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
29031 q.pop();
29032 continue;
29033 } // if this is a replacement layer that has been superceded, then forget it
29034
29035
29036 if (layer.replaces && layer !== layer.replaces.replacement) {
29037 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
29038 q.pop();
29039 continue;
29040 }
29041
29042 if (layer.invalid) {
29043 // log('replacement layer %s is invalid; dequeued', layer.id);
29044 q.pop();
29045 continue;
29046 }
29047
29048 var ele = layer.elesQueue.shift();
29049
29050 if (ele) {
29051 // log('dequeue layer %s', layer.id);
29052 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
29053 eleDeqs++;
29054 }
29055
29056 if (deqd.length === 0) {
29057 // we need only one entry in deqd to queue redrawing etc
29058 deqd.push(true);
29059 } // if the layer has all its eles done, then remove from the queue
29060
29061
29062 if (layer.elesQueue.length === 0) {
29063 q.pop();
29064 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
29065 // when a replacement layer is dequeued, it replaces the old layer in the level
29066
29067 if (layer.replaces) {
29068 self.applyLayerReplacement(layer);
29069 }
29070
29071 self.requestRedraw();
29072 }
29073 }
29074
29075 return deqd;
29076 };
29077
29078 LTCp.applyLayerReplacement = function (layer) {
29079 var self = this;
29080 var layersInLevel = self.layersByLevel[layer.level];
29081 var replaced = layer.replaces;
29082 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
29083 // refs would be a mistake (i.e. overwriting the true active layer)
29084
29085 if (index < 0 || replaced.invalid) {
29086 // log('replacement layer would have no effect', layer.id);
29087 return;
29088 }
29089
29090 layersInLevel[index] = layer; // replace level ref
29091 // replace refs in eles
29092
29093 for (var i = 0; i < layer.eles.length; i++) {
29094 var _p = layer.eles[i]._private;
29095 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
29096
29097 if (cache) {
29098 cache[layer.level] = layer;
29099 }
29100 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
29101
29102
29103 self.requestRedraw();
29104 };
29105
29106 LTCp.requestRedraw = lodash_debounce(function () {
29107 var r = this.renderer;
29108 r.redrawHint('eles', true);
29109 r.redrawHint('drag', true);
29110 r.redraw();
29111 }, 100);
29112 LTCp.setupDequeueing = defs.setupDequeueing({
29113 deqRedrawThreshold: deqRedrawThreshold$1,
29114 deqCost: deqCost$1,
29115 deqAvgCost: deqAvgCost$1,
29116 deqNoDrawCost: deqNoDrawCost$1,
29117 deqFastCost: deqFastCost$1,
29118 deq: function deq(self, pxRatio) {
29119 return self.dequeue(pxRatio);
29120 },
29121 onDeqd: noop,
29122 shouldRedraw: trueify,
29123 priority: function priority(self) {
29124 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
29125 }
29126 });
29127
29128 var CRp = {};
29129 var impl;
29130
29131 function polygon(context, points) {
29132 for (var i = 0; i < points.length; i++) {
29133 var pt = points[i];
29134 context.lineTo(pt.x, pt.y);
29135 }
29136 }
29137
29138 function triangleBackcurve(context, points, controlPoint) {
29139 var firstPt;
29140
29141 for (var i = 0; i < points.length; i++) {
29142 var pt = points[i];
29143
29144 if (i === 0) {
29145 firstPt = pt;
29146 }
29147
29148 context.lineTo(pt.x, pt.y);
29149 }
29150
29151 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
29152 }
29153
29154 function triangleTee(context, trianglePoints, teePoints) {
29155 if (context.beginPath) {
29156 context.beginPath();
29157 }
29158
29159 var triPts = trianglePoints;
29160
29161 for (var i = 0; i < triPts.length; i++) {
29162 var pt = triPts[i];
29163 context.lineTo(pt.x, pt.y);
29164 }
29165
29166 var teePts = teePoints;
29167 var firstTeePt = teePoints[0];
29168 context.moveTo(firstTeePt.x, firstTeePt.y);
29169
29170 for (var i = 1; i < teePts.length; i++) {
29171 var pt = teePts[i];
29172 context.lineTo(pt.x, pt.y);
29173 }
29174
29175 if (context.closePath) {
29176 context.closePath();
29177 }
29178 }
29179
29180 function circleTriangle(context, trianglePoints, rx, ry, r) {
29181 if (context.beginPath) {
29182 context.beginPath();
29183 }
29184
29185 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29186 var triPts = trianglePoints;
29187 var firstTrPt = triPts[0];
29188 context.moveTo(firstTrPt.x, firstTrPt.y);
29189
29190 for (var i = 0; i < triPts.length; i++) {
29191 var pt = triPts[i];
29192 context.lineTo(pt.x, pt.y);
29193 }
29194
29195 if (context.closePath) {
29196 context.closePath();
29197 }
29198 }
29199
29200 function circle(context, rx, ry, r) {
29201 context.arc(rx, ry, r, 0, Math.PI * 2, false);
29202 }
29203
29204 CRp.arrowShapeImpl = function (name) {
29205 return (impl || (impl = {
29206 'polygon': polygon,
29207 'triangle-backcurve': triangleBackcurve,
29208 'triangle-tee': triangleTee,
29209 'circle-triangle': circleTriangle,
29210 'triangle-cross': triangleTee,
29211 'circle': circle
29212 }))[name];
29213 };
29214
29215 var CRp$1 = {};
29216
29217 CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
29218 var r = this;
29219
29220 if (ele.isNode()) {
29221 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29222 } else {
29223 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
29224 }
29225 };
29226
29227 CRp$1.drawElementOverlay = function (context, ele) {
29228 var r = this;
29229
29230 if (ele.isNode()) {
29231 r.drawNodeOverlay(context, ele);
29232 } else {
29233 r.drawEdgeOverlay(context, ele);
29234 }
29235 };
29236
29237 CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
29238 var r = this;
29239 var bb = eleTxrCache.getBoundingBox(ele);
29240
29241 if (bb.w === 0 || bb.h === 0) {
29242 return;
29243 } // ignore zero size case
29244
29245
29246 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
29247
29248 if (eleCache != null) {
29249 var opacity = getOpacity(r, ele);
29250
29251 if (opacity === 0) {
29252 return;
29253 }
29254
29255 var theta = getRotation(r, ele);
29256 var x1 = bb.x1,
29257 y1 = bb.y1,
29258 w = bb.w,
29259 h = bb.h;
29260 var x, y, sx, sy, smooth;
29261
29262 if (theta !== 0) {
29263 var rotPt = eleTxrCache.getRotationPoint(ele);
29264 sx = rotPt.x;
29265 sy = rotPt.y;
29266 context.translate(sx, sy);
29267 context.rotate(theta);
29268 smooth = r.getImgSmoothing(context);
29269
29270 if (!smooth) {
29271 r.setImgSmoothing(context, true);
29272 }
29273
29274 var off = eleTxrCache.getRotationOffset(ele);
29275 x = off.x;
29276 y = off.y;
29277 } else {
29278 x = x1;
29279 y = y1;
29280 }
29281
29282 var oldGlobalAlpha;
29283
29284 if (opacity !== 1) {
29285 oldGlobalAlpha = context.globalAlpha;
29286 context.globalAlpha = oldGlobalAlpha * opacity;
29287 }
29288
29289 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
29290
29291 if (opacity !== 1) {
29292 context.globalAlpha = oldGlobalAlpha;
29293 }
29294
29295 if (theta !== 0) {
29296 context.rotate(-theta);
29297 context.translate(-sx, -sy);
29298
29299 if (!smooth) {
29300 r.setImgSmoothing(context, false);
29301 }
29302 }
29303 } else {
29304 eleTxrCache.drawElement(context, ele); // direct draw fallback
29305 }
29306 };
29307
29308 var getZeroRotation = function getZeroRotation() {
29309 return 0;
29310 };
29311
29312 var getLabelRotation = function getLabelRotation(r, ele) {
29313 return r.getTextAngle(ele, null);
29314 };
29315
29316 var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
29317 return r.getTextAngle(ele, 'source');
29318 };
29319
29320 var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
29321 return r.getTextAngle(ele, 'target');
29322 };
29323
29324 var getOpacity = function getOpacity(r, ele) {
29325 return ele.effectiveOpacity();
29326 };
29327
29328 var getTextOpacity = function getTextOpacity(e, ele) {
29329 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
29330 };
29331
29332 CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
29333 var r = this;
29334 var _r$data = r.data,
29335 eleTxrCache = _r$data.eleTxrCache,
29336 lblTxrCache = _r$data.lblTxrCache,
29337 slbTxrCache = _r$data.slbTxrCache,
29338 tlbTxrCache = _r$data.tlbTxrCache;
29339 var bb = ele.boundingBox();
29340 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
29341
29342 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
29343 return;
29344 }
29345
29346 if (!extent || boundingBoxesIntersect(bb, extent)) {
29347 var isEdge = ele.isEdge();
29348
29349 var badLine = ele.element()._private.rscratch.badLine;
29350
29351 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
29352
29353 if (!isEdge || !badLine) {
29354 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
29355 }
29356
29357 if (isEdge && !badLine) {
29358 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
29359 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
29360 }
29361
29362 r.drawElementOverlay(context, ele);
29363 }
29364 };
29365
29366 CRp$1.drawElements = function (context, eles) {
29367 var r = this;
29368
29369 for (var i = 0; i < eles.length; i++) {
29370 var ele = eles[i];
29371 r.drawElement(context, ele);
29372 }
29373 };
29374
29375 CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
29376 var r = this;
29377
29378 for (var i = 0; i < eles.length; i++) {
29379 var ele = eles[i];
29380 r.drawCachedElement(context, ele, pxRatio, extent);
29381 }
29382 };
29383
29384 CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
29385 var r = this;
29386
29387 for (var i = 0; i < eles.length; i++) {
29388 var ele = eles[i];
29389
29390 if (!ele.isNode()) {
29391 continue;
29392 }
29393
29394 r.drawCachedElement(context, ele, pxRatio, extent);
29395 }
29396 };
29397
29398 CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
29399 var r = this;
29400 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
29401
29402 if (layers) {
29403 for (var i = 0; i < layers.length; i++) {
29404 var layer = layers[i];
29405 var bb = layer.bb;
29406
29407 if (bb.w === 0 || bb.h === 0) {
29408 continue;
29409 }
29410
29411 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
29412 }
29413 } else {
29414 // fall back on plain caching if no layers
29415 r.drawCachedElements(context, eles, pxRatio, extent);
29416 }
29417 };
29418
29419 /* global Path2D */
29420 var CRp$2 = {};
29421
29422 CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
29423 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29424 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29425 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29426 var r = this;
29427 var rs = edge._private.rscratch;
29428
29429 if (shouldDrawOpacity && !edge.visible()) {
29430 return;
29431 } // if bezier ctrl pts can not be calculated, then die
29432
29433
29434 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
29435 // isNaN in case edge is impossible and browser bugs (e.g. safari)
29436 return;
29437 }
29438
29439 var bb;
29440
29441 if (shiftToOriginWithBb) {
29442 bb = shiftToOriginWithBb;
29443 context.translate(-bb.x1, -bb.y1);
29444 }
29445
29446 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
29447 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
29448 var lineStyle = edge.pstyle('line-style').value;
29449 var edgeWidth = edge.pstyle('width').pfValue;
29450 var lineCap = edge.pstyle('line-cap').value;
29451 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
29452
29453 var effectiveArrowOpacity = opacity * lineOpacity;
29454
29455 var drawLine = function drawLine() {
29456 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
29457 context.lineWidth = edgeWidth;
29458 context.lineCap = lineCap;
29459 r.eleStrokeStyle(context, edge, strokeOpacity);
29460 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
29461 context.lineCap = 'butt'; // reset for other drawing functions
29462 };
29463
29464 var drawOverlay = function drawOverlay() {
29465 if (!shouldDrawOverlay) {
29466 return;
29467 }
29468
29469 r.drawEdgeOverlay(context, edge);
29470 };
29471
29472 var drawArrows = function drawArrows() {
29473 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
29474 r.drawArrowheads(context, edge, arrowOpacity);
29475 };
29476
29477 var drawText = function drawText() {
29478 r.drawElementText(context, edge, null, drawLabel);
29479 };
29480
29481 context.lineJoin = 'round';
29482 var ghost = edge.pstyle('ghost').value === 'yes';
29483
29484 if (ghost) {
29485 var gx = edge.pstyle('ghost-offset-x').pfValue;
29486 var gy = edge.pstyle('ghost-offset-y').pfValue;
29487 var ghostOpacity = edge.pstyle('ghost-opacity').value;
29488 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
29489 context.translate(gx, gy);
29490 drawLine(effectiveGhostOpacity);
29491 drawArrows(effectiveGhostOpacity);
29492 context.translate(-gx, -gy);
29493 }
29494
29495 drawLine();
29496 drawArrows();
29497 drawOverlay();
29498 drawText();
29499
29500 if (shiftToOriginWithBb) {
29501 context.translate(bb.x1, bb.y1);
29502 }
29503 };
29504
29505 CRp$2.drawEdgeOverlay = function (context, edge) {
29506 if (!edge.visible()) {
29507 return;
29508 }
29509
29510 var overlayOpacity = edge.pstyle('overlay-opacity').value;
29511
29512 if (overlayOpacity === 0) {
29513 return;
29514 }
29515
29516 var r = this;
29517 var usePaths = r.usePaths();
29518 var rs = edge._private.rscratch;
29519 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
29520 var overlayWidth = 2 * overlayPadding;
29521 var overlayColor = edge.pstyle('overlay-color').value;
29522 context.lineWidth = overlayWidth;
29523
29524 if (rs.edgeType === 'self' && !usePaths) {
29525 context.lineCap = 'butt';
29526 } else {
29527 context.lineCap = 'round';
29528 }
29529
29530 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29531 r.drawEdgePath(edge, context, rs.allpts, 'solid');
29532 };
29533
29534 CRp$2.drawEdgePath = function (edge, context, pts, type) {
29535 var rs = edge._private.rscratch;
29536 var canvasCxt = context;
29537 var path;
29538 var pathCacheHit = false;
29539 var usePaths = this.usePaths();
29540 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
29541 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
29542
29543 if (usePaths) {
29544 var pathCacheKey = pts.join('$');
29545 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
29546
29547 if (keyMatches) {
29548 path = context = rs.pathCache;
29549 pathCacheHit = true;
29550 } else {
29551 path = context = new Path2D();
29552 rs.pathCacheKey = pathCacheKey;
29553 rs.pathCache = path;
29554 }
29555 }
29556
29557 if (canvasCxt.setLineDash) {
29558 // for very outofdate browsers
29559 switch (type) {
29560 case 'dotted':
29561 canvasCxt.setLineDash([1, 1]);
29562 break;
29563
29564 case 'dashed':
29565 canvasCxt.setLineDash(lineDashPattern);
29566 canvasCxt.lineDashOffset = lineDashOffset;
29567 break;
29568
29569 case 'solid':
29570 canvasCxt.setLineDash([]);
29571 break;
29572 }
29573 }
29574
29575 if (!pathCacheHit && !rs.badLine) {
29576 if (context.beginPath) {
29577 context.beginPath();
29578 }
29579
29580 context.moveTo(pts[0], pts[1]);
29581
29582 switch (rs.edgeType) {
29583 case 'bezier':
29584 case 'self':
29585 case 'compound':
29586 case 'multibezier':
29587 for (var i = 2; i + 3 < pts.length; i += 4) {
29588 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
29589 }
29590
29591 break;
29592
29593 case 'straight':
29594 case 'segments':
29595 case 'haystack':
29596 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
29597 context.lineTo(pts[_i], pts[_i + 1]);
29598 }
29599
29600 break;
29601 }
29602 }
29603
29604 context = canvasCxt;
29605
29606 if (usePaths) {
29607 context.stroke(path);
29608 } else {
29609 context.stroke();
29610 } // reset any line dashes
29611
29612
29613 if (context.setLineDash) {
29614 // for very outofdate browsers
29615 context.setLineDash([]);
29616 }
29617 };
29618
29619 CRp$2.drawArrowheads = function (context, edge, opacity) {
29620 var rs = edge._private.rscratch;
29621 var isHaystack = rs.edgeType === 'haystack';
29622
29623 if (!isHaystack) {
29624 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
29625 }
29626
29627 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
29628 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
29629
29630 if (!isHaystack) {
29631 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
29632 }
29633 };
29634
29635 CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
29636 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
29637 return;
29638 }
29639
29640 var self = this;
29641 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
29642
29643 if (arrowShape === 'none') {
29644 return;
29645 }
29646
29647 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
29648 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
29649 var edgeWidth = edge.pstyle('width').pfValue;
29650 var edgeOpacity = edge.pstyle('opacity').value;
29651
29652 if (opacity === undefined) {
29653 opacity = edgeOpacity;
29654 }
29655
29656 var gco = context.globalCompositeOperation;
29657
29658 if (opacity !== 1 || arrowFill === 'hollow') {
29659 // then extra clear is needed
29660 context.globalCompositeOperation = 'destination-out';
29661 self.colorFillStyle(context, 255, 255, 255, 1);
29662 self.colorStrokeStyle(context, 255, 255, 255, 1);
29663 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
29664 context.globalCompositeOperation = gco;
29665 } // otherwise, the opaque arrow clears it for free :)
29666
29667
29668 var color = edge.pstyle(prefix + '-arrow-color').value;
29669 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
29670 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
29671 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
29672 };
29673
29674 CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
29675 var r = this;
29676 var usePaths = this.usePaths() && shape !== 'triangle-cross';
29677 var pathCacheHit = false;
29678 var path;
29679 var canvasContext = context;
29680 var translation = {
29681 x: x,
29682 y: y
29683 };
29684 var scale = edge.pstyle('arrow-scale').value;
29685 var size = this.getArrowWidth(edgeWidth, scale);
29686 var shapeImpl = r.arrowShapes[shape];
29687
29688 if (usePaths) {
29689 var cache = r.arrowPathCache = r.arrowPathCache || [];
29690 var key = hashString(shape);
29691 var cachedPath = cache[key];
29692
29693 if (cachedPath != null) {
29694 path = context = cachedPath;
29695 pathCacheHit = true;
29696 } else {
29697 path = context = new Path2D();
29698 cache[key] = path;
29699 }
29700 }
29701
29702 if (!pathCacheHit) {
29703 if (context.beginPath) {
29704 context.beginPath();
29705 }
29706
29707 if (usePaths) {
29708 // store in the path cache with values easily manipulated later
29709 shapeImpl.draw(context, 1, 0, {
29710 x: 0,
29711 y: 0
29712 }, 1);
29713 } else {
29714 shapeImpl.draw(context, size, angle, translation, edgeWidth);
29715 }
29716
29717 if (context.closePath) {
29718 context.closePath();
29719 }
29720 }
29721
29722 context = canvasContext;
29723
29724 if (usePaths) {
29725 // set transform to arrow position/orientation
29726 context.translate(x, y);
29727 context.rotate(angle);
29728 context.scale(size, size);
29729 }
29730
29731 if (fill === 'filled' || fill === 'both') {
29732 if (usePaths) {
29733 context.fill(path);
29734 } else {
29735 context.fill();
29736 }
29737 }
29738
29739 if (fill === 'hollow' || fill === 'both') {
29740 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
29741 context.lineJoin = 'miter';
29742
29743 if (usePaths) {
29744 context.stroke(path);
29745 } else {
29746 context.stroke();
29747 }
29748 }
29749
29750 if (usePaths) {
29751 // reset transform by applying inverse
29752 context.scale(1 / size, 1 / size);
29753 context.rotate(-angle);
29754 context.translate(-x, -y);
29755 }
29756 };
29757
29758 var CRp$3 = {};
29759
29760 CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29761 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29762 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29763 return;
29764 }
29765
29766 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29767 };
29768
29769 CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29770 var r = this;
29771 var pos = node.position();
29772 var nodeX = pos.x;
29773 var nodeY = pos.y;
29774 var styleObj = node.cy().style();
29775 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29776 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29777 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29778 var nodeW = node.width();
29779 var nodeH = node.height();
29780 var paddingX2 = node.padding() * 2;
29781 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29782 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29783 var rs = node._private.rscratch;
29784 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29785 var shouldClip = clip === 'node';
29786 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29787 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29788 var imgW = img.width || img.cachedW;
29789 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29790
29791 if (null == imgW || null == imgH) {
29792 document.body.appendChild(img); // eslint-disable-line no-undef
29793
29794 imgW = img.cachedW = img.width || img.offsetWidth;
29795 imgH = img.cachedH = img.height || img.offsetHeight;
29796 document.body.removeChild(img); // eslint-disable-line no-undef
29797 }
29798
29799 var w = imgW;
29800 var h = imgH;
29801
29802 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29803 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29804 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29805 } else {
29806 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29807 }
29808 }
29809
29810 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29811 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29812 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29813 } else {
29814 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29815 }
29816 }
29817
29818 if (w === 0 || h === 0) {
29819 return; // no point in drawing empty image (and chrome is broken in this case)
29820 }
29821
29822 if (fit === 'contain') {
29823 var scale = Math.min(nodeTW / w, nodeTH / h);
29824 w *= scale;
29825 h *= scale;
29826 } else if (fit === 'cover') {
29827 var scale = Math.max(nodeTW / w, nodeTH / h);
29828 w *= scale;
29829 h *= scale;
29830 }
29831
29832 var x = nodeX - nodeTW / 2; // left
29833
29834 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29835 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29836
29837 if (posXUnits === '%') {
29838 x += (nodeTW - w) * posXPfVal;
29839 } else {
29840 x += posXPfVal;
29841 }
29842
29843 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29844 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29845
29846 if (offXUnits === '%') {
29847 x += (nodeTW - w) * offXPfVal;
29848 } else {
29849 x += offXPfVal;
29850 }
29851
29852 var y = nodeY - nodeTH / 2; // top
29853
29854 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29855 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29856
29857 if (posYUnits === '%') {
29858 y += (nodeTH - h) * posYPfVal;
29859 } else {
29860 y += posYPfVal;
29861 }
29862
29863 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29864 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29865
29866 if (offYUnits === '%') {
29867 y += (nodeTH - h) * offYPfVal;
29868 } else {
29869 y += offYPfVal;
29870 }
29871
29872 if (rs.pathCache) {
29873 x -= nodeX;
29874 y -= nodeY;
29875 nodeX = 0;
29876 nodeY = 0;
29877 }
29878
29879 var gAlpha = context.globalAlpha;
29880 context.globalAlpha = imgOpacity;
29881 var smoothingEnabled = r.getImgSmoothing(context);
29882 var isSmoothingSwitched = false;
29883
29884 if (smooth === 'no' && smoothingEnabled) {
29885 r.setImgSmoothing(context, false);
29886 isSmoothingSwitched = true;
29887 } else if (smooth === 'yes' && !smoothingEnabled) {
29888 r.setImgSmoothing(context, true);
29889 isSmoothingSwitched = true;
29890 }
29891
29892 if (repeat === 'no-repeat') {
29893 if (shouldClip) {
29894 context.save();
29895
29896 if (rs.pathCache) {
29897 context.clip(rs.pathCache);
29898 } else {
29899 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29900 context.clip();
29901 }
29902 }
29903
29904 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29905
29906 if (shouldClip) {
29907 context.restore();
29908 }
29909 } else {
29910 var pattern = context.createPattern(img, repeat);
29911 context.fillStyle = pattern;
29912 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29913 context.translate(x, y);
29914 context.fill();
29915 context.translate(-x, -y);
29916 }
29917
29918 context.globalAlpha = gAlpha;
29919
29920 if (isSmoothingSwitched) {
29921 r.setImgSmoothing(context, smoothingEnabled);
29922 }
29923 };
29924
29925 var CRp$4 = {};
29926
29927 CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29928 if (!scale) {
29929 var zoom = ele.cy().zoom();
29930 var pxRatio = this.getPixelRatio();
29931 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29932
29933 scale = Math.pow(2, lvl);
29934 }
29935
29936 var computedSize = ele.pstyle('font-size').pfValue * scale;
29937 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29938
29939 if (computedSize < minSize) {
29940 return false;
29941 }
29942
29943 return true;
29944 };
29945
29946 CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29947 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29948 var r = this;
29949
29950 if (force == null) {
29951 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29952 return;
29953 }
29954 } else if (force === false) {
29955 return;
29956 }
29957
29958 if (ele.isNode()) {
29959 var label = ele.pstyle('label');
29960
29961 if (!label || !label.value) {
29962 return;
29963 }
29964
29965 var justification = r.getLabelJustification(ele);
29966 context.textAlign = justification;
29967 context.textBaseline = 'bottom';
29968 } else {
29969 var badLine = ele.element()._private.rscratch.badLine;
29970
29971 var _label = ele.pstyle('label');
29972
29973 var srcLabel = ele.pstyle('source-label');
29974 var tgtLabel = ele.pstyle('target-label');
29975
29976 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29977 return;
29978 }
29979
29980 context.textAlign = 'center';
29981 context.textBaseline = 'bottom';
29982 }
29983
29984 var applyRotation = !shiftToOriginWithBb;
29985 var bb;
29986
29987 if (shiftToOriginWithBb) {
29988 bb = shiftToOriginWithBb;
29989 context.translate(-bb.x1, -bb.y1);
29990 }
29991
29992 if (prefix == null) {
29993 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29994
29995 if (ele.isEdge()) {
29996 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29997 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29998 }
29999 } else {
30000 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
30001 }
30002
30003 if (shiftToOriginWithBb) {
30004 context.translate(bb.x1, bb.y1);
30005 }
30006 };
30007
30008 CRp$4.getFontCache = function (context) {
30009 var cache;
30010 this.fontCaches = this.fontCaches || [];
30011
30012 for (var i = 0; i < this.fontCaches.length; i++) {
30013 cache = this.fontCaches[i];
30014
30015 if (cache.context === context) {
30016 return cache;
30017 }
30018 }
30019
30020 cache = {
30021 context: context
30022 };
30023 this.fontCaches.push(cache);
30024 return cache;
30025 }; // set up canvas context with font
30026 // returns transformed text string
30027
30028
30029 CRp$4.setupTextStyle = function (context, ele) {
30030 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
30031 // Font style
30032 var labelStyle = ele.pstyle('font-style').strValue;
30033 var labelSize = ele.pstyle('font-size').pfValue + 'px';
30034 var labelFamily = ele.pstyle('font-family').strValue;
30035 var labelWeight = ele.pstyle('font-weight').strValue;
30036 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
30037 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
30038 var color = ele.pstyle('color').value;
30039 var outlineColor = ele.pstyle('text-outline-color').value;
30040 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
30041 context.lineJoin = 'round'; // so text outlines aren't jagged
30042
30043 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30044 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
30045 }; // TODO ensure re-used
30046
30047
30048 function roundRect(ctx, x, y, width, height) {
30049 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
30050 ctx.beginPath();
30051 ctx.moveTo(x + radius, y);
30052 ctx.lineTo(x + width - radius, y);
30053 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
30054 ctx.lineTo(x + width, y + height - radius);
30055 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
30056 ctx.lineTo(x + radius, y + height);
30057 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
30058 ctx.lineTo(x, y + radius);
30059 ctx.quadraticCurveTo(x, y, x + radius, y);
30060 ctx.closePath();
30061 ctx.fill();
30062 }
30063
30064 CRp$4.getTextAngle = function (ele, prefix) {
30065 var theta;
30066 var _p = ele._private;
30067 var rscratch = _p.rscratch;
30068 var pdash = prefix ? prefix + '-' : '';
30069 var rotation = ele.pstyle(pdash + 'text-rotation');
30070 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
30071
30072 if (rotation.strValue === 'autorotate') {
30073 theta = ele.isEdge() ? textAngle : 0;
30074 } else if (rotation.strValue === 'none') {
30075 theta = 0;
30076 } else {
30077 theta = rotation.pfValue;
30078 }
30079
30080 return theta;
30081 };
30082
30083 CRp$4.drawText = function (context, ele, prefix) {
30084 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30085 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30086 var _p = ele._private;
30087 var rscratch = _p.rscratch;
30088 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
30089
30090 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
30091 return;
30092 } // use 'main' as an alias for the main label (i.e. null prefix)
30093
30094
30095 if (prefix === 'main') {
30096 prefix = null;
30097 }
30098
30099 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
30100 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
30101 var orgTextX, orgTextY; // used for rotation
30102
30103 var text = this.getLabelText(ele, prefix);
30104
30105 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
30106 this.setupTextStyle(context, ele, useEleOpacity);
30107 var pdash = prefix ? prefix + '-' : '';
30108 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
30109 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
30110 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
30111 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
30112 var isEdge = ele.isEdge();
30113 var halign = ele.pstyle('text-halign').value;
30114 var valign = ele.pstyle('text-valign').value;
30115
30116 if (isEdge) {
30117 halign = 'center';
30118 valign = 'center';
30119 }
30120
30121 textX += marginX;
30122 textY += marginY;
30123 var theta;
30124
30125 if (!applyRotation) {
30126 theta = 0;
30127 } else {
30128 theta = this.getTextAngle(ele, prefix);
30129 }
30130
30131 if (theta !== 0) {
30132 orgTextX = textX;
30133 orgTextY = textY;
30134 context.translate(orgTextX, orgTextY);
30135 context.rotate(theta);
30136 textX = 0;
30137 textY = 0;
30138 }
30139
30140 switch (valign) {
30141 case 'top':
30142 break;
30143
30144 case 'center':
30145 textY += textH / 2;
30146 break;
30147
30148 case 'bottom':
30149 textY += textH;
30150 break;
30151 }
30152
30153 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
30154 var borderOpacity = ele.pstyle('text-border-opacity').value;
30155 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
30156 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
30157
30158 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
30159 var bgX = textX - backgroundPadding;
30160
30161 switch (halign) {
30162 case 'left':
30163 bgX -= textW;
30164 break;
30165
30166 case 'center':
30167 bgX -= textW / 2;
30168 break;
30169 }
30170
30171 var bgY = textY - textH - backgroundPadding;
30172 var bgW = textW + 2 * backgroundPadding;
30173 var bgH = textH + 2 * backgroundPadding;
30174
30175 if (backgroundOpacity > 0) {
30176 var textFill = context.fillStyle;
30177 var textBackgroundColor = ele.pstyle('text-background-color').value;
30178 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
30179 var styleShape = ele.pstyle('text-background-shape').strValue;
30180
30181 if (styleShape.indexOf('round') === 0) {
30182 roundRect(context, bgX, bgY, bgW, bgH, 2);
30183 } else {
30184 context.fillRect(bgX, bgY, bgW, bgH);
30185 }
30186
30187 context.fillStyle = textFill;
30188 }
30189
30190 if (textBorderWidth > 0 && borderOpacity > 0) {
30191 var textStroke = context.strokeStyle;
30192 var textLineWidth = context.lineWidth;
30193 var textBorderColor = ele.pstyle('text-border-color').value;
30194 var textBorderStyle = ele.pstyle('text-border-style').value;
30195 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
30196 context.lineWidth = textBorderWidth;
30197
30198 if (context.setLineDash) {
30199 // for very outofdate browsers
30200 switch (textBorderStyle) {
30201 case 'dotted':
30202 context.setLineDash([1, 1]);
30203 break;
30204
30205 case 'dashed':
30206 context.setLineDash([4, 2]);
30207 break;
30208
30209 case 'double':
30210 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
30211
30212 context.setLineDash([]);
30213 break;
30214
30215 case 'solid':
30216 context.setLineDash([]);
30217 break;
30218 }
30219 }
30220
30221 context.strokeRect(bgX, bgY, bgW, bgH);
30222
30223 if (textBorderStyle === 'double') {
30224 var whiteWidth = textBorderWidth / 2;
30225 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
30226 }
30227
30228 if (context.setLineDash) {
30229 // for very outofdate browsers
30230 context.setLineDash([]);
30231 }
30232
30233 context.lineWidth = textLineWidth;
30234 context.strokeStyle = textStroke;
30235 }
30236 }
30237
30238 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
30239
30240 if (lineWidth > 0) {
30241 context.lineWidth = lineWidth;
30242 }
30243
30244 if (ele.pstyle('text-wrap').value === 'wrap') {
30245 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
30246 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
30247 var halfTextW = textW / 2;
30248 var justification = this.getLabelJustification(ele);
30249
30250 if (justification === 'auto') ; else if (halign === 'left') {
30251 // auto justification : right
30252 if (justification === 'left') {
30253 textX += -textW;
30254 } else if (justification === 'center') {
30255 textX += -halfTextW;
30256 } // else same as auto
30257
30258 } else if (halign === 'center') {
30259 // auto justfication : center
30260 if (justification === 'left') {
30261 textX += -halfTextW;
30262 } else if (justification === 'right') {
30263 textX += halfTextW;
30264 } // else same as auto
30265
30266 } else if (halign === 'right') {
30267 // auto justification : left
30268 if (justification === 'center') {
30269 textX += halfTextW;
30270 } else if (justification === 'right') {
30271 textX += textW;
30272 } // else same as auto
30273
30274 }
30275
30276 switch (valign) {
30277 case 'top':
30278 textY -= (lines.length - 1) * lineHeight;
30279 break;
30280
30281 case 'center':
30282 case 'bottom':
30283 textY -= (lines.length - 1) * lineHeight;
30284 break;
30285 }
30286
30287 for (var l = 0; l < lines.length; l++) {
30288 if (lineWidth > 0) {
30289 context.strokeText(lines[l], textX, textY);
30290 }
30291
30292 context.fillText(lines[l], textX, textY);
30293 textY += lineHeight;
30294 }
30295 } else {
30296 if (lineWidth > 0) {
30297 context.strokeText(text, textX, textY);
30298 }
30299
30300 context.fillText(text, textX, textY);
30301 }
30302
30303 if (theta !== 0) {
30304 context.rotate(-theta);
30305 context.translate(-orgTextX, -orgTextY);
30306 }
30307 }
30308 };
30309
30310 /* global Path2D */
30311 var CRp$5 = {};
30312
30313 CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
30314 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
30315 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
30316 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
30317 var r = this;
30318 var nodeWidth, nodeHeight;
30319 var _p = node._private;
30320 var rs = _p.rscratch;
30321 var pos = node.position();
30322
30323 if (!number(pos.x) || !number(pos.y)) {
30324 return; // can't draw node with undefined position
30325 }
30326
30327 if (shouldDrawOpacity && !node.visible()) {
30328 return;
30329 }
30330
30331 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
30332 var usePaths = r.usePaths();
30333 var path;
30334 var pathCacheHit = false;
30335 var padding = node.padding();
30336 nodeWidth = node.width() + 2 * padding;
30337 nodeHeight = node.height() + 2 * padding; //
30338 // setup shift
30339
30340 var bb;
30341
30342 if (shiftToOriginWithBb) {
30343 bb = shiftToOriginWithBb;
30344 context.translate(-bb.x1, -bb.y1);
30345 } //
30346 // load bg image
30347
30348
30349 var bgImgProp = node.pstyle('background-image');
30350 var urls = bgImgProp.value;
30351 var urlDefined = new Array(urls.length);
30352 var image = new Array(urls.length);
30353 var numImages = 0;
30354
30355 for (var i = 0; i < urls.length; i++) {
30356 var url = urls[i];
30357 var defd = urlDefined[i] = url != null && url !== 'none';
30358
30359 if (defd) {
30360 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
30361 numImages++; // get image, and if not loaded then ask to redraw when later loaded
30362
30363 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
30364 _p.backgroundTimestamp = Date.now();
30365 node.emitAndNotify('background');
30366 });
30367 }
30368 } //
30369 // setup styles
30370
30371
30372 var darkness = node.pstyle('background-blacken').value;
30373 var borderWidth = node.pstyle('border-width').pfValue;
30374 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
30375 var borderColor = node.pstyle('border-color').value;
30376 var borderStyle = node.pstyle('border-style').value;
30377 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
30378 context.lineJoin = 'miter'; // so borders are square with the node shape
30379
30380 var setupShapeColor = function setupShapeColor() {
30381 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
30382 r.eleFillStyle(context, node, bgOpy);
30383 };
30384
30385 var setupBorderColor = function setupBorderColor() {
30386 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
30387 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
30388 }; //
30389 // setup shape
30390
30391
30392 var styleShape = node.pstyle('shape').strValue;
30393 var shapePts = node.pstyle('shape-polygon-points').pfValue;
30394
30395 if (usePaths) {
30396 context.translate(pos.x, pos.y);
30397 var pathCache = r.nodePathCache = r.nodePathCache || [];
30398 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
30399 var cachedPath = pathCache[key];
30400
30401 if (cachedPath != null) {
30402 path = cachedPath;
30403 pathCacheHit = true;
30404 rs.pathCache = path;
30405 } else {
30406 path = new Path2D();
30407 pathCache[key] = rs.pathCache = path;
30408 }
30409 }
30410
30411 var drawShape = function drawShape() {
30412 if (!pathCacheHit) {
30413 var npos = pos;
30414
30415 if (usePaths) {
30416 npos = {
30417 x: 0,
30418 y: 0
30419 };
30420 }
30421
30422 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
30423 }
30424
30425 if (usePaths) {
30426 context.fill(path);
30427 } else {
30428 context.fill();
30429 }
30430 };
30431
30432 var drawImages = function drawImages() {
30433 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30434 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
30435 var prevBging = _p.backgrounding;
30436 var totalCompleted = 0;
30437
30438 for (var _i = 0; _i < image.length; _i++) {
30439 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
30440
30441 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
30442 totalCompleted++;
30443 continue;
30444 }
30445
30446 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
30447 totalCompleted++;
30448 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
30449 }
30450 }
30451
30452 _p.backgrounding = !(totalCompleted === numImages);
30453
30454 if (prevBging !== _p.backgrounding) {
30455 // update style b/c :backgrounding state changed
30456 node.updateStyle(false);
30457 }
30458 };
30459
30460 var drawPie = function drawPie() {
30461 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
30462 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
30463
30464 if (r.hasPie(node)) {
30465 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
30466
30467 if (redrawShape) {
30468 if (!usePaths) {
30469 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
30470 }
30471 }
30472 }
30473 };
30474
30475 var darken = function darken() {
30476 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
30477 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
30478 var c = darkness > 0 ? 0 : 255;
30479
30480 if (darkness !== 0) {
30481 r.colorFillStyle(context, c, c, c, opacity);
30482
30483 if (usePaths) {
30484 context.fill(path);
30485 } else {
30486 context.fill();
30487 }
30488 }
30489 };
30490
30491 var drawBorder = function drawBorder() {
30492 if (borderWidth > 0) {
30493 context.lineWidth = borderWidth;
30494 context.lineCap = 'butt';
30495
30496 if (context.setLineDash) {
30497 // for very outofdate browsers
30498 switch (borderStyle) {
30499 case 'dotted':
30500 context.setLineDash([1, 1]);
30501 break;
30502
30503 case 'dashed':
30504 context.setLineDash([4, 2]);
30505 break;
30506
30507 case 'solid':
30508 case 'double':
30509 context.setLineDash([]);
30510 break;
30511 }
30512 }
30513
30514 if (usePaths) {
30515 context.stroke(path);
30516 } else {
30517 context.stroke();
30518 }
30519
30520 if (borderStyle === 'double') {
30521 context.lineWidth = borderWidth / 3;
30522 var gco = context.globalCompositeOperation;
30523 context.globalCompositeOperation = 'destination-out';
30524
30525 if (usePaths) {
30526 context.stroke(path);
30527 } else {
30528 context.stroke();
30529 }
30530
30531 context.globalCompositeOperation = gco;
30532 } // reset in case we changed the border style
30533
30534
30535 if (context.setLineDash) {
30536 // for very outofdate browsers
30537 context.setLineDash([]);
30538 }
30539 }
30540 };
30541
30542 var drawOverlay = function drawOverlay() {
30543 if (shouldDrawOverlay) {
30544 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
30545 }
30546 };
30547
30548 var drawText = function drawText() {
30549 r.drawElementText(context, node, null, drawLabel);
30550 };
30551
30552 var ghost = node.pstyle('ghost').value === 'yes';
30553
30554 if (ghost) {
30555 var gx = node.pstyle('ghost-offset-x').pfValue;
30556 var gy = node.pstyle('ghost-offset-y').pfValue;
30557 var ghostOpacity = node.pstyle('ghost-opacity').value;
30558 var effGhostOpacity = ghostOpacity * eleOpacity;
30559 context.translate(gx, gy);
30560 setupShapeColor(ghostOpacity * bgOpacity);
30561 drawShape();
30562 drawImages(effGhostOpacity, true);
30563 setupBorderColor(ghostOpacity * borderOpacity);
30564 drawBorder();
30565 drawPie(darkness !== 0 || borderWidth !== 0);
30566 drawImages(effGhostOpacity, false);
30567 darken(effGhostOpacity);
30568 context.translate(-gx, -gy);
30569 }
30570
30571 setupShapeColor();
30572 drawShape();
30573 drawImages(eleOpacity, true);
30574 setupBorderColor();
30575 drawBorder();
30576 drawPie(darkness !== 0 || borderWidth !== 0);
30577 drawImages(eleOpacity, false);
30578 darken();
30579
30580 if (usePaths) {
30581 context.translate(-pos.x, -pos.y);
30582 }
30583
30584 drawText();
30585 drawOverlay(); //
30586 // clean up shift
30587
30588 if (shiftToOriginWithBb) {
30589 context.translate(bb.x1, bb.y1);
30590 }
30591 };
30592
30593 CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
30594 var r = this;
30595
30596 if (!node.visible()) {
30597 return;
30598 }
30599
30600 var overlayPadding = node.pstyle('overlay-padding').pfValue;
30601 var overlayOpacity = node.pstyle('overlay-opacity').value;
30602 var overlayColor = node.pstyle('overlay-color').value;
30603
30604 if (overlayOpacity > 0) {
30605 pos = pos || node.position();
30606
30607 if (nodeWidth == null || nodeHeight == null) {
30608 var padding = node.padding();
30609 nodeWidth = node.width() + 2 * padding;
30610 nodeHeight = node.height() + 2 * padding;
30611 }
30612
30613 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
30614 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
30615 context.fill();
30616 }
30617 }; // does the node have at least one pie piece?
30618
30619
30620 CRp$5.hasPie = function (node) {
30621 node = node[0]; // ensure ele ref
30622
30623 return node._private.hasPie;
30624 };
30625
30626 CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
30627 node = node[0]; // ensure ele ref
30628
30629 pos = pos || node.position();
30630 var cyStyle = node.cy().style();
30631 var pieSize = node.pstyle('pie-size');
30632 var x = pos.x;
30633 var y = pos.y;
30634 var nodeW = node.width();
30635 var nodeH = node.height();
30636 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
30637
30638 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
30639
30640 var usePaths = this.usePaths();
30641
30642 if (usePaths) {
30643 x = 0;
30644 y = 0;
30645 }
30646
30647 if (pieSize.units === '%') {
30648 radius = radius * pieSize.pfValue;
30649 } else if (pieSize.pfValue !== undefined) {
30650 radius = pieSize.pfValue / 2;
30651 }
30652
30653 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
30654 // 1..N
30655 var size = node.pstyle('pie-' + i + '-background-size').value;
30656 var color = node.pstyle('pie-' + i + '-background-color').value;
30657 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
30658 var percent = size / 100; // map integer range [0, 100] to [0, 1]
30659 // percent can't push beyond 1
30660
30661 if (percent + lastPercent > 1) {
30662 percent = 1 - lastPercent;
30663 }
30664
30665 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
30666
30667 var angleDelta = 2 * Math.PI * percent;
30668 var angleEnd = angleStart + angleDelta; // ignore if
30669 // - zero size
30670 // - we're already beyond the full circle
30671 // - adding the current slice would go beyond the full circle
30672
30673 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
30674 continue;
30675 }
30676
30677 context.beginPath();
30678 context.moveTo(x, y);
30679 context.arc(x, y, radius, angleStart, angleEnd);
30680 context.closePath();
30681 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
30682 context.fill();
30683 lastPercent += percent;
30684 }
30685 };
30686
30687 var CRp$6 = {};
30688 var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
30689
30690 CRp$6.getPixelRatio = function () {
30691 var context = this.data.contexts[0];
30692
30693 if (this.forcedPixelRatio != null) {
30694 return this.forcedPixelRatio;
30695 }
30696
30697 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
30698 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
30699 };
30700
30701 CRp$6.paintCache = function (context) {
30702 var caches = this.paintCaches = this.paintCaches || [];
30703 var needToCreateCache = true;
30704 var cache;
30705
30706 for (var i = 0; i < caches.length; i++) {
30707 cache = caches[i];
30708
30709 if (cache.context === context) {
30710 needToCreateCache = false;
30711 break;
30712 }
30713 }
30714
30715 if (needToCreateCache) {
30716 cache = {
30717 context: context
30718 };
30719 caches.push(cache);
30720 }
30721
30722 return cache;
30723 };
30724
30725 CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
30726 var gradientStyle;
30727 var usePaths = this.usePaths();
30728 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
30729 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
30730
30731 if (fill === 'radial-gradient') {
30732 if (ele.isEdge()) {
30733 var start = ele.sourceEndpoint(),
30734 end = ele.targetEndpoint(),
30735 mid = ele.midpoint();
30736 var d1 = dist(start, mid);
30737 var d2 = dist(end, mid);
30738 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
30739 } else {
30740 var pos = usePaths ? {
30741 x: 0,
30742 y: 0
30743 } : ele.position(),
30744 width = ele.paddedWidth(),
30745 height = ele.paddedHeight();
30746 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
30747 }
30748 } else {
30749 if (ele.isEdge()) {
30750 var _start = ele.sourceEndpoint(),
30751 _end = ele.targetEndpoint();
30752
30753 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30754 } else {
30755 var _pos = usePaths ? {
30756 x: 0,
30757 y: 0
30758 } : ele.position(),
30759 _width = ele.paddedWidth(),
30760 _height = ele.paddedHeight(),
30761 halfWidth = _width / 2,
30762 halfHeight = _height / 2;
30763
30764 var direction = ele.pstyle('background-gradient-direction').value;
30765
30766 switch (direction) {
30767 case 'to-bottom':
30768 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30769 break;
30770
30771 case 'to-top':
30772 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30773 break;
30774
30775 case 'to-left':
30776 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30777 break;
30778
30779 case 'to-right':
30780 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30781 break;
30782
30783 case 'to-bottom-right':
30784 case 'to-right-bottom':
30785 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30786 break;
30787
30788 case 'to-top-right':
30789 case 'to-right-top':
30790 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30791 break;
30792
30793 case 'to-bottom-left':
30794 case 'to-left-bottom':
30795 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30796 break;
30797
30798 case 'to-top-left':
30799 case 'to-left-top':
30800 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30801 break;
30802 }
30803 }
30804 }
30805
30806 if (!gradientStyle) return null; // invalid gradient style
30807
30808 var hasPositions = positions.length === colors.length;
30809 var length = colors.length;
30810
30811 for (var i = 0; i < length; i++) {
30812 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30813 }
30814
30815 return gradientStyle;
30816 };
30817
30818 CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30819 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30820 if (!gradientStyle) return null; // error
30821
30822 context.fillStyle = gradientStyle;
30823 };
30824
30825 CRp$6.colorFillStyle = function (context, r, g, b, a) {
30826 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30827 // var cache = this.paintCache(context);
30828 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30829 // if( cache.fillStyle !== fillStyle ){
30830 // context.fillStyle = cache.fillStyle = fillStyle;
30831 // }
30832 };
30833
30834 CRp$6.eleFillStyle = function (context, ele, opacity) {
30835 var backgroundFill = ele.pstyle('background-fill').value;
30836
30837 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30838 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30839 } else {
30840 var backgroundColor = ele.pstyle('background-color').value;
30841 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30842 }
30843 };
30844
30845 CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30846 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30847 if (!gradientStyle) return null; // error
30848
30849 context.strokeStyle = gradientStyle;
30850 };
30851
30852 CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30853 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30854 // var cache = this.paintCache(context);
30855 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30856 // if( cache.strokeStyle !== strokeStyle ){
30857 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30858 // }
30859 };
30860
30861 CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30862 var lineFill = ele.pstyle('line-fill').value;
30863
30864 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30865 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30866 } else {
30867 var lineColor = ele.pstyle('line-color').value;
30868 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30869 }
30870 }; // Resize canvas
30871
30872
30873 CRp$6.matchCanvasSize = function (container) {
30874 var r = this;
30875 var data = r.data;
30876 var bb = r.findContainerClientCoords();
30877 var width = bb[2];
30878 var height = bb[3];
30879 var pixelRatio = r.getPixelRatio();
30880 var mbPxRatio = r.motionBlurPxRatio;
30881
30882 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30883 pixelRatio = mbPxRatio;
30884 }
30885
30886 var canvasWidth = width * pixelRatio;
30887 var canvasHeight = height * pixelRatio;
30888 var canvas;
30889
30890 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30891 return; // save cycles if same
30892 }
30893
30894 r.fontCaches = null; // resizing resets the style
30895
30896 var canvasContainer = data.canvasContainer;
30897 canvasContainer.style.width = width + 'px';
30898 canvasContainer.style.height = height + 'px';
30899
30900 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30901 canvas = data.canvases[i];
30902 canvas.width = canvasWidth;
30903 canvas.height = canvasHeight;
30904 canvas.style.width = width + 'px';
30905 canvas.style.height = height + 'px';
30906 }
30907
30908 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30909 canvas = data.bufferCanvases[i];
30910 canvas.width = canvasWidth;
30911 canvas.height = canvasHeight;
30912 canvas.style.width = width + 'px';
30913 canvas.style.height = height + 'px';
30914 }
30915
30916 r.textureMult = 1;
30917
30918 if (pixelRatio <= 1) {
30919 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30920 r.textureMult = 2;
30921 canvas.width = canvasWidth * r.textureMult;
30922 canvas.height = canvasHeight * r.textureMult;
30923 }
30924
30925 r.canvasWidth = canvasWidth;
30926 r.canvasHeight = canvasHeight;
30927 };
30928
30929 CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30930 this.render({
30931 forcedContext: cxt,
30932 forcedZoom: zoom,
30933 forcedPan: pan,
30934 drawAllLayers: true,
30935 forcedPxRatio: pxRatio
30936 });
30937 };
30938
30939 CRp$6.render = function (options) {
30940 options = options || staticEmptyObject();
30941 var forcedContext = options.forcedContext;
30942 var drawAllLayers = options.drawAllLayers;
30943 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30944 var forcedZoom = options.forcedZoom;
30945 var forcedPan = options.forcedPan;
30946 var r = this;
30947 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30948 var cy = r.cy;
30949 var data = r.data;
30950 var needDraw = data.canvasNeedsRedraw;
30951 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30952 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30953 var mbPxRatio = r.motionBlurPxRatio;
30954 var hasCompoundNodes = cy.hasCompoundNodes();
30955 var inNodeDragGesture = r.hoverData.draggingEles;
30956 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30957 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30958 var motionBlurFadeEffect = motionBlur;
30959
30960 if (!forcedContext) {
30961 if (r.prevPxRatio !== pixelRatio) {
30962 r.invalidateContainerClientCoordsCache();
30963 r.matchCanvasSize(r.container);
30964 r.redrawHint('eles', true);
30965 r.redrawHint('drag', true);
30966 }
30967
30968 r.prevPxRatio = pixelRatio;
30969 }
30970
30971 if (!forcedContext && r.motionBlurTimeout) {
30972 clearTimeout(r.motionBlurTimeout);
30973 }
30974
30975 if (motionBlur) {
30976 if (r.mbFrames == null) {
30977 r.mbFrames = 0;
30978 }
30979
30980 r.mbFrames++;
30981
30982 if (r.mbFrames < 3) {
30983 // need several frames before even high quality motionblur
30984 motionBlurFadeEffect = false;
30985 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30986
30987
30988 if (r.mbFrames > r.minMbLowQualFrames) {
30989 //r.fullQualityMb = false;
30990 r.motionBlurPxRatio = r.mbPxRBlurry;
30991 }
30992 }
30993
30994 if (r.clearingMotionBlur) {
30995 r.motionBlurPxRatio = 1;
30996 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30997 // because a rogue async texture frame would clear needDraw
30998
30999
31000 if (r.textureDrawLastFrame && !textureDraw) {
31001 needDraw[r.NODE] = true;
31002 needDraw[r.SELECT_BOX] = true;
31003 }
31004
31005 var style = cy.style();
31006 var zoom = cy.zoom();
31007 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
31008 var pan = cy.pan();
31009 var effectivePan = {
31010 x: pan.x,
31011 y: pan.y
31012 };
31013 var vp = {
31014 zoom: zoom,
31015 pan: {
31016 x: pan.x,
31017 y: pan.y
31018 }
31019 };
31020 var prevVp = r.prevViewport;
31021 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)
31022
31023 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
31024 r.motionBlurPxRatio = 1;
31025 }
31026
31027 if (forcedPan) {
31028 effectivePan = forcedPan;
31029 } // apply pixel ratio
31030
31031
31032 effectiveZoom *= pixelRatio;
31033 effectivePan.x *= pixelRatio;
31034 effectivePan.y *= pixelRatio;
31035 var eles = r.getCachedZSortedEles();
31036
31037 function mbclear(context, x, y, w, h) {
31038 var gco = context.globalCompositeOperation;
31039 context.globalCompositeOperation = 'destination-out';
31040 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
31041 context.fillRect(x, y, w, h);
31042 context.globalCompositeOperation = gco;
31043 }
31044
31045 function setContextTransform(context, clear) {
31046 var ePan, eZoom, w, h;
31047
31048 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
31049 ePan = {
31050 x: pan.x * mbPxRatio,
31051 y: pan.y * mbPxRatio
31052 };
31053 eZoom = zoom * mbPxRatio;
31054 w = r.canvasWidth * mbPxRatio;
31055 h = r.canvasHeight * mbPxRatio;
31056 } else {
31057 ePan = effectivePan;
31058 eZoom = effectiveZoom;
31059 w = r.canvasWidth;
31060 h = r.canvasHeight;
31061 }
31062
31063 context.setTransform(1, 0, 0, 1, 0, 0);
31064
31065 if (clear === 'motionBlur') {
31066 mbclear(context, 0, 0, w, h);
31067 } else if (!forcedContext && (clear === undefined || clear)) {
31068 context.clearRect(0, 0, w, h);
31069 }
31070
31071 if (!drawAllLayers) {
31072 context.translate(ePan.x, ePan.y);
31073 context.scale(eZoom, eZoom);
31074 }
31075
31076 if (forcedPan) {
31077 context.translate(forcedPan.x, forcedPan.y);
31078 }
31079
31080 if (forcedZoom) {
31081 context.scale(forcedZoom, forcedZoom);
31082 }
31083 }
31084
31085 if (!textureDraw) {
31086 r.textureDrawLastFrame = false;
31087 }
31088
31089 if (textureDraw) {
31090 r.textureDrawLastFrame = true;
31091
31092 if (!r.textureCache) {
31093 r.textureCache = {};
31094 r.textureCache.bb = cy.mutableElements().boundingBox();
31095 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
31096 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
31097 cxt.setTransform(1, 0, 0, 1, 0, 0);
31098 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
31099 r.render({
31100 forcedContext: cxt,
31101 drawOnlyNodeLayer: true,
31102 forcedPxRatio: pixelRatio * r.textureMult
31103 });
31104 var vp = r.textureCache.viewport = {
31105 zoom: cy.zoom(),
31106 pan: cy.pan(),
31107 width: r.canvasWidth,
31108 height: r.canvasHeight
31109 };
31110 vp.mpan = {
31111 x: (0 - vp.pan.x) / vp.zoom,
31112 y: (0 - vp.pan.y) / vp.zoom
31113 };
31114 }
31115
31116 needDraw[r.DRAG] = false;
31117 needDraw[r.NODE] = false;
31118 var context = data.contexts[r.NODE];
31119 var texture = r.textureCache.texture;
31120 var vp = r.textureCache.viewport;
31121 context.setTransform(1, 0, 0, 1, 0, 0);
31122
31123 if (motionBlur) {
31124 mbclear(context, 0, 0, vp.width, vp.height);
31125 } else {
31126 context.clearRect(0, 0, vp.width, vp.height);
31127 }
31128
31129 var outsideBgColor = style.core('outside-texture-bg-color').value;
31130 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
31131 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
31132 context.fillRect(0, 0, vp.width, vp.height);
31133 var zoom = cy.zoom();
31134 setContextTransform(context, false);
31135 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
31136 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
31137 } else if (r.textureOnViewport && !forcedContext) {
31138 // clear the cache since we don't need it
31139 r.textureCache = null;
31140 }
31141
31142 var extent = cy.extent();
31143 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
31144 var hideEdges = r.hideEdgesOnViewport && vpManip;
31145 var needMbClear = [];
31146 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
31147
31148 if (needMbClear[r.NODE]) {
31149 r.clearedForMotionBlur[r.NODE] = true;
31150 }
31151
31152 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
31153
31154 if (needMbClear[r.DRAG]) {
31155 r.clearedForMotionBlur[r.DRAG] = true;
31156 }
31157
31158 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
31159 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
31160 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
31161 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
31162 setContextTransform(context, clear);
31163
31164 if (hideEdges) {
31165 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
31166 } else {
31167 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
31168 }
31169
31170 if (r.debug) {
31171 r.drawDebugPoints(context, eles.nondrag);
31172 }
31173
31174 if (!drawAllLayers && !motionBlur) {
31175 needDraw[r.NODE] = false;
31176 }
31177 }
31178
31179 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
31180 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
31181 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
31182 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
31183
31184 if (hideEdges) {
31185 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
31186 } else {
31187 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
31188 }
31189
31190 if (r.debug) {
31191 r.drawDebugPoints(context, eles.drag);
31192 }
31193
31194 if (!drawAllLayers && !motionBlur) {
31195 needDraw[r.DRAG] = false;
31196 }
31197 }
31198
31199 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
31200 var context = forcedContext || data.contexts[r.SELECT_BOX];
31201 setContextTransform(context);
31202
31203 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
31204 var zoom = r.cy.zoom();
31205 var borderWidth = style.core('selection-box-border-width').value / zoom;
31206 context.lineWidth = borderWidth;
31207 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 + ')';
31208 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31209
31210 if (borderWidth > 0) {
31211 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 + ')';
31212 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
31213 }
31214 }
31215
31216 if (data.bgActivePosistion && !r.hoverData.selecting) {
31217 var zoom = r.cy.zoom();
31218 var pos = data.bgActivePosistion;
31219 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 + ')';
31220 context.beginPath();
31221 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
31222 context.fill();
31223 }
31224
31225 var timeToRender = r.lastRedrawTime;
31226
31227 if (r.showFps && timeToRender) {
31228 timeToRender = Math.round(timeToRender);
31229 var fps = Math.round(1000 / timeToRender);
31230 context.setTransform(1, 0, 0, 1, 0, 0);
31231 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
31232 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
31233 context.lineWidth = 1;
31234 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
31235 var maxFps = 60;
31236 context.strokeRect(0, 30, 250, 20);
31237 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
31238 }
31239
31240 if (!drawAllLayers) {
31241 needDraw[r.SELECT_BOX] = false;
31242 }
31243 } // motionblur: blit rendered blurry frames
31244
31245
31246 if (motionBlur && mbPxRatio !== 1) {
31247 var cxtNode = data.contexts[r.NODE];
31248 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
31249 var cxtDrag = data.contexts[r.DRAG];
31250 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
31251
31252 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
31253 cxt.setTransform(1, 0, 0, 1, 0, 0);
31254
31255 if (needClear || !motionBlurFadeEffect) {
31256 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
31257 } else {
31258 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
31259 }
31260
31261 var pxr = mbPxRatio;
31262 cxt.drawImage(txt, // img
31263 0, 0, // sx, sy
31264 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
31265 0, 0, // x, y
31266 r.canvasWidth, r.canvasHeight // w, h
31267 );
31268 };
31269
31270 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
31271 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
31272 needDraw[r.NODE] = false;
31273 }
31274
31275 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
31276 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
31277 needDraw[r.DRAG] = false;
31278 }
31279 }
31280
31281 r.prevViewport = vp;
31282
31283 if (r.clearingMotionBlur) {
31284 r.clearingMotionBlur = false;
31285 r.motionBlurCleared = true;
31286 r.motionBlur = true;
31287 }
31288
31289 if (motionBlur) {
31290 r.motionBlurTimeout = setTimeout(function () {
31291 r.motionBlurTimeout = null;
31292 r.clearedForMotionBlur[r.NODE] = false;
31293 r.clearedForMotionBlur[r.DRAG] = false;
31294 r.motionBlur = false;
31295 r.clearingMotionBlur = !textureDraw;
31296 r.mbFrames = 0;
31297 needDraw[r.NODE] = true;
31298 needDraw[r.DRAG] = true;
31299 r.redraw();
31300 }, motionBlurDelay);
31301 }
31302
31303 if (!forcedContext) {
31304 cy.emit('render');
31305 }
31306 };
31307
31308 var CRp$7 = {}; // @O Polygon drawing
31309
31310 CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
31311 var halfW = width / 2;
31312 var halfH = height / 2;
31313
31314 if (context.beginPath) {
31315 context.beginPath();
31316 }
31317
31318 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
31319
31320 for (var i = 1; i < points.length / 2; i++) {
31321 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
31322 }
31323
31324 context.closePath();
31325 };
31326
31327 CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
31328 var halfW = width / 2;
31329 var halfH = height / 2;
31330 var cornerRadius = getRoundPolygonRadius(width, height);
31331
31332 if (context.beginPath) {
31333 context.beginPath();
31334 }
31335
31336 for (var _i = 0; _i < points.length / 4; _i++) {
31337 var sourceUv = void 0,
31338 destUv = void 0;
31339
31340 if (_i === 0) {
31341 sourceUv = points.length - 2;
31342 } else {
31343 sourceUv = _i * 4 - 2;
31344 }
31345
31346 destUv = _i * 4 + 2;
31347 var px = x + halfW * points[_i * 4];
31348 var py = y + halfH * points[_i * 4 + 1];
31349 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
31350 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
31351 var cp0x = px - offset * points[sourceUv];
31352 var cp0y = py - offset * points[sourceUv + 1];
31353 var cp1x = px + offset * points[destUv];
31354 var cp1y = py + offset * points[destUv + 1];
31355
31356 if (_i === 0) {
31357 context.moveTo(cp0x, cp0y);
31358 } else {
31359 context.lineTo(cp0x, cp0y);
31360 }
31361
31362 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
31363 }
31364
31365 context.closePath();
31366 }; // Round rectangle drawing
31367
31368
31369 CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
31370 var halfWidth = width / 2;
31371 var halfHeight = height / 2;
31372 var cornerRadius = getRoundRectangleRadius(width, height);
31373
31374 if (context.beginPath) {
31375 context.beginPath();
31376 } // Start at top middle
31377
31378
31379 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
31380
31381 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
31382
31383 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
31384
31385 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
31386
31387 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
31388
31389 context.lineTo(x, y - halfHeight);
31390 context.closePath();
31391 };
31392
31393 CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
31394 var halfWidth = width / 2;
31395 var halfHeight = height / 2;
31396 var cornerRadius = getRoundRectangleRadius(width, height);
31397
31398 if (context.beginPath) {
31399 context.beginPath();
31400 } // Start at top middle
31401
31402
31403 context.moveTo(x, y - halfHeight);
31404 context.lineTo(x + halfWidth, y - halfHeight);
31405 context.lineTo(x + halfWidth, y);
31406 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
31407 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
31408 context.lineTo(x - halfWidth, y - halfHeight);
31409 context.lineTo(x, y - halfHeight);
31410 context.closePath();
31411 };
31412
31413 CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
31414 var halfWidth = width / 2;
31415 var halfHeight = height / 2;
31416 var cornerLength = getCutRectangleCornerLength();
31417
31418 if (context.beginPath) {
31419 context.beginPath();
31420 }
31421
31422 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
31423 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
31424 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
31425 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
31426 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
31427 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
31428 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
31429 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
31430 context.closePath();
31431 };
31432
31433 CRp$7.drawBarrelPath = function (context, x, y, width, height) {
31434 var halfWidth = width / 2;
31435 var halfHeight = height / 2;
31436 var xBegin = x - halfWidth;
31437 var xEnd = x + halfWidth;
31438 var yBegin = y - halfHeight;
31439 var yEnd = y + halfHeight;
31440 var barrelCurveConstants = getBarrelCurveConstants(width, height);
31441 var wOffset = barrelCurveConstants.widthOffset;
31442 var hOffset = barrelCurveConstants.heightOffset;
31443 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
31444
31445 if (context.beginPath) {
31446 context.beginPath();
31447 }
31448
31449 context.moveTo(xBegin, yBegin + hOffset);
31450 context.lineTo(xBegin, yEnd - hOffset);
31451 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
31452 context.lineTo(xEnd - wOffset, yEnd);
31453 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
31454 context.lineTo(xEnd, yBegin + hOffset);
31455 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
31456 context.lineTo(xBegin + wOffset, yBegin);
31457 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
31458 context.closePath();
31459 };
31460
31461 var sin0 = Math.sin(0);
31462 var cos0 = Math.cos(0);
31463 var sin = {};
31464 var cos = {};
31465 var ellipseStepSize = Math.PI / 40;
31466
31467 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31468 sin[i] = Math.sin(i);
31469 cos[i] = Math.cos(i);
31470 }
31471
31472 CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
31473 if (context.beginPath) {
31474 context.beginPath();
31475 }
31476
31477 if (context.ellipse) {
31478 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
31479 } else {
31480 var xPos, yPos;
31481 var rw = width / 2;
31482 var rh = height / 2;
31483
31484 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
31485 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
31486 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
31487
31488 if (i === 0) {
31489 context.moveTo(xPos, yPos);
31490 } else {
31491 context.lineTo(xPos, yPos);
31492 }
31493 }
31494 }
31495
31496 context.closePath();
31497 };
31498
31499 /* global atob, ArrayBuffer, Uint8Array, Blob */
31500 var CRp$8 = {};
31501
31502 CRp$8.createBuffer = function (w, h) {
31503 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
31504
31505 buffer.width = w;
31506 buffer.height = h;
31507 return [buffer, buffer.getContext('2d')];
31508 };
31509
31510 CRp$8.bufferCanvasImage = function (options) {
31511 var cy = this.cy;
31512 var eles = cy.mutableElements();
31513 var bb = eles.boundingBox();
31514 var ctrRect = this.findContainerClientCoords();
31515 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
31516 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
31517 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
31518 var pxRatio = this.getPixelRatio();
31519 var scale = 1;
31520
31521 if (options.scale !== undefined) {
31522 width *= options.scale;
31523 height *= options.scale;
31524 scale = options.scale;
31525 } else if (specdMaxDims) {
31526 var maxScaleW = Infinity;
31527 var maxScaleH = Infinity;
31528
31529 if (number(options.maxWidth)) {
31530 maxScaleW = scale * options.maxWidth / width;
31531 }
31532
31533 if (number(options.maxHeight)) {
31534 maxScaleH = scale * options.maxHeight / height;
31535 }
31536
31537 scale = Math.min(maxScaleW, maxScaleH);
31538 width *= scale;
31539 height *= scale;
31540 }
31541
31542 if (!specdMaxDims) {
31543 width *= pxRatio;
31544 height *= pxRatio;
31545 scale *= pxRatio;
31546 }
31547
31548 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
31549
31550 buffCanvas.width = width;
31551 buffCanvas.height = height;
31552 buffCanvas.style.width = width + 'px';
31553 buffCanvas.style.height = height + 'px';
31554 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
31555
31556 if (width > 0 && height > 0) {
31557 buffCxt.clearRect(0, 0, width, height);
31558 buffCxt.globalCompositeOperation = 'source-over';
31559 var zsortedEles = this.getCachedZSortedEles();
31560
31561 if (options.full) {
31562 // draw the full bounds of the graph
31563 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
31564 buffCxt.scale(scale, scale);
31565 this.drawElements(buffCxt, zsortedEles);
31566 buffCxt.scale(1 / scale, 1 / scale);
31567 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
31568 } else {
31569 // draw the current view
31570 var pan = cy.pan();
31571 var translation = {
31572 x: pan.x * scale,
31573 y: pan.y * scale
31574 };
31575 scale *= cy.zoom();
31576 buffCxt.translate(translation.x, translation.y);
31577 buffCxt.scale(scale, scale);
31578 this.drawElements(buffCxt, zsortedEles);
31579 buffCxt.scale(1 / scale, 1 / scale);
31580 buffCxt.translate(-translation.x, -translation.y);
31581 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
31582
31583
31584 if (options.bg) {
31585 buffCxt.globalCompositeOperation = 'destination-over';
31586 buffCxt.fillStyle = options.bg;
31587 buffCxt.rect(0, 0, width, height);
31588 buffCxt.fill();
31589 }
31590 }
31591
31592 return buffCanvas;
31593 };
31594
31595 function b64ToBlob(b64, mimeType) {
31596 var bytes = atob(b64);
31597 var buff = new ArrayBuffer(bytes.length);
31598 var buffUint8 = new Uint8Array(buff);
31599
31600 for (var i = 0; i < bytes.length; i++) {
31601 buffUint8[i] = bytes.charCodeAt(i);
31602 }
31603
31604 return new Blob([buff], {
31605 type: mimeType
31606 });
31607 }
31608
31609 function b64UriToB64(b64uri) {
31610 var i = b64uri.indexOf(',');
31611 return b64uri.substr(i + 1);
31612 }
31613
31614 function output(options, canvas, mimeType) {
31615 var getB64Uri = function getB64Uri() {
31616 return canvas.toDataURL(mimeType, options.quality);
31617 };
31618
31619 switch (options.output) {
31620 case 'blob-promise':
31621 return new Promise$1(function (resolve, reject) {
31622 try {
31623 canvas.toBlob(function (blob) {
31624 if (blob != null) {
31625 resolve(blob);
31626 } else {
31627 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
31628 }
31629 }, mimeType, options.quality);
31630 } catch (err) {
31631 reject(err);
31632 }
31633 });
31634
31635 case 'blob':
31636 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
31637
31638 case 'base64':
31639 return b64UriToB64(getB64Uri());
31640
31641 case 'base64uri':
31642 default:
31643 return getB64Uri();
31644 }
31645 }
31646
31647 CRp$8.png = function (options) {
31648 return output(options, this.bufferCanvasImage(options), 'image/png');
31649 };
31650
31651 CRp$8.jpg = function (options) {
31652 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
31653 };
31654
31655 var CRp$9 = {};
31656
31657 CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
31658 switch (name) {
31659 case 'ellipse':
31660 return this.drawEllipsePath(context, centerX, centerY, width, height);
31661
31662 case 'polygon':
31663 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
31664
31665 case 'round-polygon':
31666 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
31667
31668 case 'roundrectangle':
31669 case 'round-rectangle':
31670 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
31671
31672 case 'cutrectangle':
31673 case 'cut-rectangle':
31674 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
31675
31676 case 'bottomroundrectangle':
31677 case 'bottom-round-rectangle':
31678 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
31679
31680 case 'barrel':
31681 return this.drawBarrelPath(context, centerX, centerY, width, height);
31682 }
31683 };
31684
31685 var CR = CanvasRenderer;
31686 var CRp$a = CanvasRenderer.prototype;
31687 CRp$a.CANVAS_LAYERS = 3; //
31688
31689 CRp$a.SELECT_BOX = 0;
31690 CRp$a.DRAG = 1;
31691 CRp$a.NODE = 2;
31692 CRp$a.BUFFER_COUNT = 3; //
31693
31694 CRp$a.TEXTURE_BUFFER = 0;
31695 CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
31696 CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
31697
31698 function CanvasRenderer(options) {
31699 var r = this;
31700 r.data = {
31701 canvases: new Array(CRp$a.CANVAS_LAYERS),
31702 contexts: new Array(CRp$a.CANVAS_LAYERS),
31703 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
31704 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
31705 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
31706 };
31707 var tapHlOffAttr = '-webkit-tap-highlight-color';
31708 var tapHlOffStyle = 'rgba(0,0,0,0)';
31709 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
31710
31711 var containerStyle = r.data.canvasContainer.style;
31712 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
31713 containerStyle.position = 'relative';
31714 containerStyle.zIndex = '0';
31715 containerStyle.overflow = 'hidden';
31716 var container = options.cy.container();
31717 container.appendChild(r.data.canvasContainer);
31718 container.style[tapHlOffAttr] = tapHlOffStyle;
31719 var styleMap = {
31720 '-webkit-user-select': 'none',
31721 '-moz-user-select': '-moz-none',
31722 'user-select': 'none',
31723 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
31724 'outline-style': 'none'
31725 };
31726
31727 if (ms()) {
31728 styleMap['-ms-touch-action'] = 'none';
31729 styleMap['touch-action'] = 'none';
31730 }
31731
31732 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
31733 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31734
31735 r.data.contexts[i] = canvas.getContext('2d');
31736 Object.keys(styleMap).forEach(function (k) {
31737 canvas.style[k] = styleMap[k];
31738 });
31739 canvas.style.position = 'absolute';
31740 canvas.setAttribute('data-id', 'layer' + i);
31741 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
31742 r.data.canvasContainer.appendChild(canvas);
31743 r.data.canvasNeedsRedraw[i] = false;
31744 }
31745
31746 r.data.topCanvas = r.data.canvases[0];
31747 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
31748 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
31749 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
31750
31751 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
31752 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
31753
31754 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31755 r.data.bufferCanvases[i].style.position = 'absolute';
31756 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31757 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31758 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31759 }
31760
31761 r.pathsEnabled = true;
31762 var emptyBb = makeBoundingBox();
31763
31764 var getBoxCenter = function getBoxCenter(bb) {
31765 return {
31766 x: (bb.x1 + bb.x2) / 2,
31767 y: (bb.y1 + bb.y2) / 2
31768 };
31769 };
31770
31771 var getCenterOffset = function getCenterOffset(bb) {
31772 return {
31773 x: -bb.w / 2,
31774 y: -bb.h / 2
31775 };
31776 };
31777
31778 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31779 var _p = ele[0]._private;
31780 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31781 return !same;
31782 };
31783
31784 var getStyleKey = function getStyleKey(ele) {
31785 return ele[0]._private.nodeKey;
31786 };
31787
31788 var getLabelKey = function getLabelKey(ele) {
31789 return ele[0]._private.labelStyleKey;
31790 };
31791
31792 var getSourceLabelKey = function getSourceLabelKey(ele) {
31793 return ele[0]._private.sourceLabelStyleKey;
31794 };
31795
31796 var getTargetLabelKey = function getTargetLabelKey(ele) {
31797 return ele[0]._private.targetLabelStyleKey;
31798 };
31799
31800 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31801 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31802 };
31803
31804 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31805 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31806 };
31807
31808 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31809 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31810 };
31811
31812 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31813 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31814 };
31815
31816 var getElementBox = function getElementBox(ele) {
31817 ele.boundingBox();
31818 return ele[0]._private.bodyBounds;
31819 };
31820
31821 var getLabelBox = function getLabelBox(ele) {
31822 ele.boundingBox();
31823 return ele[0]._private.labelBounds.main || emptyBb;
31824 };
31825
31826 var getSourceLabelBox = function getSourceLabelBox(ele) {
31827 ele.boundingBox();
31828 return ele[0]._private.labelBounds.source || emptyBb;
31829 };
31830
31831 var getTargetLabelBox = function getTargetLabelBox(ele) {
31832 ele.boundingBox();
31833 return ele[0]._private.labelBounds.target || emptyBb;
31834 };
31835
31836 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31837 return scaledLabelShown;
31838 };
31839
31840 var getElementRotationPoint = function getElementRotationPoint(ele) {
31841 return getBoxCenter(getElementBox(ele));
31842 };
31843
31844 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31845 var pre = prefix ? prefix + '-' : '';
31846 return {
31847 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31848 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31849 };
31850 };
31851
31852 var getRsPt = function getRsPt(ele, x, y) {
31853 var rs = ele[0]._private.rscratch;
31854 return {
31855 x: rs[x],
31856 y: rs[y]
31857 };
31858 };
31859
31860 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31861 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31862 };
31863
31864 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31865 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31866 };
31867
31868 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31869 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31870 };
31871
31872 var getElementRotationOffset = function getElementRotationOffset(ele) {
31873 return getCenterOffset(getElementBox(ele));
31874 };
31875
31876 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31877 return getCenterOffset(getSourceLabelBox(ele));
31878 };
31879
31880 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31881 return getCenterOffset(getTargetLabelBox(ele));
31882 };
31883
31884 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31885 var bb = getLabelBox(ele);
31886 var p = getCenterOffset(getLabelBox(ele));
31887
31888 if (ele.isNode()) {
31889 switch (ele.pstyle('text-halign').value) {
31890 case 'left':
31891 p.x = -bb.w;
31892 break;
31893
31894 case 'right':
31895 p.x = 0;
31896 break;
31897 }
31898
31899 switch (ele.pstyle('text-valign').value) {
31900 case 'top':
31901 p.y = -bb.h;
31902 break;
31903
31904 case 'bottom':
31905 p.y = 0;
31906 break;
31907 }
31908 }
31909
31910 return p;
31911 };
31912
31913 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31914 getKey: getStyleKey,
31915 doesEleInvalidateKey: backgroundTimestampHasChanged,
31916 drawElement: drawElement,
31917 getBoundingBox: getElementBox,
31918 getRotationPoint: getElementRotationPoint,
31919 getRotationOffset: getElementRotationOffset,
31920 allowEdgeTxrCaching: false,
31921 allowParentTxrCaching: false
31922 });
31923 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31924 getKey: getLabelKey,
31925 drawElement: drawLabel,
31926 getBoundingBox: getLabelBox,
31927 getRotationPoint: getLabelRotationPoint,
31928 getRotationOffset: getLabelRotationOffset,
31929 isVisible: isLabelVisibleAtScale
31930 });
31931 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31932 getKey: getSourceLabelKey,
31933 drawElement: drawSourceLabel,
31934 getBoundingBox: getSourceLabelBox,
31935 getRotationPoint: getSourceLabelRotationPoint,
31936 getRotationOffset: getSourceLabelRotationOffset,
31937 isVisible: isLabelVisibleAtScale
31938 });
31939 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31940 getKey: getTargetLabelKey,
31941 drawElement: drawTargetLabel,
31942 getBoundingBox: getTargetLabelBox,
31943 getRotationPoint: getTargetLabelRotationPoint,
31944 getRotationOffset: getTargetLabelRotationOffset,
31945 isVisible: isLabelVisibleAtScale
31946 });
31947 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31948 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31949 // each cache should check for sub-key diff to see that the update affects that cache particularly
31950 eleTxrCache.invalidateElements(eles);
31951 lblTxrCache.invalidateElements(eles);
31952 slbTxrCache.invalidateElements(eles);
31953 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31954
31955 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31956
31957 for (var _i = 0; _i < eles.length; _i++) {
31958 var _p = eles[_i]._private;
31959 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31960 }
31961 });
31962
31963 var refineInLayers = function refineInLayers(reqs) {
31964 for (var i = 0; i < reqs.length; i++) {
31965 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31966 }
31967 };
31968
31969 eleTxrCache.onDequeue(refineInLayers);
31970 lblTxrCache.onDequeue(refineInLayers);
31971 slbTxrCache.onDequeue(refineInLayers);
31972 tlbTxrCache.onDequeue(refineInLayers);
31973 }
31974
31975 CRp$a.redrawHint = function (group, bool) {
31976 var r = this;
31977
31978 switch (group) {
31979 case 'eles':
31980 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31981 break;
31982
31983 case 'drag':
31984 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31985 break;
31986
31987 case 'select':
31988 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31989 break;
31990 }
31991 }; // whether to use Path2D caching for drawing
31992
31993
31994 var pathsImpld = typeof Path2D !== 'undefined';
31995
31996 CRp$a.path2dEnabled = function (on) {
31997 if (on === undefined) {
31998 return this.pathsEnabled;
31999 }
32000
32001 this.pathsEnabled = on ? true : false;
32002 };
32003
32004 CRp$a.usePaths = function () {
32005 return pathsImpld && this.pathsEnabled;
32006 };
32007
32008 CRp$a.setImgSmoothing = function (context, bool) {
32009 if (context.imageSmoothingEnabled != null) {
32010 context.imageSmoothingEnabled = bool;
32011 } else {
32012 context.webkitImageSmoothingEnabled = bool;
32013 context.mozImageSmoothingEnabled = bool;
32014 context.msImageSmoothingEnabled = bool;
32015 }
32016 };
32017
32018 CRp$a.getImgSmoothing = function (context) {
32019 if (context.imageSmoothingEnabled != null) {
32020 return context.imageSmoothingEnabled;
32021 } else {
32022 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
32023 }
32024 };
32025
32026 CRp$a.makeOffscreenCanvas = function (width, height) {
32027 var canvas;
32028
32029 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
32030 canvas = new OffscreenCanvas(width, height);
32031 } else {
32032 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
32033
32034 canvas.width = width;
32035 canvas.height = height;
32036 }
32037
32038 return canvas;
32039 };
32040
32041 [CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
32042 extend(CRp$a, props);
32043 });
32044
32045 var renderer = [{
32046 name: 'null',
32047 impl: NullRenderer
32048 }, {
32049 name: 'base',
32050 impl: BR
32051 }, {
32052 name: 'canvas',
32053 impl: CR
32054 }];
32055
32056 var incExts = [{
32057 type: 'layout',
32058 extensions: layout
32059 }, {
32060 type: 'renderer',
32061 extensions: renderer
32062 }];
32063
32064 var extensions = {}; // registered modules for extensions, indexed by name
32065
32066 var modules = {};
32067
32068 function setExtension(type, name, registrant) {
32069 var ext = registrant;
32070
32071 var overrideErr = function overrideErr(field) {
32072 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
32073 };
32074
32075 if (type === 'core') {
32076 if (Core.prototype[name]) {
32077 return overrideErr(name);
32078 } else {
32079 Core.prototype[name] = registrant;
32080 }
32081 } else if (type === 'collection') {
32082 if (Collection.prototype[name]) {
32083 return overrideErr(name);
32084 } else {
32085 Collection.prototype[name] = registrant;
32086 }
32087 } else if (type === 'layout') {
32088 // fill in missing layout functions in the prototype
32089 var Layout = function Layout(options) {
32090 this.options = options;
32091 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
32092
32093 if (!plainObject(this._private)) {
32094 this._private = {};
32095 }
32096
32097 this._private.cy = options.cy;
32098 this._private.listeners = [];
32099 this.createEmitter();
32100 };
32101
32102 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
32103 var optLayoutFns = [];
32104
32105 for (var i = 0; i < optLayoutFns.length; i++) {
32106 var fnName = optLayoutFns[i];
32107
32108 layoutProto[fnName] = layoutProto[fnName] || function () {
32109 return this;
32110 };
32111 } // either .start() or .run() is defined, so autogen the other
32112
32113
32114 if (layoutProto.start && !layoutProto.run) {
32115 layoutProto.run = function () {
32116 this.start();
32117 return this;
32118 };
32119 } else if (!layoutProto.start && layoutProto.run) {
32120 layoutProto.start = function () {
32121 this.run();
32122 return this;
32123 };
32124 }
32125
32126 var regStop = registrant.prototype.stop;
32127
32128 layoutProto.stop = function () {
32129 var opts = this.options;
32130
32131 if (opts && opts.animate) {
32132 var anis = this.animations;
32133
32134 if (anis) {
32135 for (var _i = 0; _i < anis.length; _i++) {
32136 anis[_i].stop();
32137 }
32138 }
32139 }
32140
32141 if (regStop) {
32142 regStop.call(this);
32143 } else {
32144 this.emit('layoutstop');
32145 }
32146
32147 return this;
32148 };
32149
32150 if (!layoutProto.destroy) {
32151 layoutProto.destroy = function () {
32152 return this;
32153 };
32154 }
32155
32156 layoutProto.cy = function () {
32157 return this._private.cy;
32158 };
32159
32160 var getCy = function getCy(layout) {
32161 return layout._private.cy;
32162 };
32163
32164 var emitterOpts = {
32165 addEventFields: function addEventFields(layout, evt) {
32166 evt.layout = layout;
32167 evt.cy = getCy(layout);
32168 evt.target = layout;
32169 },
32170 bubble: function bubble() {
32171 return true;
32172 },
32173 parent: function parent(layout) {
32174 return getCy(layout);
32175 }
32176 };
32177 extend(layoutProto, {
32178 createEmitter: function createEmitter() {
32179 this._private.emitter = new Emitter(emitterOpts, this);
32180 return this;
32181 },
32182 emitter: function emitter() {
32183 return this._private.emitter;
32184 },
32185 on: function on(evt, cb) {
32186 this.emitter().on(evt, cb);
32187 return this;
32188 },
32189 one: function one(evt, cb) {
32190 this.emitter().one(evt, cb);
32191 return this;
32192 },
32193 once: function once(evt, cb) {
32194 this.emitter().one(evt, cb);
32195 return this;
32196 },
32197 removeListener: function removeListener(evt, cb) {
32198 this.emitter().removeListener(evt, cb);
32199 return this;
32200 },
32201 removeAllListeners: function removeAllListeners() {
32202 this.emitter().removeAllListeners();
32203 return this;
32204 },
32205 emit: function emit(evt, params) {
32206 this.emitter().emit(evt, params);
32207 return this;
32208 }
32209 });
32210 define$3.eventAliasesOn(layoutProto);
32211 ext = Layout; // replace with our wrapped layout
32212 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
32213 // user registered renderers inherit from base
32214 var BaseRenderer = getExtension('renderer', 'base');
32215 var bProto = BaseRenderer.prototype;
32216 var RegistrantRenderer = registrant;
32217 var rProto = registrant.prototype;
32218
32219 var Renderer = function Renderer() {
32220 BaseRenderer.apply(this, arguments);
32221 RegistrantRenderer.apply(this, arguments);
32222 };
32223
32224 var proto = Renderer.prototype;
32225
32226 for (var pName in bProto) {
32227 var pVal = bProto[pName];
32228 var existsInR = rProto[pName] != null;
32229
32230 if (existsInR) {
32231 return overrideErr(pName);
32232 }
32233
32234 proto[pName] = pVal; // take impl from base
32235 }
32236
32237 for (var _pName in rProto) {
32238 proto[_pName] = rProto[_pName]; // take impl from registrant
32239 }
32240
32241 bProto.clientFunctions.forEach(function (name) {
32242 proto[name] = proto[name] || function () {
32243 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
32244 };
32245 });
32246 ext = Renderer;
32247 }
32248
32249 return setMap({
32250 map: extensions,
32251 keys: [type, name],
32252 value: ext
32253 });
32254 }
32255
32256 function getExtension(type, name) {
32257 return getMap({
32258 map: extensions,
32259 keys: [type, name]
32260 });
32261 }
32262
32263 function setModule(type, name, moduleType, moduleName, registrant) {
32264 return setMap({
32265 map: modules,
32266 keys: [type, name, moduleType, moduleName],
32267 value: registrant
32268 });
32269 }
32270
32271 function getModule(type, name, moduleType, moduleName) {
32272 return getMap({
32273 map: modules,
32274 keys: [type, name, moduleType, moduleName]
32275 });
32276 }
32277
32278 var extension = function extension() {
32279 // e.g. extension('renderer', 'svg')
32280 if (arguments.length === 2) {
32281 return getExtension.apply(null, arguments);
32282 } // e.g. extension('renderer', 'svg', { ... })
32283 else if (arguments.length === 3) {
32284 return setExtension.apply(null, arguments);
32285 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
32286 else if (arguments.length === 4) {
32287 return getModule.apply(null, arguments);
32288 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
32289 else if (arguments.length === 5) {
32290 return setModule.apply(null, arguments);
32291 } else {
32292 error('Invalid extension access syntax');
32293 }
32294 }; // allows a core instance to access extensions internally
32295
32296
32297 Core.prototype.extension = extension; // included extensions
32298
32299 incExts.forEach(function (group) {
32300 group.extensions.forEach(function (ext) {
32301 setExtension(group.type, ext.name, ext.impl);
32302 });
32303 });
32304
32305 // (useful for init)
32306
32307 var Stylesheet = function Stylesheet() {
32308 if (!(this instanceof Stylesheet)) {
32309 return new Stylesheet();
32310 }
32311
32312 this.length = 0;
32313 };
32314
32315 var sheetfn = Stylesheet.prototype;
32316
32317 sheetfn.instanceString = function () {
32318 return 'stylesheet';
32319 }; // just store the selector to be parsed later
32320
32321
32322 sheetfn.selector = function (selector) {
32323 var i = this.length++;
32324 this[i] = {
32325 selector: selector,
32326 properties: []
32327 };
32328 return this; // chaining
32329 }; // just store the property to be parsed later
32330
32331
32332 sheetfn.css = function (name, value) {
32333 var i = this.length - 1;
32334
32335 if (string(name)) {
32336 this[i].properties.push({
32337 name: name,
32338 value: value
32339 });
32340 } else if (plainObject(name)) {
32341 var map = name;
32342 var propNames = Object.keys(map);
32343
32344 for (var j = 0; j < propNames.length; j++) {
32345 var key = propNames[j];
32346 var mapVal = map[key];
32347
32348 if (mapVal == null) {
32349 continue;
32350 }
32351
32352 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
32353
32354 if (prop == null) {
32355 continue;
32356 }
32357
32358 var _name = prop.name;
32359 var _value = mapVal;
32360 this[i].properties.push({
32361 name: _name,
32362 value: _value
32363 });
32364 }
32365 }
32366
32367 return this; // chaining
32368 };
32369
32370 sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
32371
32372 sheetfn.generateStyle = function (cy) {
32373 var style = new Style(cy);
32374 return this.appendToStyle(style);
32375 }; // append a dummy stylesheet object on a real style object
32376
32377
32378 sheetfn.appendToStyle = function (style) {
32379 for (var i = 0; i < this.length; i++) {
32380 var context = this[i];
32381 var selector = context.selector;
32382 var props = context.properties;
32383 style.selector(selector); // apply selector
32384
32385 for (var j = 0; j < props.length; j++) {
32386 var prop = props[j];
32387 style.css(prop.name, prop.value); // apply property
32388 }
32389 }
32390
32391 return style;
32392 };
32393
32394 var version = "3.18.2";
32395
32396 var cytoscape = function cytoscape(options) {
32397 // if no options specified, use default
32398 if (options === undefined) {
32399 options = {};
32400 } // create instance
32401
32402
32403 if (plainObject(options)) {
32404 return new Core(options);
32405 } // allow for registration of extensions
32406 else if (string(options)) {
32407 return extension.apply(extension, arguments);
32408 }
32409 }; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
32410
32411
32412 cytoscape.use = function (ext) {
32413 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
32414
32415 args.unshift(cytoscape); // cytoscape is first arg to ext
32416
32417 ext.apply(null, args);
32418 return this;
32419 };
32420
32421 cytoscape.warnings = function (bool) {
32422 return warnings(bool);
32423 }; // replaced by build system
32424
32425
32426 cytoscape.version = version; // expose public apis (mostly for extensions)
32427
32428 cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
32429
32430 return cytoscape;
32431
32432})));