UNPKG

1.01 MBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2024, 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 = typeof globalThis !== 'undefined' ? globalThis : global || self, global.cytoscape = factory());
27})(this, (function () { 'use strict';
28
29 function _typeof(obj) {
30 "@babel/helpers - typeof";
31
32 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
33 return typeof obj;
34 } : function (obj) {
35 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
36 }, _typeof(obj);
37 }
38 function _classCallCheck(instance, Constructor) {
39 if (!(instance instanceof Constructor)) {
40 throw new TypeError("Cannot call a class as a function");
41 }
42 }
43 function _defineProperties(target, props) {
44 for (var i = 0; i < props.length; i++) {
45 var descriptor = props[i];
46 descriptor.enumerable = descriptor.enumerable || false;
47 descriptor.configurable = true;
48 if ("value" in descriptor) descriptor.writable = true;
49 Object.defineProperty(target, descriptor.key, descriptor);
50 }
51 }
52 function _createClass(Constructor, protoProps, staticProps) {
53 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
54 if (staticProps) _defineProperties(Constructor, staticProps);
55 Object.defineProperty(Constructor, "prototype", {
56 writable: false
57 });
58 return Constructor;
59 }
60 function _defineProperty$1(obj, key, value) {
61 if (key in obj) {
62 Object.defineProperty(obj, key, {
63 value: value,
64 enumerable: true,
65 configurable: true,
66 writable: true
67 });
68 } else {
69 obj[key] = value;
70 }
71 return obj;
72 }
73 function _slicedToArray(arr, i) {
74 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
75 }
76 function _arrayWithHoles(arr) {
77 if (Array.isArray(arr)) return arr;
78 }
79 function _iterableToArrayLimit(arr, i) {
80 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
81 if (_i == null) return;
82 var _arr = [];
83 var _n = true;
84 var _d = false;
85 var _s, _e;
86 try {
87 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
88 _arr.push(_s.value);
89 if (i && _arr.length === i) break;
90 }
91 } catch (err) {
92 _d = true;
93 _e = err;
94 } finally {
95 try {
96 if (!_n && _i["return"] != null) _i["return"]();
97 } finally {
98 if (_d) throw _e;
99 }
100 }
101 return _arr;
102 }
103 function _unsupportedIterableToArray(o, minLen) {
104 if (!o) return;
105 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
106 var n = Object.prototype.toString.call(o).slice(8, -1);
107 if (n === "Object" && o.constructor) n = o.constructor.name;
108 if (n === "Map" || n === "Set") return Array.from(o);
109 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
110 }
111 function _arrayLikeToArray(arr, len) {
112 if (len == null || len > arr.length) len = arr.length;
113 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
114 return arr2;
115 }
116 function _nonIterableRest() {
117 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
118 }
119 function _createForOfIteratorHelper(o, allowArrayLike) {
120 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
121 if (!it) {
122 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
123 if (it) o = it;
124 var i = 0;
125 var F = function () {};
126 return {
127 s: F,
128 n: function () {
129 if (i >= o.length) return {
130 done: true
131 };
132 return {
133 done: false,
134 value: o[i++]
135 };
136 },
137 e: function (e) {
138 throw e;
139 },
140 f: F
141 };
142 }
143 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
144 }
145 var normalCompletion = true,
146 didErr = false,
147 err;
148 return {
149 s: function () {
150 it = it.call(o);
151 },
152 n: function () {
153 var step = it.next();
154 normalCompletion = step.done;
155 return step;
156 },
157 e: function (e) {
158 didErr = true;
159 err = e;
160 },
161 f: function () {
162 try {
163 if (!normalCompletion && it.return != null) it.return();
164 } finally {
165 if (didErr) throw err;
166 }
167 }
168 };
169 }
170
171 var _window = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
172
173 var navigator = _window ? _window.navigator : null;
174 _window ? _window.document : null;
175 var typeofstr = _typeof('');
176 var typeofobj = _typeof({});
177 var typeoffn = _typeof(function () {});
178 var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
179 var instanceStr = function instanceStr(obj) {
180 return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null;
181 };
182
183 var string = function string(obj) {
184 return obj != null && _typeof(obj) == typeofstr;
185 };
186 var fn$6 = function fn(obj) {
187 return obj != null && _typeof(obj) === typeoffn;
188 };
189 var array = function array(obj) {
190 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
191 };
192 var plainObject = function plainObject(obj) {
193 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
194 };
195 var object = function object(obj) {
196 return obj != null && _typeof(obj) === typeofobj;
197 };
198 var number$1 = function number(obj) {
199 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
200 };
201 var integer = function integer(obj) {
202 return number$1(obj) && Math.floor(obj) === obj;
203 };
204 var htmlElement = function htmlElement(obj) {
205 if ('undefined' === typeofhtmlele) {
206 return undefined;
207 } else {
208 return null != obj && obj instanceof HTMLElement;
209 }
210 };
211 var elementOrCollection = function elementOrCollection(obj) {
212 return element(obj) || collection(obj);
213 };
214 var element = function element(obj) {
215 return instanceStr(obj) === 'collection' && obj._private.single;
216 };
217 var collection = function collection(obj) {
218 return instanceStr(obj) === 'collection' && !obj._private.single;
219 };
220 var core = function core(obj) {
221 return instanceStr(obj) === 'core';
222 };
223 var stylesheet = function stylesheet(obj) {
224 return instanceStr(obj) === 'stylesheet';
225 };
226 var event = function event(obj) {
227 return instanceStr(obj) === 'event';
228 };
229 var emptyString = function emptyString(obj) {
230 if (obj === undefined || obj === null) {
231 // null is empty
232 return true;
233 } else if (obj === '' || obj.match(/^\s+$/)) {
234 return true; // empty string is empty
235 }
236
237 return false; // otherwise, we don't know what we've got
238 };
239 var domElement = function domElement(obj) {
240 if (typeof HTMLElement === 'undefined') {
241 return false; // we're not in a browser so it doesn't matter
242 } else {
243 return obj instanceof HTMLElement;
244 }
245 };
246 var boundingBox = function boundingBox(obj) {
247 return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2);
248 };
249 var promise = function promise(obj) {
250 return object(obj) && fn$6(obj.then);
251 };
252 var ms = function ms() {
253 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
254 }; // probably a better way to detect this...
255
256 var memoize$1 = function memoize(fn, keyFn) {
257 if (!keyFn) {
258 keyFn = function keyFn() {
259 if (arguments.length === 1) {
260 return arguments[0];
261 } else if (arguments.length === 0) {
262 return 'undefined';
263 }
264 var args = [];
265 for (var i = 0; i < arguments.length; i++) {
266 args.push(arguments[i]);
267 }
268 return args.join('$');
269 };
270 }
271 var memoizedFn = function memoizedFn() {
272 var self = this;
273 var args = arguments;
274 var ret;
275 var k = keyFn.apply(self, args);
276 var cache = memoizedFn.cache;
277 if (!(ret = cache[k])) {
278 ret = cache[k] = fn.apply(self, args);
279 }
280 return ret;
281 };
282 memoizedFn.cache = {};
283 return memoizedFn;
284 };
285
286 var camel2dash = memoize$1(function (str) {
287 return str.replace(/([A-Z])/g, function (v) {
288 return '-' + v.toLowerCase();
289 });
290 });
291 var dash2camel = memoize$1(function (str) {
292 return str.replace(/(-\w)/g, function (v) {
293 return v[1].toUpperCase();
294 });
295 });
296 var prependCamel = memoize$1(function (prefix, str) {
297 return prefix + str[0].toUpperCase() + str.substring(1);
298 }, function (prefix, str) {
299 return prefix + '$' + str;
300 });
301 var capitalize = function capitalize(str) {
302 if (emptyString(str)) {
303 return str;
304 }
305 return str.charAt(0).toUpperCase() + str.substring(1);
306 };
307
308 var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
309 var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)';
310 var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)';
311 var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)';
312 var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)';
313 var hex3 = '\\#[0-9a-fA-F]{3}';
314 var hex6 = '\\#[0-9a-fA-F]{6}';
315
316 var ascending = function ascending(a, b) {
317 if (a < b) {
318 return -1;
319 } else if (a > b) {
320 return 1;
321 } else {
322 return 0;
323 }
324 };
325 var descending = function descending(a, b) {
326 return -1 * ascending(a, b);
327 };
328
329 var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
330 var args = arguments;
331 for (var i = 1; i < args.length; i++) {
332 var obj = args[i];
333 if (obj == null) {
334 continue;
335 }
336 var keys = Object.keys(obj);
337 for (var j = 0; j < keys.length; j++) {
338 var k = keys[j];
339 tgt[k] = obj[k];
340 }
341 }
342 return tgt;
343 };
344
345 // get [r, g, b] from #abc or #aabbcc
346 var hex2tuple = function hex2tuple(hex) {
347 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
348 return;
349 }
350 var shortHex = hex.length === 4;
351 var r, g, b;
352 var base = 16;
353 if (shortHex) {
354 r = parseInt(hex[1] + hex[1], base);
355 g = parseInt(hex[2] + hex[2], base);
356 b = parseInt(hex[3] + hex[3], base);
357 } else {
358 r = parseInt(hex[1] + hex[2], base);
359 g = parseInt(hex[3] + hex[4], base);
360 b = parseInt(hex[5] + hex[6], base);
361 }
362 return [r, g, b];
363 };
364
365 // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
366 var hsl2tuple = function hsl2tuple(hsl) {
367 var ret;
368 var h, s, l, a, r, g, b;
369 function hue2rgb(p, q, t) {
370 if (t < 0) t += 1;
371 if (t > 1) t -= 1;
372 if (t < 1 / 6) return p + (q - p) * 6 * t;
373 if (t < 1 / 2) return q;
374 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
375 return p;
376 }
377 var m = new RegExp('^' + hsla + '$').exec(hsl);
378 if (m) {
379 // get hue
380 h = parseInt(m[1]);
381 if (h < 0) {
382 h = (360 - -1 * h % 360) % 360;
383 } else if (h > 360) {
384 h = h % 360;
385 }
386 h /= 360; // normalise on [0, 1]
387
388 s = parseFloat(m[2]);
389 if (s < 0 || s > 100) {
390 return;
391 } // saturation is [0, 100]
392 s = s / 100; // normalise on [0, 1]
393
394 l = parseFloat(m[3]);
395 if (l < 0 || l > 100) {
396 return;
397 } // lightness is [0, 100]
398 l = l / 100; // normalise on [0, 1]
399
400 a = m[4];
401 if (a !== undefined) {
402 a = parseFloat(a);
403 if (a < 0 || a > 1) {
404 return;
405 } // alpha is [0, 1]
406 }
407
408 // now, convert to rgb
409 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
410 if (s === 0) {
411 r = g = b = Math.round(l * 255); // achromatic
412 } else {
413 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
414 var p = 2 * l - q;
415 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
416 g = Math.round(255 * hue2rgb(p, q, h));
417 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
418 }
419 ret = [r, g, b, a];
420 }
421 return ret;
422 };
423
424 // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
425 var rgb2tuple = function rgb2tuple(rgb) {
426 var ret;
427 var m = new RegExp('^' + rgba + '$').exec(rgb);
428 if (m) {
429 ret = [];
430 var isPct = [];
431 for (var i = 1; i <= 3; i++) {
432 var channel = m[i];
433 if (channel[channel.length - 1] === '%') {
434 isPct[i] = true;
435 }
436 channel = parseFloat(channel);
437 if (isPct[i]) {
438 channel = channel / 100 * 255; // normalise to [0, 255]
439 }
440
441 if (channel < 0 || channel > 255) {
442 return;
443 } // invalid channel value
444
445 ret.push(Math.floor(channel));
446 }
447 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
448 var allArePct = isPct[1] && isPct[2] && isPct[3];
449 if (atLeastOneIsPct && !allArePct) {
450 return;
451 } // must all be percent values if one is
452
453 var alpha = m[4];
454 if (alpha !== undefined) {
455 alpha = parseFloat(alpha);
456 if (alpha < 0 || alpha > 1) {
457 return;
458 } // invalid alpha value
459
460 ret.push(alpha);
461 }
462 }
463 return ret;
464 };
465 var colorname2tuple = function colorname2tuple(color) {
466 return colors[color.toLowerCase()];
467 };
468 var color2tuple = function color2tuple(color) {
469 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
470 };
471 var colors = {
472 // special colour names
473 transparent: [0, 0, 0, 0],
474 // NB alpha === 0
475
476 // regular colours
477 aliceblue: [240, 248, 255],
478 antiquewhite: [250, 235, 215],
479 aqua: [0, 255, 255],
480 aquamarine: [127, 255, 212],
481 azure: [240, 255, 255],
482 beige: [245, 245, 220],
483 bisque: [255, 228, 196],
484 black: [0, 0, 0],
485 blanchedalmond: [255, 235, 205],
486 blue: [0, 0, 255],
487 blueviolet: [138, 43, 226],
488 brown: [165, 42, 42],
489 burlywood: [222, 184, 135],
490 cadetblue: [95, 158, 160],
491 chartreuse: [127, 255, 0],
492 chocolate: [210, 105, 30],
493 coral: [255, 127, 80],
494 cornflowerblue: [100, 149, 237],
495 cornsilk: [255, 248, 220],
496 crimson: [220, 20, 60],
497 cyan: [0, 255, 255],
498 darkblue: [0, 0, 139],
499 darkcyan: [0, 139, 139],
500 darkgoldenrod: [184, 134, 11],
501 darkgray: [169, 169, 169],
502 darkgreen: [0, 100, 0],
503 darkgrey: [169, 169, 169],
504 darkkhaki: [189, 183, 107],
505 darkmagenta: [139, 0, 139],
506 darkolivegreen: [85, 107, 47],
507 darkorange: [255, 140, 0],
508 darkorchid: [153, 50, 204],
509 darkred: [139, 0, 0],
510 darksalmon: [233, 150, 122],
511 darkseagreen: [143, 188, 143],
512 darkslateblue: [72, 61, 139],
513 darkslategray: [47, 79, 79],
514 darkslategrey: [47, 79, 79],
515 darkturquoise: [0, 206, 209],
516 darkviolet: [148, 0, 211],
517 deeppink: [255, 20, 147],
518 deepskyblue: [0, 191, 255],
519 dimgray: [105, 105, 105],
520 dimgrey: [105, 105, 105],
521 dodgerblue: [30, 144, 255],
522 firebrick: [178, 34, 34],
523 floralwhite: [255, 250, 240],
524 forestgreen: [34, 139, 34],
525 fuchsia: [255, 0, 255],
526 gainsboro: [220, 220, 220],
527 ghostwhite: [248, 248, 255],
528 gold: [255, 215, 0],
529 goldenrod: [218, 165, 32],
530 gray: [128, 128, 128],
531 grey: [128, 128, 128],
532 green: [0, 128, 0],
533 greenyellow: [173, 255, 47],
534 honeydew: [240, 255, 240],
535 hotpink: [255, 105, 180],
536 indianred: [205, 92, 92],
537 indigo: [75, 0, 130],
538 ivory: [255, 255, 240],
539 khaki: [240, 230, 140],
540 lavender: [230, 230, 250],
541 lavenderblush: [255, 240, 245],
542 lawngreen: [124, 252, 0],
543 lemonchiffon: [255, 250, 205],
544 lightblue: [173, 216, 230],
545 lightcoral: [240, 128, 128],
546 lightcyan: [224, 255, 255],
547 lightgoldenrodyellow: [250, 250, 210],
548 lightgray: [211, 211, 211],
549 lightgreen: [144, 238, 144],
550 lightgrey: [211, 211, 211],
551 lightpink: [255, 182, 193],
552 lightsalmon: [255, 160, 122],
553 lightseagreen: [32, 178, 170],
554 lightskyblue: [135, 206, 250],
555 lightslategray: [119, 136, 153],
556 lightslategrey: [119, 136, 153],
557 lightsteelblue: [176, 196, 222],
558 lightyellow: [255, 255, 224],
559 lime: [0, 255, 0],
560 limegreen: [50, 205, 50],
561 linen: [250, 240, 230],
562 magenta: [255, 0, 255],
563 maroon: [128, 0, 0],
564 mediumaquamarine: [102, 205, 170],
565 mediumblue: [0, 0, 205],
566 mediumorchid: [186, 85, 211],
567 mediumpurple: [147, 112, 219],
568 mediumseagreen: [60, 179, 113],
569 mediumslateblue: [123, 104, 238],
570 mediumspringgreen: [0, 250, 154],
571 mediumturquoise: [72, 209, 204],
572 mediumvioletred: [199, 21, 133],
573 midnightblue: [25, 25, 112],
574 mintcream: [245, 255, 250],
575 mistyrose: [255, 228, 225],
576 moccasin: [255, 228, 181],
577 navajowhite: [255, 222, 173],
578 navy: [0, 0, 128],
579 oldlace: [253, 245, 230],
580 olive: [128, 128, 0],
581 olivedrab: [107, 142, 35],
582 orange: [255, 165, 0],
583 orangered: [255, 69, 0],
584 orchid: [218, 112, 214],
585 palegoldenrod: [238, 232, 170],
586 palegreen: [152, 251, 152],
587 paleturquoise: [175, 238, 238],
588 palevioletred: [219, 112, 147],
589 papayawhip: [255, 239, 213],
590 peachpuff: [255, 218, 185],
591 peru: [205, 133, 63],
592 pink: [255, 192, 203],
593 plum: [221, 160, 221],
594 powderblue: [176, 224, 230],
595 purple: [128, 0, 128],
596 red: [255, 0, 0],
597 rosybrown: [188, 143, 143],
598 royalblue: [65, 105, 225],
599 saddlebrown: [139, 69, 19],
600 salmon: [250, 128, 114],
601 sandybrown: [244, 164, 96],
602 seagreen: [46, 139, 87],
603 seashell: [255, 245, 238],
604 sienna: [160, 82, 45],
605 silver: [192, 192, 192],
606 skyblue: [135, 206, 235],
607 slateblue: [106, 90, 205],
608 slategray: [112, 128, 144],
609 slategrey: [112, 128, 144],
610 snow: [255, 250, 250],
611 springgreen: [0, 255, 127],
612 steelblue: [70, 130, 180],
613 tan: [210, 180, 140],
614 teal: [0, 128, 128],
615 thistle: [216, 191, 216],
616 tomato: [255, 99, 71],
617 turquoise: [64, 224, 208],
618 violet: [238, 130, 238],
619 wheat: [245, 222, 179],
620 white: [255, 255, 255],
621 whitesmoke: [245, 245, 245],
622 yellow: [255, 255, 0],
623 yellowgreen: [154, 205, 50]
624 };
625
626 // sets the value in a map (map may not be built)
627 var setMap = function setMap(options) {
628 var obj = options.map;
629 var keys = options.keys;
630 var l = keys.length;
631 for (var i = 0; i < l; i++) {
632 var key = keys[i];
633 if (plainObject(key)) {
634 throw Error('Tried to set map with object key');
635 }
636 if (i < keys.length - 1) {
637 // extend the map if necessary
638 if (obj[key] == null) {
639 obj[key] = {};
640 }
641 obj = obj[key];
642 } else {
643 // set the value
644 obj[key] = options.value;
645 }
646 }
647 };
648
649 // gets the value in a map even if it's not built in places
650 var getMap = function getMap(options) {
651 var obj = options.map;
652 var keys = options.keys;
653 var l = keys.length;
654 for (var i = 0; i < l; i++) {
655 var key = keys[i];
656 if (plainObject(key)) {
657 throw Error('Tried to get map with object key');
658 }
659 obj = obj[key];
660 if (obj == null) {
661 return obj;
662 }
663 }
664 return obj;
665 };
666
667 /**
668 * Checks if `value` is the
669 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
670 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
671 *
672 * @static
673 * @memberOf _
674 * @since 0.1.0
675 * @category Lang
676 * @param {*} value The value to check.
677 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
678 * @example
679 *
680 * _.isObject({});
681 * // => true
682 *
683 * _.isObject([1, 2, 3]);
684 * // => true
685 *
686 * _.isObject(_.noop);
687 * // => true
688 *
689 * _.isObject(null);
690 * // => false
691 */
692 function isObject(value) {
693 var type = typeof value;
694 return value != null && (type == 'object' || type == 'function');
695 }
696
697 var isObject_1 = isObject;
698
699 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
700
701 function createCommonjsModule(fn, module) {
702 return module = { exports: {} }, fn(module, module.exports), module.exports;
703 }
704
705 /** Detect free variable `global` from Node.js. */
706 var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
707
708 var _freeGlobal = freeGlobal;
709
710 /** Detect free variable `self`. */
711 var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
712
713 /** Used as a reference to the global object. */
714 var root = _freeGlobal || freeSelf || Function('return this')();
715
716 var _root = root;
717
718 /**
719 * Gets the timestamp of the number of milliseconds that have elapsed since
720 * the Unix epoch (1 January 1970 00:00:00 UTC).
721 *
722 * @static
723 * @memberOf _
724 * @since 2.4.0
725 * @category Date
726 * @returns {number} Returns the timestamp.
727 * @example
728 *
729 * _.defer(function(stamp) {
730 * console.log(_.now() - stamp);
731 * }, _.now());
732 * // => Logs the number of milliseconds it took for the deferred invocation.
733 */
734 var now = function() {
735 return _root.Date.now();
736 };
737
738 var now_1 = now;
739
740 /** Used to match a single whitespace character. */
741 var reWhitespace = /\s/;
742
743 /**
744 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
745 * character of `string`.
746 *
747 * @private
748 * @param {string} string The string to inspect.
749 * @returns {number} Returns the index of the last non-whitespace character.
750 */
751 function trimmedEndIndex(string) {
752 var index = string.length;
753
754 while (index-- && reWhitespace.test(string.charAt(index))) {}
755 return index;
756 }
757
758 var _trimmedEndIndex = trimmedEndIndex;
759
760 /** Used to match leading whitespace. */
761 var reTrimStart = /^\s+/;
762
763 /**
764 * The base implementation of `_.trim`.
765 *
766 * @private
767 * @param {string} string The string to trim.
768 * @returns {string} Returns the trimmed string.
769 */
770 function baseTrim(string) {
771 return string
772 ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '')
773 : string;
774 }
775
776 var _baseTrim = baseTrim;
777
778 /** Built-in value references. */
779 var Symbol$1 = _root.Symbol;
780
781 var _Symbol = Symbol$1;
782
783 /** Used for built-in method references. */
784 var objectProto$5 = Object.prototype;
785
786 /** Used to check objects for own properties. */
787 var hasOwnProperty$4 = objectProto$5.hasOwnProperty;
788
789 /**
790 * Used to resolve the
791 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
792 * of values.
793 */
794 var nativeObjectToString$1 = objectProto$5.toString;
795
796 /** Built-in value references. */
797 var symToStringTag$1 = _Symbol ? _Symbol.toStringTag : undefined;
798
799 /**
800 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
801 *
802 * @private
803 * @param {*} value The value to query.
804 * @returns {string} Returns the raw `toStringTag`.
805 */
806 function getRawTag(value) {
807 var isOwn = hasOwnProperty$4.call(value, symToStringTag$1),
808 tag = value[symToStringTag$1];
809
810 try {
811 value[symToStringTag$1] = undefined;
812 var unmasked = true;
813 } catch (e) {}
814
815 var result = nativeObjectToString$1.call(value);
816 if (unmasked) {
817 if (isOwn) {
818 value[symToStringTag$1] = tag;
819 } else {
820 delete value[symToStringTag$1];
821 }
822 }
823 return result;
824 }
825
826 var _getRawTag = getRawTag;
827
828 /** Used for built-in method references. */
829 var objectProto$4 = Object.prototype;
830
831 /**
832 * Used to resolve the
833 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
834 * of values.
835 */
836 var nativeObjectToString = objectProto$4.toString;
837
838 /**
839 * Converts `value` to a string using `Object.prototype.toString`.
840 *
841 * @private
842 * @param {*} value The value to convert.
843 * @returns {string} Returns the converted string.
844 */
845 function objectToString(value) {
846 return nativeObjectToString.call(value);
847 }
848
849 var _objectToString = objectToString;
850
851 /** `Object#toString` result references. */
852 var nullTag = '[object Null]',
853 undefinedTag = '[object Undefined]';
854
855 /** Built-in value references. */
856 var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;
857
858 /**
859 * The base implementation of `getTag` without fallbacks for buggy environments.
860 *
861 * @private
862 * @param {*} value The value to query.
863 * @returns {string} Returns the `toStringTag`.
864 */
865 function baseGetTag(value) {
866 if (value == null) {
867 return value === undefined ? undefinedTag : nullTag;
868 }
869 return (symToStringTag && symToStringTag in Object(value))
870 ? _getRawTag(value)
871 : _objectToString(value);
872 }
873
874 var _baseGetTag = baseGetTag;
875
876 /**
877 * Checks if `value` is object-like. A value is object-like if it's not `null`
878 * and has a `typeof` result of "object".
879 *
880 * @static
881 * @memberOf _
882 * @since 4.0.0
883 * @category Lang
884 * @param {*} value The value to check.
885 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
886 * @example
887 *
888 * _.isObjectLike({});
889 * // => true
890 *
891 * _.isObjectLike([1, 2, 3]);
892 * // => true
893 *
894 * _.isObjectLike(_.noop);
895 * // => false
896 *
897 * _.isObjectLike(null);
898 * // => false
899 */
900 function isObjectLike(value) {
901 return value != null && typeof value == 'object';
902 }
903
904 var isObjectLike_1 = isObjectLike;
905
906 /** `Object#toString` result references. */
907 var symbolTag = '[object Symbol]';
908
909 /**
910 * Checks if `value` is classified as a `Symbol` primitive or object.
911 *
912 * @static
913 * @memberOf _
914 * @since 4.0.0
915 * @category Lang
916 * @param {*} value The value to check.
917 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
918 * @example
919 *
920 * _.isSymbol(Symbol.iterator);
921 * // => true
922 *
923 * _.isSymbol('abc');
924 * // => false
925 */
926 function isSymbol(value) {
927 return typeof value == 'symbol' ||
928 (isObjectLike_1(value) && _baseGetTag(value) == symbolTag);
929 }
930
931 var isSymbol_1 = isSymbol;
932
933 /** Used as references for various `Number` constants. */
934 var NAN = 0 / 0;
935
936 /** Used to detect bad signed hexadecimal string values. */
937 var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
938
939 /** Used to detect binary string values. */
940 var reIsBinary = /^0b[01]+$/i;
941
942 /** Used to detect octal string values. */
943 var reIsOctal = /^0o[0-7]+$/i;
944
945 /** Built-in method references without a dependency on `root`. */
946 var freeParseInt = parseInt;
947
948 /**
949 * Converts `value` to a number.
950 *
951 * @static
952 * @memberOf _
953 * @since 4.0.0
954 * @category Lang
955 * @param {*} value The value to process.
956 * @returns {number} Returns the number.
957 * @example
958 *
959 * _.toNumber(3.2);
960 * // => 3.2
961 *
962 * _.toNumber(Number.MIN_VALUE);
963 * // => 5e-324
964 *
965 * _.toNumber(Infinity);
966 * // => Infinity
967 *
968 * _.toNumber('3.2');
969 * // => 3.2
970 */
971 function toNumber(value) {
972 if (typeof value == 'number') {
973 return value;
974 }
975 if (isSymbol_1(value)) {
976 return NAN;
977 }
978 if (isObject_1(value)) {
979 var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
980 value = isObject_1(other) ? (other + '') : other;
981 }
982 if (typeof value != 'string') {
983 return value === 0 ? value : +value;
984 }
985 value = _baseTrim(value);
986 var isBinary = reIsBinary.test(value);
987 return (isBinary || reIsOctal.test(value))
988 ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
989 : (reIsBadHex.test(value) ? NAN : +value);
990 }
991
992 var toNumber_1 = toNumber;
993
994 /** Error message constants. */
995 var FUNC_ERROR_TEXT$1 = 'Expected a function';
996
997 /* Built-in method references for those with the same name as other `lodash` methods. */
998 var nativeMax = Math.max,
999 nativeMin = Math.min;
1000
1001 /**
1002 * Creates a debounced function that delays invoking `func` until after `wait`
1003 * milliseconds have elapsed since the last time the debounced function was
1004 * invoked. The debounced function comes with a `cancel` method to cancel
1005 * delayed `func` invocations and a `flush` method to immediately invoke them.
1006 * Provide `options` to indicate whether `func` should be invoked on the
1007 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
1008 * with the last arguments provided to the debounced function. Subsequent
1009 * calls to the debounced function return the result of the last `func`
1010 * invocation.
1011 *
1012 * **Note:** If `leading` and `trailing` options are `true`, `func` is
1013 * invoked on the trailing edge of the timeout only if the debounced function
1014 * is invoked more than once during the `wait` timeout.
1015 *
1016 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
1017 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
1018 *
1019 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
1020 * for details over the differences between `_.debounce` and `_.throttle`.
1021 *
1022 * @static
1023 * @memberOf _
1024 * @since 0.1.0
1025 * @category Function
1026 * @param {Function} func The function to debounce.
1027 * @param {number} [wait=0] The number of milliseconds to delay.
1028 * @param {Object} [options={}] The options object.
1029 * @param {boolean} [options.leading=false]
1030 * Specify invoking on the leading edge of the timeout.
1031 * @param {number} [options.maxWait]
1032 * The maximum time `func` is allowed to be delayed before it's invoked.
1033 * @param {boolean} [options.trailing=true]
1034 * Specify invoking on the trailing edge of the timeout.
1035 * @returns {Function} Returns the new debounced function.
1036 * @example
1037 *
1038 * // Avoid costly calculations while the window size is in flux.
1039 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
1040 *
1041 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
1042 * jQuery(element).on('click', _.debounce(sendMail, 300, {
1043 * 'leading': true,
1044 * 'trailing': false
1045 * }));
1046 *
1047 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
1048 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
1049 * var source = new EventSource('/stream');
1050 * jQuery(source).on('message', debounced);
1051 *
1052 * // Cancel the trailing debounced invocation.
1053 * jQuery(window).on('popstate', debounced.cancel);
1054 */
1055 function debounce(func, wait, options) {
1056 var lastArgs,
1057 lastThis,
1058 maxWait,
1059 result,
1060 timerId,
1061 lastCallTime,
1062 lastInvokeTime = 0,
1063 leading = false,
1064 maxing = false,
1065 trailing = true;
1066
1067 if (typeof func != 'function') {
1068 throw new TypeError(FUNC_ERROR_TEXT$1);
1069 }
1070 wait = toNumber_1(wait) || 0;
1071 if (isObject_1(options)) {
1072 leading = !!options.leading;
1073 maxing = 'maxWait' in options;
1074 maxWait = maxing ? nativeMax(toNumber_1(options.maxWait) || 0, wait) : maxWait;
1075 trailing = 'trailing' in options ? !!options.trailing : trailing;
1076 }
1077
1078 function invokeFunc(time) {
1079 var args = lastArgs,
1080 thisArg = lastThis;
1081
1082 lastArgs = lastThis = undefined;
1083 lastInvokeTime = time;
1084 result = func.apply(thisArg, args);
1085 return result;
1086 }
1087
1088 function leadingEdge(time) {
1089 // Reset any `maxWait` timer.
1090 lastInvokeTime = time;
1091 // Start the timer for the trailing edge.
1092 timerId = setTimeout(timerExpired, wait);
1093 // Invoke the leading edge.
1094 return leading ? invokeFunc(time) : result;
1095 }
1096
1097 function remainingWait(time) {
1098 var timeSinceLastCall = time - lastCallTime,
1099 timeSinceLastInvoke = time - lastInvokeTime,
1100 timeWaiting = wait - timeSinceLastCall;
1101
1102 return maxing
1103 ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
1104 : timeWaiting;
1105 }
1106
1107 function shouldInvoke(time) {
1108 var timeSinceLastCall = time - lastCallTime,
1109 timeSinceLastInvoke = time - lastInvokeTime;
1110
1111 // Either this is the first call, activity has stopped and we're at the
1112 // trailing edge, the system time has gone backwards and we're treating
1113 // it as the trailing edge, or we've hit the `maxWait` limit.
1114 return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
1115 (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
1116 }
1117
1118 function timerExpired() {
1119 var time = now_1();
1120 if (shouldInvoke(time)) {
1121 return trailingEdge(time);
1122 }
1123 // Restart the timer.
1124 timerId = setTimeout(timerExpired, remainingWait(time));
1125 }
1126
1127 function trailingEdge(time) {
1128 timerId = undefined;
1129
1130 // Only invoke if we have `lastArgs` which means `func` has been
1131 // debounced at least once.
1132 if (trailing && lastArgs) {
1133 return invokeFunc(time);
1134 }
1135 lastArgs = lastThis = undefined;
1136 return result;
1137 }
1138
1139 function cancel() {
1140 if (timerId !== undefined) {
1141 clearTimeout(timerId);
1142 }
1143 lastInvokeTime = 0;
1144 lastArgs = lastCallTime = lastThis = timerId = undefined;
1145 }
1146
1147 function flush() {
1148 return timerId === undefined ? result : trailingEdge(now_1());
1149 }
1150
1151 function debounced() {
1152 var time = now_1(),
1153 isInvoking = shouldInvoke(time);
1154
1155 lastArgs = arguments;
1156 lastThis = this;
1157 lastCallTime = time;
1158
1159 if (isInvoking) {
1160 if (timerId === undefined) {
1161 return leadingEdge(lastCallTime);
1162 }
1163 if (maxing) {
1164 // Handle invocations in a tight loop.
1165 clearTimeout(timerId);
1166 timerId = setTimeout(timerExpired, wait);
1167 return invokeFunc(lastCallTime);
1168 }
1169 }
1170 if (timerId === undefined) {
1171 timerId = setTimeout(timerExpired, wait);
1172 }
1173 return result;
1174 }
1175 debounced.cancel = cancel;
1176 debounced.flush = flush;
1177 return debounced;
1178 }
1179
1180 var debounce_1 = debounce;
1181
1182 var performance = _window ? _window.performance : null;
1183 var pnow = performance && performance.now ? function () {
1184 return performance.now();
1185 } : function () {
1186 return Date.now();
1187 };
1188 var raf = function () {
1189 if (_window) {
1190 if (_window.requestAnimationFrame) {
1191 return function (fn) {
1192 _window.requestAnimationFrame(fn);
1193 };
1194 } else if (_window.mozRequestAnimationFrame) {
1195 return function (fn) {
1196 _window.mozRequestAnimationFrame(fn);
1197 };
1198 } else if (_window.webkitRequestAnimationFrame) {
1199 return function (fn) {
1200 _window.webkitRequestAnimationFrame(fn);
1201 };
1202 } else if (_window.msRequestAnimationFrame) {
1203 return function (fn) {
1204 _window.msRequestAnimationFrame(fn);
1205 };
1206 }
1207 }
1208 return function (fn) {
1209 if (fn) {
1210 setTimeout(function () {
1211 fn(pnow());
1212 }, 1000 / 60);
1213 }
1214 };
1215 }();
1216 var requestAnimationFrame = function requestAnimationFrame(fn) {
1217 return raf(fn);
1218 };
1219 var performanceNow = pnow;
1220
1221 var DEFAULT_HASH_SEED = 9261;
1222 var K = 65599; // 37 also works pretty well
1223 var DEFAULT_HASH_SEED_ALT = 5381;
1224 var hashIterableInts = function hashIterableInts(iterator) {
1225 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1226 // sdbm/string-hash
1227 var hash = seed;
1228 var entry;
1229 for (;;) {
1230 entry = iterator.next();
1231 if (entry.done) {
1232 break;
1233 }
1234 hash = hash * K + entry.value | 0;
1235 }
1236 return hash;
1237 };
1238 var hashInt = function hashInt(num) {
1239 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
1240 // sdbm/string-hash
1241 return seed * K + num | 0;
1242 };
1243 var hashIntAlt = function hashIntAlt(num) {
1244 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
1245 // djb2/string-hash
1246 return (seed << 5) + seed + num | 0;
1247 };
1248 var combineHashes = function combineHashes(hash1, hash2) {
1249 return hash1 * 0x200000 + hash2;
1250 };
1251 var combineHashesArray = function combineHashesArray(hashes) {
1252 return hashes[0] * 0x200000 + hashes[1];
1253 };
1254 var hashArrays = function hashArrays(hashes1, hashes2) {
1255 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
1256 };
1257 var hashIntsArray = function hashIntsArray(ints, seed) {
1258 var entry = {
1259 value: 0,
1260 done: false
1261 };
1262 var i = 0;
1263 var length = ints.length;
1264 var iterator = {
1265 next: function next() {
1266 if (i < length) {
1267 entry.value = ints[i++];
1268 } else {
1269 entry.done = true;
1270 }
1271 return entry;
1272 }
1273 };
1274 return hashIterableInts(iterator, seed);
1275 };
1276 var hashString = function hashString(str, seed) {
1277 var entry = {
1278 value: 0,
1279 done: false
1280 };
1281 var i = 0;
1282 var length = str.length;
1283 var iterator = {
1284 next: function next() {
1285 if (i < length) {
1286 entry.value = str.charCodeAt(i++);
1287 } else {
1288 entry.done = true;
1289 }
1290 return entry;
1291 }
1292 };
1293 return hashIterableInts(iterator, seed);
1294 };
1295 var hashStrings = function hashStrings() {
1296 return hashStringsArray(arguments);
1297 };
1298 var hashStringsArray = function hashStringsArray(strs) {
1299 var hash;
1300 for (var i = 0; i < strs.length; i++) {
1301 var str = strs[i];
1302 if (i === 0) {
1303 hash = hashString(str);
1304 } else {
1305 hash = hashString(str, hash);
1306 }
1307 }
1308 return hash;
1309 };
1310
1311 /*global console */
1312 var warningsEnabled = true;
1313 var warnSupported = console.warn != null; // eslint-disable-line no-console
1314 var traceSupported = console.trace != null; // eslint-disable-line no-console
1315
1316 var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991;
1317 var trueify = function trueify() {
1318 return true;
1319 };
1320 var falsify = function falsify() {
1321 return false;
1322 };
1323 var zeroify = function zeroify() {
1324 return 0;
1325 };
1326 var noop$1 = function noop() {};
1327 var error = function error(msg) {
1328 throw new Error(msg);
1329 };
1330 var warnings = function warnings(enabled) {
1331 if (enabled !== undefined) {
1332 warningsEnabled = !!enabled;
1333 } else {
1334 return warningsEnabled;
1335 }
1336 };
1337 var warn = function warn(msg) {
1338 /* eslint-disable no-console */
1339 if (!warnings()) {
1340 return;
1341 }
1342 if (warnSupported) {
1343 console.warn(msg);
1344 } else {
1345 console.log(msg);
1346 if (traceSupported) {
1347 console.trace();
1348 }
1349 }
1350 }; /* eslint-enable */
1351
1352 var clone = function clone(obj) {
1353 return extend({}, obj);
1354 };
1355
1356 // gets a shallow copy of the argument
1357 var copy = function copy(obj) {
1358 if (obj == null) {
1359 return obj;
1360 }
1361 if (array(obj)) {
1362 return obj.slice();
1363 } else if (plainObject(obj)) {
1364 return clone(obj);
1365 } else {
1366 return obj;
1367 }
1368 };
1369 var copyArray$1 = function copyArray(arr) {
1370 return arr.slice();
1371 };
1372 var uuid = function uuid(a, b /* placeholders */) {
1373 for (
1374 // loop :)
1375 b = a = '';
1376 // b - result , a - numeric letiable
1377 a++ < 36;
1378 //
1379 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
1380 ?
1381 // return a random number or 4
1382 (a ^ 15 // if "a" is not 15
1383 ?
1384 // generate a random number from 0 to 15
1385 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
1386 : 4 // otherwise 4
1387 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
1388 ) {
1389 }
1390 return b;
1391 };
1392 var _staticEmptyObject = {};
1393 var staticEmptyObject = function staticEmptyObject() {
1394 return _staticEmptyObject;
1395 };
1396 var defaults$g = function defaults(_defaults) {
1397 var keys = Object.keys(_defaults);
1398 return function (opts) {
1399 var filledOpts = {};
1400 for (var i = 0; i < keys.length; i++) {
1401 var key = keys[i];
1402 var optVal = opts == null ? undefined : opts[key];
1403 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
1404 }
1405 return filledOpts;
1406 };
1407 };
1408 var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
1409 for (var i = arr.length - 1; i >= 0; i--) {
1410 if (arr[i] === ele) {
1411 arr.splice(i, 1);
1412 if (oneCopy) {
1413 break;
1414 }
1415 }
1416 }
1417 };
1418 var clearArray = function clearArray(arr) {
1419 arr.splice(0, arr.length);
1420 };
1421 var push = function push(arr, otherArr) {
1422 for (var i = 0; i < otherArr.length; i++) {
1423 var el = otherArr[i];
1424 arr.push(el);
1425 }
1426 };
1427 var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
1428 if (prefix) {
1429 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1430 }
1431
1432 return obj[propName];
1433 };
1434 var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
1435 if (prefix) {
1436 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
1437 }
1438
1439 obj[propName] = value;
1440 };
1441
1442 /* global Map */
1443 var ObjectMap = /*#__PURE__*/function () {
1444 function ObjectMap() {
1445 _classCallCheck(this, ObjectMap);
1446 this._obj = {};
1447 }
1448 _createClass(ObjectMap, [{
1449 key: "set",
1450 value: function set(key, val) {
1451 this._obj[key] = val;
1452 return this;
1453 }
1454 }, {
1455 key: "delete",
1456 value: function _delete(key) {
1457 this._obj[key] = undefined;
1458 return this;
1459 }
1460 }, {
1461 key: "clear",
1462 value: function clear() {
1463 this._obj = {};
1464 }
1465 }, {
1466 key: "has",
1467 value: function has(key) {
1468 return this._obj[key] !== undefined;
1469 }
1470 }, {
1471 key: "get",
1472 value: function get(key) {
1473 return this._obj[key];
1474 }
1475 }]);
1476 return ObjectMap;
1477 }();
1478 var Map$2 = typeof Map !== 'undefined' ? Map : ObjectMap;
1479
1480 /* global Set */
1481
1482 var undef = "undefined" ;
1483 var ObjectSet = /*#__PURE__*/function () {
1484 function ObjectSet(arrayOrObjectSet) {
1485 _classCallCheck(this, ObjectSet);
1486 this._obj = Object.create(null);
1487 this.size = 0;
1488 if (arrayOrObjectSet != null) {
1489 var arr;
1490 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1491 arr = arrayOrObjectSet.toArray();
1492 } else {
1493 arr = arrayOrObjectSet;
1494 }
1495 for (var i = 0; i < arr.length; i++) {
1496 this.add(arr[i]);
1497 }
1498 }
1499 }
1500 _createClass(ObjectSet, [{
1501 key: "instanceString",
1502 value: function instanceString() {
1503 return 'set';
1504 }
1505 }, {
1506 key: "add",
1507 value: function add(val) {
1508 var o = this._obj;
1509 if (o[val] !== 1) {
1510 o[val] = 1;
1511 this.size++;
1512 }
1513 }
1514 }, {
1515 key: "delete",
1516 value: function _delete(val) {
1517 var o = this._obj;
1518 if (o[val] === 1) {
1519 o[val] = 0;
1520 this.size--;
1521 }
1522 }
1523 }, {
1524 key: "clear",
1525 value: function clear() {
1526 this._obj = Object.create(null);
1527 }
1528 }, {
1529 key: "has",
1530 value: function has(val) {
1531 return this._obj[val] === 1;
1532 }
1533 }, {
1534 key: "toArray",
1535 value: function toArray() {
1536 var _this = this;
1537 return Object.keys(this._obj).filter(function (key) {
1538 return _this.has(key);
1539 });
1540 }
1541 }, {
1542 key: "forEach",
1543 value: function forEach(callback, thisArg) {
1544 return this.toArray().forEach(callback, thisArg);
1545 }
1546 }]);
1547 return ObjectSet;
1548 }();
1549 var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1550
1551 // represents a node or an edge
1552 var Element = function Element(cy, params) {
1553 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1554 if (cy === undefined || params === undefined || !core(cy)) {
1555 error('An element must have a core reference and parameters set');
1556 return;
1557 }
1558 var group = params.group;
1559
1560 // try to automatically infer the group if unspecified
1561 if (group == null) {
1562 if (params.data && params.data.source != null && params.data.target != null) {
1563 group = 'edges';
1564 } else {
1565 group = 'nodes';
1566 }
1567 }
1568
1569 // validate group
1570 if (group !== 'nodes' && group !== 'edges') {
1571 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1572 return;
1573 }
1574
1575 // make the element array-like, just like a collection
1576 this.length = 1;
1577 this[0] = this;
1578
1579 // NOTE: when something is added here, add also to ele.json()
1580 var _p = this._private = {
1581 cy: cy,
1582 single: true,
1583 // indicates this is an element
1584 data: params.data || {},
1585 // data object
1586 position: params.position || {
1587 x: 0,
1588 y: 0
1589 },
1590 // (x, y) position pair
1591 autoWidth: undefined,
1592 // width and height of nodes calculated by the renderer when set to special 'auto' value
1593 autoHeight: undefined,
1594 autoPadding: undefined,
1595 compoundBoundsClean: false,
1596 // whether the compound dimensions need to be recalculated the next time dimensions are read
1597 listeners: [],
1598 // array of bound listeners
1599 group: group,
1600 // string; 'nodes' or 'edges'
1601 style: {},
1602 // properties as set by the style
1603 rstyle: {},
1604 // properties for style sent from the renderer to the core
1605 styleCxts: [],
1606 // applied style contexts from the styler
1607 styleKeys: {},
1608 // per-group keys of style property values
1609 removed: true,
1610 // whether it's inside the vis; true if removed (set true here since we call restore)
1611 selected: params.selected ? true : false,
1612 // whether it's selected
1613 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1614 // whether it's selectable
1615 locked: params.locked ? true : false,
1616 // whether the element is locked (cannot be moved)
1617 grabbed: false,
1618 // whether the element is grabbed by the mouse; renderer sets this privately
1619 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1620 // whether the element can be grabbed
1621 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1622 // whether the element has passthrough panning enabled
1623 active: false,
1624 // whether the element is active from user interaction
1625 classes: new Set$1(),
1626 // map ( className => true )
1627 animation: {
1628 // object for currently-running animations
1629 current: [],
1630 queue: []
1631 },
1632 rscratch: {},
1633 // object in which the renderer can store information
1634 scratch: params.scratch || {},
1635 // scratch objects
1636 edges: [],
1637 // array of connected edges
1638 children: [],
1639 // array of children
1640 parent: params.parent && params.parent.isNode() ? params.parent : null,
1641 // parent ref
1642 traversalCache: {},
1643 // cache of output of traversal functions
1644 backgrounding: false,
1645 // whether background images are loading
1646 bbCache: null,
1647 // cache of the current bounding box
1648 bbCacheShift: {
1649 x: 0,
1650 y: 0
1651 },
1652 // shift applied to cached bb to be applied on next get
1653 bodyBounds: null,
1654 // bounds cache of element body, w/o overlay
1655 overlayBounds: null,
1656 // bounds cache of element body, including overlay
1657 labelBounds: {
1658 // bounds cache of labels
1659 all: null,
1660 source: null,
1661 target: null,
1662 main: null
1663 },
1664 arrowBounds: {
1665 // bounds cache of edge arrows
1666 source: null,
1667 target: null,
1668 'mid-source': null,
1669 'mid-target': null
1670 }
1671 };
1672 if (_p.position.x == null) {
1673 _p.position.x = 0;
1674 }
1675 if (_p.position.y == null) {
1676 _p.position.y = 0;
1677 }
1678
1679 // renderedPosition overrides if specified
1680 if (params.renderedPosition) {
1681 var rpos = params.renderedPosition;
1682 var pan = cy.pan();
1683 var zoom = cy.zoom();
1684 _p.position = {
1685 x: (rpos.x - pan.x) / zoom,
1686 y: (rpos.y - pan.y) / zoom
1687 };
1688 }
1689 var classes = [];
1690 if (array(params.classes)) {
1691 classes = params.classes;
1692 } else if (string(params.classes)) {
1693 classes = params.classes.split(/\s+/);
1694 }
1695 for (var i = 0, l = classes.length; i < l; i++) {
1696 var cls = classes[i];
1697 if (!cls || cls === '') {
1698 continue;
1699 }
1700 _p.classes.add(cls);
1701 }
1702 this.createEmitter();
1703 var bypass = params.style || params.css;
1704 if (bypass) {
1705 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1706 this.style(bypass);
1707 }
1708 if (restore === undefined || restore) {
1709 this.restore();
1710 }
1711 };
1712
1713 var defineSearch = function defineSearch(params) {
1714 params = {
1715 bfs: params.bfs || !params.dfs,
1716 dfs: params.dfs || !params.bfs
1717 };
1718
1719 // from pseudocode on wikipedia
1720 return function searchFn(roots, fn, directed) {
1721 var options;
1722 if (plainObject(roots) && !elementOrCollection(roots)) {
1723 options = roots;
1724 roots = options.roots || options.root;
1725 fn = options.visit;
1726 directed = options.directed;
1727 }
1728 directed = arguments.length === 2 && !fn$6(fn) ? fn : directed;
1729 fn = fn$6(fn) ? fn : function () {};
1730 var cy = this._private.cy;
1731 var v = roots = string(roots) ? this.filter(roots) : roots;
1732 var Q = [];
1733 var connectedNodes = [];
1734 var connectedBy = {};
1735 var id2depth = {};
1736 var V = {};
1737 var j = 0;
1738 var found;
1739 var _this$byGroup = this.byGroup(),
1740 nodes = _this$byGroup.nodes,
1741 edges = _this$byGroup.edges;
1742
1743 // enqueue v
1744 for (var i = 0; i < v.length; i++) {
1745 var vi = v[i];
1746 var viId = vi.id();
1747 if (vi.isNode()) {
1748 Q.unshift(vi);
1749 if (params.bfs) {
1750 V[viId] = true;
1751 connectedNodes.push(vi);
1752 }
1753 id2depth[viId] = 0;
1754 }
1755 }
1756 var _loop = function _loop() {
1757 var v = params.bfs ? Q.shift() : Q.pop();
1758 var vId = v.id();
1759 if (params.dfs) {
1760 if (V[vId]) {
1761 return "continue";
1762 }
1763 V[vId] = true;
1764 connectedNodes.push(v);
1765 }
1766 var depth = id2depth[vId];
1767 var prevEdge = connectedBy[vId];
1768 var src = prevEdge != null ? prevEdge.source() : null;
1769 var tgt = prevEdge != null ? prevEdge.target() : null;
1770 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1771 var ret = void 0;
1772 ret = fn(v, prevEdge, prevNode, j++, depth);
1773 if (ret === true) {
1774 found = v;
1775 return "break";
1776 }
1777 if (ret === false) {
1778 return "break";
1779 }
1780 var vwEdges = v.connectedEdges().filter(function (e) {
1781 return (!directed || e.source().same(v)) && edges.has(e);
1782 });
1783 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1784 var e = vwEdges[_i2];
1785 var w = e.connectedNodes().filter(function (n) {
1786 return !n.same(v) && nodes.has(n);
1787 });
1788 var wId = w.id();
1789 if (w.length !== 0 && !V[wId]) {
1790 w = w[0];
1791 Q.push(w);
1792 if (params.bfs) {
1793 V[wId] = true;
1794 connectedNodes.push(w);
1795 }
1796 connectedBy[wId] = e;
1797 id2depth[wId] = id2depth[vId] + 1;
1798 }
1799 }
1800 };
1801 while (Q.length !== 0) {
1802 var _ret = _loop();
1803 if (_ret === "continue") continue;
1804 if (_ret === "break") break;
1805 }
1806 var connectedEles = cy.collection();
1807 for (var _i = 0; _i < connectedNodes.length; _i++) {
1808 var node = connectedNodes[_i];
1809 var edge = connectedBy[node.id()];
1810 if (edge != null) {
1811 connectedEles.push(edge);
1812 }
1813 connectedEles.push(node);
1814 }
1815 return {
1816 path: cy.collection(connectedEles),
1817 found: cy.collection(found)
1818 };
1819 };
1820 };
1821
1822 // search, spanning trees, etc
1823 var elesfn$v = {
1824 breadthFirstSearch: defineSearch({
1825 bfs: true
1826 }),
1827 depthFirstSearch: defineSearch({
1828 dfs: true
1829 })
1830 };
1831
1832 // nice, short mathematical alias
1833 elesfn$v.bfs = elesfn$v.breadthFirstSearch;
1834 elesfn$v.dfs = elesfn$v.depthFirstSearch;
1835
1836 var heap$1 = createCommonjsModule(function (module, exports) {
1837 // Generated by CoffeeScript 1.8.0
1838 (function() {
1839 var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
1840
1841 floor = Math.floor, min = Math.min;
1842
1843
1844 /*
1845 Default comparison function to be used
1846 */
1847
1848 defaultCmp = function(x, y) {
1849 if (x < y) {
1850 return -1;
1851 }
1852 if (x > y) {
1853 return 1;
1854 }
1855 return 0;
1856 };
1857
1858
1859 /*
1860 Insert item x in list a, and keep it sorted assuming a is sorted.
1861
1862 If x is already in a, insert it to the right of the rightmost x.
1863
1864 Optional args lo (default 0) and hi (default a.length) bound the slice
1865 of a to be searched.
1866 */
1867
1868 insort = function(a, x, lo, hi, cmp) {
1869 var mid;
1870 if (lo == null) {
1871 lo = 0;
1872 }
1873 if (cmp == null) {
1874 cmp = defaultCmp;
1875 }
1876 if (lo < 0) {
1877 throw new Error('lo must be non-negative');
1878 }
1879 if (hi == null) {
1880 hi = a.length;
1881 }
1882 while (lo < hi) {
1883 mid = floor((lo + hi) / 2);
1884 if (cmp(x, a[mid]) < 0) {
1885 hi = mid;
1886 } else {
1887 lo = mid + 1;
1888 }
1889 }
1890 return ([].splice.apply(a, [lo, lo - lo].concat(x)), x);
1891 };
1892
1893
1894 /*
1895 Push item onto heap, maintaining the heap invariant.
1896 */
1897
1898 heappush = function(array, item, cmp) {
1899 if (cmp == null) {
1900 cmp = defaultCmp;
1901 }
1902 array.push(item);
1903 return _siftdown(array, 0, array.length - 1, cmp);
1904 };
1905
1906
1907 /*
1908 Pop the smallest item off the heap, maintaining the heap invariant.
1909 */
1910
1911 heappop = function(array, cmp) {
1912 var lastelt, returnitem;
1913 if (cmp == null) {
1914 cmp = defaultCmp;
1915 }
1916 lastelt = array.pop();
1917 if (array.length) {
1918 returnitem = array[0];
1919 array[0] = lastelt;
1920 _siftup(array, 0, cmp);
1921 } else {
1922 returnitem = lastelt;
1923 }
1924 return returnitem;
1925 };
1926
1927
1928 /*
1929 Pop and return the current smallest value, and add the new item.
1930
1931 This is more efficient than heappop() followed by heappush(), and can be
1932 more appropriate when using a fixed size heap. Note that the value
1933 returned may be larger than item! That constrains reasonable use of
1934 this routine unless written as part of a conditional replacement:
1935 if item > array[0]
1936 item = heapreplace(array, item)
1937 */
1938
1939 heapreplace = function(array, item, cmp) {
1940 var returnitem;
1941 if (cmp == null) {
1942 cmp = defaultCmp;
1943 }
1944 returnitem = array[0];
1945 array[0] = item;
1946 _siftup(array, 0, cmp);
1947 return returnitem;
1948 };
1949
1950
1951 /*
1952 Fast version of a heappush followed by a heappop.
1953 */
1954
1955 heappushpop = function(array, item, cmp) {
1956 var _ref;
1957 if (cmp == null) {
1958 cmp = defaultCmp;
1959 }
1960 if (array.length && cmp(array[0], item) < 0) {
1961 _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
1962 _siftup(array, 0, cmp);
1963 }
1964 return item;
1965 };
1966
1967
1968 /*
1969 Transform list into a heap, in-place, in O(array.length) time.
1970 */
1971
1972 heapify = function(array, cmp) {
1973 var i, _i, _len, _ref1, _results, _results1;
1974 if (cmp == null) {
1975 cmp = defaultCmp;
1976 }
1977 _ref1 = (function() {
1978 _results1 = [];
1979 for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--){ _results1.push(_j); }
1980 return _results1;
1981 }).apply(this).reverse();
1982 _results = [];
1983 for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
1984 i = _ref1[_i];
1985 _results.push(_siftup(array, i, cmp));
1986 }
1987 return _results;
1988 };
1989
1990
1991 /*
1992 Update the position of the given item in the heap.
1993 This function should be called every time the item is being modified.
1994 */
1995
1996 updateItem = function(array, item, cmp) {
1997 var pos;
1998 if (cmp == null) {
1999 cmp = defaultCmp;
2000 }
2001 pos = array.indexOf(item);
2002 if (pos === -1) {
2003 return;
2004 }
2005 _siftdown(array, 0, pos, cmp);
2006 return _siftup(array, pos, cmp);
2007 };
2008
2009
2010 /*
2011 Find the n largest elements in a dataset.
2012 */
2013
2014 nlargest = function(array, n, cmp) {
2015 var elem, result, _i, _len, _ref;
2016 if (cmp == null) {
2017 cmp = defaultCmp;
2018 }
2019 result = array.slice(0, n);
2020 if (!result.length) {
2021 return result;
2022 }
2023 heapify(result, cmp);
2024 _ref = array.slice(n);
2025 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2026 elem = _ref[_i];
2027 heappushpop(result, elem, cmp);
2028 }
2029 return result.sort(cmp).reverse();
2030 };
2031
2032
2033 /*
2034 Find the n smallest elements in a dataset.
2035 */
2036
2037 nsmallest = function(array, n, cmp) {
2038 var elem, los, result, _i, _j, _len, _ref, _ref1, _results;
2039 if (cmp == null) {
2040 cmp = defaultCmp;
2041 }
2042 if (n * 10 <= array.length) {
2043 result = array.slice(0, n).sort(cmp);
2044 if (!result.length) {
2045 return result;
2046 }
2047 los = result[result.length - 1];
2048 _ref = array.slice(n);
2049 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2050 elem = _ref[_i];
2051 if (cmp(elem, los) < 0) {
2052 insort(result, elem, 0, null, cmp);
2053 result.pop();
2054 los = result[result.length - 1];
2055 }
2056 }
2057 return result;
2058 }
2059 heapify(array, cmp);
2060 _results = [];
2061 for (_j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; 0 <= _ref1 ? ++_j : --_j) {
2062 _results.push(heappop(array, cmp));
2063 }
2064 return _results;
2065 };
2066
2067 _siftdown = function(array, startpos, pos, cmp) {
2068 var newitem, parent, parentpos;
2069 if (cmp == null) {
2070 cmp = defaultCmp;
2071 }
2072 newitem = array[pos];
2073 while (pos > startpos) {
2074 parentpos = (pos - 1) >> 1;
2075 parent = array[parentpos];
2076 if (cmp(newitem, parent) < 0) {
2077 array[pos] = parent;
2078 pos = parentpos;
2079 continue;
2080 }
2081 break;
2082 }
2083 return array[pos] = newitem;
2084 };
2085
2086 _siftup = function(array, pos, cmp) {
2087 var childpos, endpos, newitem, rightpos, startpos;
2088 if (cmp == null) {
2089 cmp = defaultCmp;
2090 }
2091 endpos = array.length;
2092 startpos = pos;
2093 newitem = array[pos];
2094 childpos = 2 * pos + 1;
2095 while (childpos < endpos) {
2096 rightpos = childpos + 1;
2097 if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
2098 childpos = rightpos;
2099 }
2100 array[pos] = array[childpos];
2101 pos = childpos;
2102 childpos = 2 * pos + 1;
2103 }
2104 array[pos] = newitem;
2105 return _siftdown(array, startpos, pos, cmp);
2106 };
2107
2108 Heap = (function() {
2109 Heap.push = heappush;
2110
2111 Heap.pop = heappop;
2112
2113 Heap.replace = heapreplace;
2114
2115 Heap.pushpop = heappushpop;
2116
2117 Heap.heapify = heapify;
2118
2119 Heap.updateItem = updateItem;
2120
2121 Heap.nlargest = nlargest;
2122
2123 Heap.nsmallest = nsmallest;
2124
2125 function Heap(cmp) {
2126 this.cmp = cmp != null ? cmp : defaultCmp;
2127 this.nodes = [];
2128 }
2129
2130 Heap.prototype.push = function(x) {
2131 return heappush(this.nodes, x, this.cmp);
2132 };
2133
2134 Heap.prototype.pop = function() {
2135 return heappop(this.nodes, this.cmp);
2136 };
2137
2138 Heap.prototype.peek = function() {
2139 return this.nodes[0];
2140 };
2141
2142 Heap.prototype.contains = function(x) {
2143 return this.nodes.indexOf(x) !== -1;
2144 };
2145
2146 Heap.prototype.replace = function(x) {
2147 return heapreplace(this.nodes, x, this.cmp);
2148 };
2149
2150 Heap.prototype.pushpop = function(x) {
2151 return heappushpop(this.nodes, x, this.cmp);
2152 };
2153
2154 Heap.prototype.heapify = function() {
2155 return heapify(this.nodes, this.cmp);
2156 };
2157
2158 Heap.prototype.updateItem = function(x) {
2159 return updateItem(this.nodes, x, this.cmp);
2160 };
2161
2162 Heap.prototype.clear = function() {
2163 return this.nodes = [];
2164 };
2165
2166 Heap.prototype.empty = function() {
2167 return this.nodes.length === 0;
2168 };
2169
2170 Heap.prototype.size = function() {
2171 return this.nodes.length;
2172 };
2173
2174 Heap.prototype.clone = function() {
2175 var heap;
2176 heap = new Heap();
2177 heap.nodes = this.nodes.slice(0);
2178 return heap;
2179 };
2180
2181 Heap.prototype.toArray = function() {
2182 return this.nodes.slice(0);
2183 };
2184
2185 Heap.prototype.insert = Heap.prototype.push;
2186
2187 Heap.prototype.top = Heap.prototype.peek;
2188
2189 Heap.prototype.front = Heap.prototype.peek;
2190
2191 Heap.prototype.has = Heap.prototype.contains;
2192
2193 Heap.prototype.copy = Heap.prototype.clone;
2194
2195 return Heap;
2196
2197 })();
2198
2199 (function(root, factory) {
2200 {
2201 return module.exports = factory();
2202 }
2203 })(this, function() {
2204 return Heap;
2205 });
2206
2207 }).call(commonjsGlobal);
2208 });
2209
2210 var heap = heap$1;
2211
2212 var dijkstraDefaults = defaults$g({
2213 root: null,
2214 weight: function weight(edge) {
2215 return 1;
2216 },
2217 directed: false
2218 });
2219 var elesfn$u = {
2220 dijkstra: function dijkstra(options) {
2221 if (!plainObject(options)) {
2222 var args = arguments;
2223 options = {
2224 root: args[0],
2225 weight: args[1],
2226 directed: args[2]
2227 };
2228 }
2229 var _dijkstraDefaults = dijkstraDefaults(options),
2230 root = _dijkstraDefaults.root,
2231 weight = _dijkstraDefaults.weight,
2232 directed = _dijkstraDefaults.directed;
2233 var eles = this;
2234 var weightFn = weight;
2235 var source = string(root) ? this.filter(root)[0] : root[0];
2236 var dist = {};
2237 var prev = {};
2238 var knownDist = {};
2239 var _this$byGroup = this.byGroup(),
2240 nodes = _this$byGroup.nodes,
2241 edges = _this$byGroup.edges;
2242 edges.unmergeBy(function (ele) {
2243 return ele.isLoop();
2244 });
2245 var getDist = function getDist(node) {
2246 return dist[node.id()];
2247 };
2248 var setDist = function setDist(node, d) {
2249 dist[node.id()] = d;
2250 Q.updateItem(node);
2251 };
2252 var Q = new heap(function (a, b) {
2253 return getDist(a) - getDist(b);
2254 });
2255 for (var i = 0; i < nodes.length; i++) {
2256 var node = nodes[i];
2257 dist[node.id()] = node.same(source) ? 0 : Infinity;
2258 Q.push(node);
2259 }
2260 var distBetween = function distBetween(u, v) {
2261 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
2262 var smallestDistance = Infinity;
2263 var smallestEdge;
2264 for (var _i = 0; _i < uvs.length; _i++) {
2265 var edge = uvs[_i];
2266 var _weight = weightFn(edge);
2267 if (_weight < smallestDistance || !smallestEdge) {
2268 smallestDistance = _weight;
2269 smallestEdge = edge;
2270 }
2271 }
2272 return {
2273 edge: smallestEdge,
2274 dist: smallestDistance
2275 };
2276 };
2277 while (Q.size() > 0) {
2278 var u = Q.pop();
2279 var smalletsDist = getDist(u);
2280 var uid = u.id();
2281 knownDist[uid] = smalletsDist;
2282 if (smalletsDist === Infinity) {
2283 continue;
2284 }
2285 var neighbors = u.neighborhood().intersect(nodes);
2286 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
2287 var v = neighbors[_i2];
2288 var vid = v.id();
2289 var vDist = distBetween(u, v);
2290 var alt = smalletsDist + vDist.dist;
2291 if (alt < getDist(v)) {
2292 setDist(v, alt);
2293 prev[vid] = {
2294 node: u,
2295 edge: vDist.edge
2296 };
2297 }
2298 } // for
2299 } // while
2300
2301 return {
2302 distanceTo: function distanceTo(node) {
2303 var target = string(node) ? nodes.filter(node)[0] : node[0];
2304 return knownDist[target.id()];
2305 },
2306 pathTo: function pathTo(node) {
2307 var target = string(node) ? nodes.filter(node)[0] : node[0];
2308 var S = [];
2309 var u = target;
2310 var uid = u.id();
2311 if (target.length > 0) {
2312 S.unshift(target);
2313 while (prev[uid]) {
2314 var p = prev[uid];
2315 S.unshift(p.edge);
2316 S.unshift(p.node);
2317 u = p.node;
2318 uid = u.id();
2319 }
2320 }
2321 return eles.spawn(S);
2322 }
2323 };
2324 }
2325 };
2326
2327 var elesfn$t = {
2328 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
2329 // implemented from pseudocode from wikipedia
2330 kruskal: function kruskal(weightFn) {
2331 weightFn = weightFn || function (edge) {
2332 return 1;
2333 };
2334 var _this$byGroup = this.byGroup(),
2335 nodes = _this$byGroup.nodes,
2336 edges = _this$byGroup.edges;
2337 var numNodes = nodes.length;
2338 var forest = new Array(numNodes);
2339 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
2340
2341 var findSetIndex = function findSetIndex(ele) {
2342 for (var i = 0; i < forest.length; i++) {
2343 var eles = forest[i];
2344 if (eles.has(ele)) {
2345 return i;
2346 }
2347 }
2348 };
2349
2350 // start with one forest per node
2351 for (var i = 0; i < numNodes; i++) {
2352 forest[i] = this.spawn(nodes[i]);
2353 }
2354 var S = edges.sort(function (a, b) {
2355 return weightFn(a) - weightFn(b);
2356 });
2357 for (var _i = 0; _i < S.length; _i++) {
2358 var edge = S[_i];
2359 var u = edge.source()[0];
2360 var v = edge.target()[0];
2361 var setUIndex = findSetIndex(u);
2362 var setVIndex = findSetIndex(v);
2363 var setU = forest[setUIndex];
2364 var setV = forest[setVIndex];
2365 if (setUIndex !== setVIndex) {
2366 A.merge(edge);
2367
2368 // combine forests for u and v
2369 setU.merge(setV);
2370 forest.splice(setVIndex, 1);
2371 }
2372 }
2373 return A;
2374 }
2375 };
2376
2377 var aStarDefaults = defaults$g({
2378 root: null,
2379 goal: null,
2380 weight: function weight(edge) {
2381 return 1;
2382 },
2383 heuristic: function heuristic(edge) {
2384 return 0;
2385 },
2386 directed: false
2387 });
2388 var elesfn$s = {
2389 // Implemented from pseudocode from wikipedia
2390 aStar: function aStar(options) {
2391 var cy = this.cy();
2392 var _aStarDefaults = aStarDefaults(options),
2393 root = _aStarDefaults.root,
2394 goal = _aStarDefaults.goal,
2395 heuristic = _aStarDefaults.heuristic,
2396 directed = _aStarDefaults.directed,
2397 weight = _aStarDefaults.weight;
2398 root = cy.collection(root)[0];
2399 goal = cy.collection(goal)[0];
2400 var sid = root.id();
2401 var tid = goal.id();
2402 var gScore = {};
2403 var fScore = {};
2404 var closedSetIds = {};
2405 var openSet = new heap(function (a, b) {
2406 return fScore[a.id()] - fScore[b.id()];
2407 });
2408 var openSetIds = new Set$1();
2409 var cameFrom = {};
2410 var cameFromEdge = {};
2411 var addToOpenSet = function addToOpenSet(ele, id) {
2412 openSet.push(ele);
2413 openSetIds.add(id);
2414 };
2415 var cMin, cMinId;
2416 var popFromOpenSet = function popFromOpenSet() {
2417 cMin = openSet.pop();
2418 cMinId = cMin.id();
2419 openSetIds["delete"](cMinId);
2420 };
2421 var isInOpenSet = function isInOpenSet(id) {
2422 return openSetIds.has(id);
2423 };
2424 addToOpenSet(root, sid);
2425 gScore[sid] = 0;
2426 fScore[sid] = heuristic(root);
2427
2428 // Counter
2429 var steps = 0;
2430
2431 // Main loop
2432 while (openSet.size() > 0) {
2433 popFromOpenSet();
2434 steps++;
2435
2436 // If we've found our goal, then we are done
2437 if (cMinId === tid) {
2438 var path = [];
2439 var pathNode = goal;
2440 var pathNodeId = tid;
2441 var pathEdge = cameFromEdge[pathNodeId];
2442 for (;;) {
2443 path.unshift(pathNode);
2444 if (pathEdge != null) {
2445 path.unshift(pathEdge);
2446 }
2447 pathNode = cameFrom[pathNodeId];
2448 if (pathNode == null) {
2449 break;
2450 }
2451 pathNodeId = pathNode.id();
2452 pathEdge = cameFromEdge[pathNodeId];
2453 }
2454 return {
2455 found: true,
2456 distance: gScore[cMinId],
2457 path: this.spawn(path),
2458 steps: steps
2459 };
2460 }
2461
2462 // Add cMin to processed nodes
2463 closedSetIds[cMinId] = true;
2464
2465 // Update scores for neighbors of cMin
2466 // Take into account if graph is directed or not
2467 var vwEdges = cMin._private.edges;
2468 for (var i = 0; i < vwEdges.length; i++) {
2469 var e = vwEdges[i];
2470
2471 // edge must be in set of calling eles
2472 if (!this.hasElementWithId(e.id())) {
2473 continue;
2474 }
2475
2476 // cMin must be the source of edge if directed
2477 if (directed && e.data('source') !== cMinId) {
2478 continue;
2479 }
2480 var wSrc = e.source();
2481 var wTgt = e.target();
2482 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
2483 var wid = w.id();
2484
2485 // node must be in set of calling eles
2486 if (!this.hasElementWithId(wid)) {
2487 continue;
2488 }
2489
2490 // if node is in closedSet, ignore it
2491 if (closedSetIds[wid]) {
2492 continue;
2493 }
2494
2495 // New tentative score for node w
2496 var tempScore = gScore[cMinId] + weight(e);
2497
2498 // Update gScore for node w if:
2499 // w not present in openSet
2500 // OR
2501 // tentative gScore is less than previous value
2502
2503 // w not in openSet
2504 if (!isInOpenSet(wid)) {
2505 gScore[wid] = tempScore;
2506 fScore[wid] = tempScore + heuristic(w);
2507 addToOpenSet(w, wid);
2508 cameFrom[wid] = cMin;
2509 cameFromEdge[wid] = e;
2510 continue;
2511 }
2512
2513 // w already in openSet, but with greater gScore
2514 if (tempScore < gScore[wid]) {
2515 gScore[wid] = tempScore;
2516 fScore[wid] = tempScore + heuristic(w);
2517 cameFrom[wid] = cMin;
2518 cameFromEdge[wid] = e;
2519 }
2520 } // End of neighbors update
2521 } // End of main loop
2522
2523 // If we've reached here, then we've not reached our goal
2524 return {
2525 found: false,
2526 distance: undefined,
2527 path: undefined,
2528 steps: steps
2529 };
2530 }
2531 }; // elesfn
2532
2533 var floydWarshallDefaults = defaults$g({
2534 weight: function weight(edge) {
2535 return 1;
2536 },
2537 directed: false
2538 });
2539 var elesfn$r = {
2540 // Implemented from pseudocode from wikipedia
2541 floydWarshall: function floydWarshall(options) {
2542 var cy = this.cy();
2543 var _floydWarshallDefault = floydWarshallDefaults(options),
2544 weight = _floydWarshallDefault.weight,
2545 directed = _floydWarshallDefault.directed;
2546 var weightFn = weight;
2547 var _this$byGroup = this.byGroup(),
2548 nodes = _this$byGroup.nodes,
2549 edges = _this$byGroup.edges;
2550 var N = nodes.length;
2551 var Nsq = N * N;
2552 var indexOf = function indexOf(node) {
2553 return nodes.indexOf(node);
2554 };
2555 var atIndex = function atIndex(i) {
2556 return nodes[i];
2557 };
2558
2559 // Initialize distance matrix
2560 var dist = new Array(Nsq);
2561 for (var n = 0; n < Nsq; n++) {
2562 var j = n % N;
2563 var i = (n - j) / N;
2564 if (i === j) {
2565 dist[n] = 0;
2566 } else {
2567 dist[n] = Infinity;
2568 }
2569 }
2570
2571 // Initialize matrix used for path reconstruction
2572 // Initialize distance matrix
2573 var next = new Array(Nsq);
2574 var edgeNext = new Array(Nsq);
2575
2576 // Process edges
2577 for (var _i = 0; _i < edges.length; _i++) {
2578 var edge = edges[_i];
2579 var src = edge.source()[0];
2580 var tgt = edge.target()[0];
2581 if (src === tgt) {
2582 continue;
2583 } // exclude loops
2584
2585 var s = indexOf(src);
2586 var t = indexOf(tgt);
2587 var st = s * N + t; // source to target index
2588 var _weight = weightFn(edge);
2589
2590 // Check if already process another edge between same 2 nodes
2591 if (dist[st] > _weight) {
2592 dist[st] = _weight;
2593 next[st] = t;
2594 edgeNext[st] = edge;
2595 }
2596
2597 // If undirected graph, process 'reversed' edge
2598 if (!directed) {
2599 var ts = t * N + s; // target to source index
2600
2601 if (!directed && dist[ts] > _weight) {
2602 dist[ts] = _weight;
2603 next[ts] = s;
2604 edgeNext[ts] = edge;
2605 }
2606 }
2607 }
2608
2609 // Main loop
2610 for (var k = 0; k < N; k++) {
2611 for (var _i2 = 0; _i2 < N; _i2++) {
2612 var ik = _i2 * N + k;
2613 for (var _j = 0; _j < N; _j++) {
2614 var ij = _i2 * N + _j;
2615 var kj = k * N + _j;
2616 if (dist[ik] + dist[kj] < dist[ij]) {
2617 dist[ij] = dist[ik] + dist[kj];
2618 next[ij] = next[ik];
2619 }
2620 }
2621 }
2622 }
2623 var getArgEle = function getArgEle(ele) {
2624 return (string(ele) ? cy.filter(ele) : ele)[0];
2625 };
2626 var indexOfArgEle = function indexOfArgEle(ele) {
2627 return indexOf(getArgEle(ele));
2628 };
2629 var res = {
2630 distance: function distance(from, to) {
2631 var i = indexOfArgEle(from);
2632 var j = indexOfArgEle(to);
2633 return dist[i * N + j];
2634 },
2635 path: function path(from, to) {
2636 var i = indexOfArgEle(from);
2637 var j = indexOfArgEle(to);
2638 var fromNode = atIndex(i);
2639 if (i === j) {
2640 return fromNode.collection();
2641 }
2642 if (next[i * N + j] == null) {
2643 return cy.collection();
2644 }
2645 var path = cy.collection();
2646 var prev = i;
2647 var edge;
2648 path.merge(fromNode);
2649 while (i !== j) {
2650 prev = i;
2651 i = next[i * N + j];
2652 edge = edgeNext[prev * N + i];
2653 path.merge(edge);
2654 path.merge(atIndex(i));
2655 }
2656 return path;
2657 }
2658 };
2659 return res;
2660 } // floydWarshall
2661 }; // elesfn
2662
2663 var bellmanFordDefaults = defaults$g({
2664 weight: function weight(edge) {
2665 return 1;
2666 },
2667 directed: false,
2668 root: null
2669 });
2670 var elesfn$q = {
2671 // Implemented from pseudocode from wikipedia
2672 bellmanFord: function bellmanFord(options) {
2673 var _this = this;
2674 var _bellmanFordDefaults = bellmanFordDefaults(options),
2675 weight = _bellmanFordDefaults.weight,
2676 directed = _bellmanFordDefaults.directed,
2677 root = _bellmanFordDefaults.root;
2678 var weightFn = weight;
2679 var eles = this;
2680 var cy = this.cy();
2681 var _this$byGroup = this.byGroup(),
2682 edges = _this$byGroup.edges,
2683 nodes = _this$byGroup.nodes;
2684 var numNodes = nodes.length;
2685 var infoMap = new Map$2();
2686 var hasNegativeWeightCycle = false;
2687 var negativeWeightCycles = [];
2688 root = cy.collection(root)[0]; // in case selector passed
2689
2690 edges.unmergeBy(function (edge) {
2691 return edge.isLoop();
2692 });
2693 var numEdges = edges.length;
2694 var getInfo = function getInfo(node) {
2695 var obj = infoMap.get(node.id());
2696 if (!obj) {
2697 obj = {};
2698 infoMap.set(node.id(), obj);
2699 }
2700 return obj;
2701 };
2702 var getNodeFromTo = function getNodeFromTo(to) {
2703 return (string(to) ? cy.$(to) : to)[0];
2704 };
2705 var distanceTo = function distanceTo(to) {
2706 return getInfo(getNodeFromTo(to)).dist;
2707 };
2708 var pathTo = function pathTo(to) {
2709 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
2710 var end = getNodeFromTo(to);
2711 var path = [];
2712 var node = end;
2713 for (;;) {
2714 if (node == null) {
2715 return _this.spawn();
2716 }
2717 var _getInfo = getInfo(node),
2718 edge = _getInfo.edge,
2719 pred = _getInfo.pred;
2720 path.unshift(node[0]);
2721 if (node.same(thisStart) && path.length > 0) {
2722 break;
2723 }
2724 if (edge != null) {
2725 path.unshift(edge);
2726 }
2727 node = pred;
2728 }
2729 return eles.spawn(path);
2730 };
2731
2732 // Initializations { dist, pred, edge }
2733 for (var i = 0; i < numNodes; i++) {
2734 var node = nodes[i];
2735 var info = getInfo(node);
2736 if (node.same(root)) {
2737 info.dist = 0;
2738 } else {
2739 info.dist = Infinity;
2740 }
2741 info.pred = null;
2742 info.edge = null;
2743 }
2744
2745 // Edges relaxation
2746 var replacedEdge = false;
2747 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2748 var dist = info1.dist + weight;
2749 if (dist < info2.dist && !edge.same(info1.edge)) {
2750 info2.dist = dist;
2751 info2.pred = node1;
2752 info2.edge = edge;
2753 replacedEdge = true;
2754 }
2755 };
2756 for (var _i = 1; _i < numNodes; _i++) {
2757 replacedEdge = false;
2758 for (var e = 0; e < numEdges; e++) {
2759 var edge = edges[e];
2760 var src = edge.source();
2761 var tgt = edge.target();
2762 var _weight = weightFn(edge);
2763 var srcInfo = getInfo(src);
2764 var tgtInfo = getInfo(tgt);
2765 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight);
2766
2767 // If undirected graph, we need to take into account the 'reverse' edge
2768 if (!directed) {
2769 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2770 }
2771 }
2772 if (!replacedEdge) {
2773 break;
2774 }
2775 }
2776 if (replacedEdge) {
2777 // Check for negative weight cycles
2778 var negativeWeightCycleIds = [];
2779 for (var _e = 0; _e < numEdges; _e++) {
2780 var _edge = edges[_e];
2781 var _src = _edge.source();
2782 var _tgt = _edge.target();
2783 var _weight2 = weightFn(_edge);
2784 var srcDist = getInfo(_src).dist;
2785 var tgtDist = getInfo(_tgt).dist;
2786 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2787 if (!hasNegativeWeightCycle) {
2788 warn('Graph contains a negative weight cycle for Bellman-Ford');
2789 hasNegativeWeightCycle = true;
2790 }
2791 if (options.findNegativeWeightCycles !== false) {
2792 var negativeNodes = [];
2793 if (srcDist + _weight2 < tgtDist) {
2794 negativeNodes.push(_src);
2795 }
2796 if (!directed && tgtDist + _weight2 < srcDist) {
2797 negativeNodes.push(_tgt);
2798 }
2799 var numNegativeNodes = negativeNodes.length;
2800 for (var n = 0; n < numNegativeNodes; n++) {
2801 var start = negativeNodes[n];
2802 var cycle = [start];
2803 cycle.push(getInfo(start).edge);
2804 var _node = getInfo(start).pred;
2805 while (cycle.indexOf(_node) === -1) {
2806 cycle.push(_node);
2807 cycle.push(getInfo(_node).edge);
2808 _node = getInfo(_node).pred;
2809 }
2810 cycle = cycle.slice(cycle.indexOf(_node));
2811 var smallestId = cycle[0].id();
2812 var smallestIndex = 0;
2813 for (var c = 2; c < cycle.length; c += 2) {
2814 if (cycle[c].id() < smallestId) {
2815 smallestId = cycle[c].id();
2816 smallestIndex = c;
2817 }
2818 }
2819 cycle = cycle.slice(smallestIndex).concat(cycle.slice(0, smallestIndex));
2820 cycle.push(cycle[0]);
2821 var cycleId = cycle.map(function (el) {
2822 return el.id();
2823 }).join(",");
2824 if (negativeWeightCycleIds.indexOf(cycleId) === -1) {
2825 negativeWeightCycles.push(eles.spawn(cycle));
2826 negativeWeightCycleIds.push(cycleId);
2827 }
2828 }
2829 } else {
2830 break;
2831 }
2832 }
2833 }
2834 }
2835 return {
2836 distanceTo: distanceTo,
2837 pathTo: pathTo,
2838 hasNegativeWeightCycle: hasNegativeWeightCycle,
2839 negativeWeightCycles: negativeWeightCycles
2840 };
2841 } // bellmanFord
2842 }; // elesfn
2843
2844 var sqrt2 = Math.sqrt(2);
2845
2846 // Function which colapses 2 (meta) nodes into one
2847 // Updates the remaining edge lists
2848 // Receives as a paramater the edge which causes the collapse
2849 var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2850 if (remainingEdges.length === 0) {
2851 error("Karger-Stein must be run on a connected (sub)graph");
2852 }
2853 var edgeInfo = remainingEdges[edgeIndex];
2854 var sourceIn = edgeInfo[1];
2855 var targetIn = edgeInfo[2];
2856 var partition1 = nodeMap[sourceIn];
2857 var partition2 = nodeMap[targetIn];
2858 var newEdges = remainingEdges; // re-use array
2859
2860 // Delete all edges between partition1 and partition2
2861 for (var i = newEdges.length - 1; i >= 0; i--) {
2862 var edge = newEdges[i];
2863 var src = edge[1];
2864 var tgt = edge[2];
2865 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2866 newEdges.splice(i, 1);
2867 }
2868 }
2869
2870 // All edges pointing to partition2 should now point to partition1
2871 for (var _i = 0; _i < newEdges.length; _i++) {
2872 var _edge = newEdges[_i];
2873 if (_edge[1] === partition2) {
2874 // Check source
2875 newEdges[_i] = _edge.slice(); // copy
2876 newEdges[_i][1] = partition1;
2877 } else if (_edge[2] === partition2) {
2878 // Check target
2879 newEdges[_i] = _edge.slice(); // copy
2880 newEdges[_i][2] = partition1;
2881 }
2882 }
2883
2884 // Move all nodes from partition2 to partition1
2885 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2886 if (nodeMap[_i2] === partition2) {
2887 nodeMap[_i2] = partition1;
2888 }
2889 }
2890 return newEdges;
2891 };
2892
2893 // Contracts a graph until we reach a certain number of meta nodes
2894 var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2895 while (size > sizeLimit) {
2896 // Choose an edge randomly
2897 var edgeIndex = Math.floor(Math.random() * remainingEdges.length);
2898
2899 // Collapse graph based on edge
2900 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2901 size--;
2902 }
2903 return remainingEdges;
2904 };
2905 var elesfn$p = {
2906 // Computes the minimum cut of an undirected graph
2907 // Returns the correct answer with high probability
2908 kargerStein: function kargerStein() {
2909 var _this = this;
2910 var _this$byGroup = this.byGroup(),
2911 nodes = _this$byGroup.nodes,
2912 edges = _this$byGroup.edges;
2913 edges.unmergeBy(function (edge) {
2914 return edge.isLoop();
2915 });
2916 var numNodes = nodes.length;
2917 var numEdges = edges.length;
2918 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2919 var stopSize = Math.floor(numNodes / sqrt2);
2920 if (numNodes < 2) {
2921 error('At least 2 nodes are required for Karger-Stein algorithm');
2922 return undefined;
2923 }
2924
2925 // Now store edge destination as indexes
2926 // Format for each edge (edge index, source node index, target node index)
2927 var edgeIndexes = [];
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 }
2932
2933 // We will store the best cut found here
2934 var minCutSize = Infinity;
2935 var minCutEdgeIndexes = [];
2936 var minCutNodeMap = new Array(numNodes);
2937
2938 // Initial meta node partition
2939 var metaNodeMap = new Array(numNodes);
2940 var metaNodeMap2 = new Array(numNodes);
2941 var copyNodesMap = function copyNodesMap(from, to) {
2942 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2943 to[_i3] = from[_i3];
2944 }
2945 };
2946
2947 // Main loop
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 }
2953
2954 // Contract until stop point (stopSize nodes)
2955 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2956 var edgesState2 = edgesState.slice(); // copy
2957
2958 // Create a copy of the colapsed nodes state
2959 copyNodesMap(metaNodeMap, metaNodeMap2);
2960
2961 // Run 2 iterations starting in the stop state
2962 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2963 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2);
2964
2965 // Is any of the 2 results the best cut so far?
2966 if (res1.length <= res2.length && res1.length < minCutSize) {
2967 minCutSize = res1.length;
2968 minCutEdgeIndexes = res1;
2969 copyNodesMap(metaNodeMap, minCutNodeMap);
2970 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2971 minCutSize = res2.length;
2972 minCutEdgeIndexes = res2;
2973 copyNodesMap(metaNodeMap2, minCutNodeMap);
2974 }
2975 } // end of main loop
2976
2977 // Construct result
2978 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2979 return edges[e[0]];
2980 }));
2981 var partition1 = this.spawn();
2982 var partition2 = this.spawn();
2983
2984 // traverse metaNodeMap for best cut
2985 var witnessNodePartition = minCutNodeMap[0];
2986 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2987 var partitionId = minCutNodeMap[_i5];
2988 var node = nodes[_i5];
2989 if (partitionId === witnessNodePartition) {
2990 partition1.merge(node);
2991 } else {
2992 partition2.merge(node);
2993 }
2994 }
2995
2996 // construct components corresponding to each disjoint subset of nodes
2997 var constructComponent = function constructComponent(subset) {
2998 var component = _this.spawn();
2999 subset.forEach(function (node) {
3000 component.merge(node);
3001 node.connectedEdges().forEach(function (edge) {
3002 // ensure edge is within calling collection and edge is not in cut
3003 if (_this.contains(edge) && !cut.contains(edge)) {
3004 component.merge(edge);
3005 }
3006 });
3007 });
3008 return component;
3009 };
3010 var components = [constructComponent(partition1), constructComponent(partition2)];
3011 var ret = {
3012 cut: cut,
3013 components: components,
3014 // n.b. partitions are included to be compatible with the old api spec
3015 // (could be removed in a future major version)
3016 partition1: partition1,
3017 partition2: partition2
3018 };
3019 return ret;
3020 }
3021 }; // elesfn
3022
3023 var copyPosition = function copyPosition(p) {
3024 return {
3025 x: p.x,
3026 y: p.y
3027 };
3028 };
3029 var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
3030 return {
3031 x: p.x * zoom + pan.x,
3032 y: p.y * zoom + pan.y
3033 };
3034 };
3035 var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
3036 return {
3037 x: (p.x - pan.x) / zoom,
3038 y: (p.y - pan.y) / zoom
3039 };
3040 };
3041 var array2point = function array2point(arr) {
3042 return {
3043 x: arr[0],
3044 y: arr[1]
3045 };
3046 };
3047 var min = function min(arr) {
3048 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3049 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3050 var min = Infinity;
3051 for (var i = begin; i < end; i++) {
3052 var val = arr[i];
3053 if (isFinite(val)) {
3054 min = Math.min(val, min);
3055 }
3056 }
3057 return min;
3058 };
3059 var max = function max(arr) {
3060 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3061 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3062 var max = -Infinity;
3063 for (var i = begin; i < end; i++) {
3064 var val = arr[i];
3065 if (isFinite(val)) {
3066 max = Math.max(val, max);
3067 }
3068 }
3069 return max;
3070 };
3071 var mean = function mean(arr) {
3072 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3073 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3074 var total = 0;
3075 var n = 0;
3076 for (var i = begin; i < end; i++) {
3077 var val = arr[i];
3078 if (isFinite(val)) {
3079 total += val;
3080 n++;
3081 }
3082 }
3083 return total / n;
3084 };
3085 var median = function median(arr) {
3086 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3087 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
3088 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
3089 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
3090 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
3091 if (copy) {
3092 arr = arr.slice(begin, end);
3093 } else {
3094 if (end < arr.length) {
3095 arr.splice(end, arr.length - end);
3096 }
3097 if (begin > 0) {
3098 arr.splice(0, begin);
3099 }
3100 }
3101
3102 // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
3103 var off = 0; // offset from non-finite values
3104 for (var i = arr.length - 1; i >= 0; i--) {
3105 var v = arr[i];
3106 if (includeHoles) {
3107 if (!isFinite(v)) {
3108 arr[i] = -Infinity;
3109 off++;
3110 }
3111 } else {
3112 // just remove it if we don't want to consider holes
3113 arr.splice(i, 1);
3114 }
3115 }
3116 if (sort) {
3117 arr.sort(function (a, b) {
3118 return a - b;
3119 }); // requires copy = true if you don't want to change the orig
3120 }
3121
3122 var len = arr.length;
3123 var mid = Math.floor(len / 2);
3124 if (len % 2 !== 0) {
3125 return arr[mid + 1 + off];
3126 } else {
3127 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
3128 }
3129 };
3130 var deg2rad = function deg2rad(deg) {
3131 return Math.PI * deg / 180;
3132 };
3133 var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
3134 return Math.atan2(dispY, dispX) - Math.PI / 2;
3135 };
3136 var log2 = Math.log2 || function (n) {
3137 return Math.log(n) / Math.log(2);
3138 };
3139 var signum = function signum(x) {
3140 if (x > 0) {
3141 return 1;
3142 } else if (x < 0) {
3143 return -1;
3144 } else {
3145 return 0;
3146 }
3147 };
3148 var dist = function dist(p1, p2) {
3149 return Math.sqrt(sqdist(p1, p2));
3150 };
3151 var sqdist = function sqdist(p1, p2) {
3152 var dx = p2.x - p1.x;
3153 var dy = p2.y - p1.y;
3154 return dx * dx + dy * dy;
3155 };
3156 var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
3157 var length = v.length;
3158
3159 // First, get sum of all elements
3160 var total = 0;
3161 for (var i = 0; i < length; i++) {
3162 total += v[i];
3163 }
3164
3165 // Now, divide each by the sum of all elements
3166 for (var _i = 0; _i < length; _i++) {
3167 v[_i] = v[_i] / total;
3168 }
3169 return v;
3170 };
3171
3172 // from http://en.wikipedia.org/wiki/Bézier_curve#Quadratic_curves
3173 var qbezierAt = function qbezierAt(p0, p1, p2, t) {
3174 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
3175 };
3176 var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
3177 return {
3178 x: qbezierAt(p0.x, p1.x, p2.x, t),
3179 y: qbezierAt(p0.y, p1.y, p2.y, t)
3180 };
3181 };
3182 var lineAt = function lineAt(p0, p1, t, d) {
3183 var vec = {
3184 x: p1.x - p0.x,
3185 y: p1.y - p0.y
3186 };
3187 var vecDist = dist(p0, p1);
3188 var normVec = {
3189 x: vec.x / vecDist,
3190 y: vec.y / vecDist
3191 };
3192 t = t == null ? 0 : t;
3193 d = d != null ? d : t * vecDist;
3194 return {
3195 x: p0.x + normVec.x * d,
3196 y: p0.y + normVec.y * d
3197 };
3198 };
3199 var bound = function bound(min, val, max) {
3200 return Math.max(min, Math.min(max, val));
3201 };
3202
3203 // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
3204 var makeBoundingBox = function makeBoundingBox(bb) {
3205 if (bb == null) {
3206 return {
3207 x1: Infinity,
3208 y1: Infinity,
3209 x2: -Infinity,
3210 y2: -Infinity,
3211 w: 0,
3212 h: 0
3213 };
3214 } else if (bb.x1 != null && bb.y1 != null) {
3215 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
3216 return {
3217 x1: bb.x1,
3218 y1: bb.y1,
3219 x2: bb.x2,
3220 y2: bb.y2,
3221 w: bb.x2 - bb.x1,
3222 h: bb.y2 - bb.y1
3223 };
3224 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
3225 return {
3226 x1: bb.x1,
3227 y1: bb.y1,
3228 x2: bb.x1 + bb.w,
3229 y2: bb.y1 + bb.h,
3230 w: bb.w,
3231 h: bb.h
3232 };
3233 }
3234 }
3235 };
3236 var copyBoundingBox = function copyBoundingBox(bb) {
3237 return {
3238 x1: bb.x1,
3239 x2: bb.x2,
3240 w: bb.w,
3241 y1: bb.y1,
3242 y2: bb.y2,
3243 h: bb.h
3244 };
3245 };
3246 var clearBoundingBox = function clearBoundingBox(bb) {
3247 bb.x1 = Infinity;
3248 bb.y1 = Infinity;
3249 bb.x2 = -Infinity;
3250 bb.y2 = -Infinity;
3251 bb.w = 0;
3252 bb.h = 0;
3253 };
3254 var shiftBoundingBox = function shiftBoundingBox(bb, dx, dy) {
3255 return {
3256 x1: bb.x1 + dx,
3257 x2: bb.x2 + dx,
3258 y1: bb.y1 + dy,
3259 y2: bb.y2 + dy,
3260 w: bb.w,
3261 h: bb.h
3262 };
3263 };
3264 var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
3265 // update bb1 with bb2 bounds
3266
3267 bb1.x1 = Math.min(bb1.x1, bb2.x1);
3268 bb1.x2 = Math.max(bb1.x2, bb2.x2);
3269 bb1.w = bb1.x2 - bb1.x1;
3270 bb1.y1 = Math.min(bb1.y1, bb2.y1);
3271 bb1.y2 = Math.max(bb1.y2, bb2.y2);
3272 bb1.h = bb1.y2 - bb1.y1;
3273 };
3274 var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
3275 bb.x1 = Math.min(bb.x1, x);
3276 bb.x2 = Math.max(bb.x2, x);
3277 bb.w = bb.x2 - bb.x1;
3278 bb.y1 = Math.min(bb.y1, y);
3279 bb.y2 = Math.max(bb.y2, y);
3280 bb.h = bb.y2 - bb.y1;
3281 };
3282 var expandBoundingBox = function expandBoundingBox(bb) {
3283 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
3284 bb.x1 -= padding;
3285 bb.x2 += padding;
3286 bb.y1 -= padding;
3287 bb.y2 += padding;
3288 bb.w = bb.x2 - bb.x1;
3289 bb.h = bb.y2 - bb.y1;
3290 return bb;
3291 };
3292 var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
3293 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
3294 var top, right, bottom, left;
3295 if (padding.length === 1) {
3296 top = right = bottom = left = padding[0];
3297 } else if (padding.length === 2) {
3298 top = bottom = padding[0];
3299 left = right = padding[1];
3300 } else if (padding.length === 4) {
3301 var _padding = _slicedToArray(padding, 4);
3302 top = _padding[0];
3303 right = _padding[1];
3304 bottom = _padding[2];
3305 left = _padding[3];
3306 }
3307 bb.x1 -= left;
3308 bb.x2 += right;
3309 bb.y1 -= top;
3310 bb.y2 += bottom;
3311 bb.w = bb.x2 - bb.x1;
3312 bb.h = bb.y2 - bb.y1;
3313 return bb;
3314 };
3315
3316 // assign the values of bb2 into bb1
3317 var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
3318 bb1.x1 = bb2.x1;
3319 bb1.y1 = bb2.y1;
3320 bb1.x2 = bb2.x2;
3321 bb1.y2 = bb2.y2;
3322 bb1.w = bb1.x2 - bb1.x1;
3323 bb1.h = bb1.y2 - bb1.y1;
3324 };
3325 var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
3326 // case: one bb to right of other
3327 if (bb1.x1 > bb2.x2) {
3328 return false;
3329 }
3330 if (bb2.x1 > bb1.x2) {
3331 return false;
3332 }
3333
3334 // case: one bb to left of other
3335 if (bb1.x2 < bb2.x1) {
3336 return false;
3337 }
3338 if (bb2.x2 < bb1.x1) {
3339 return false;
3340 }
3341
3342 // case: one bb above other
3343 if (bb1.y2 < bb2.y1) {
3344 return false;
3345 }
3346 if (bb2.y2 < bb1.y1) {
3347 return false;
3348 }
3349
3350 // case: one bb below other
3351 if (bb1.y1 > bb2.y2) {
3352 return false;
3353 }
3354 if (bb2.y1 > bb1.y2) {
3355 return false;
3356 }
3357
3358 // otherwise, must have some overlap
3359 return true;
3360 };
3361 var inBoundingBox = function inBoundingBox(bb, x, y) {
3362 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
3363 };
3364 var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
3365 return inBoundingBox(bb, pt.x, pt.y);
3366 };
3367 var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
3368 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
3369 };
3370 var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
3371 var radius = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 'auto';
3372 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : radius;
3373 var halfWidth = width / 2;
3374 var halfHeight = height / 2;
3375 cornerRadius = Math.min(cornerRadius, halfWidth, halfHeight);
3376 var doWidth = cornerRadius !== halfWidth,
3377 doHeight = cornerRadius !== halfHeight;
3378
3379 // Check intersections with straight line segments
3380 var straightLineIntersections;
3381
3382 // Top segment, left to right
3383 if (doWidth) {
3384 var topStartX = nodeX - halfWidth + cornerRadius - padding;
3385 var topStartY = nodeY - halfHeight - padding;
3386 var topEndX = nodeX + halfWidth - cornerRadius + padding;
3387 var topEndY = topStartY;
3388 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
3389 if (straightLineIntersections.length > 0) {
3390 return straightLineIntersections;
3391 }
3392 }
3393
3394 // Right segment, top to bottom
3395 if (doHeight) {
3396 var rightStartX = nodeX + halfWidth + padding;
3397 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
3398 var rightEndX = rightStartX;
3399 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
3400 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
3401 if (straightLineIntersections.length > 0) {
3402 return straightLineIntersections;
3403 }
3404 }
3405
3406 // Bottom segment, left to right
3407 if (doWidth) {
3408 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
3409 var bottomStartY = nodeY + halfHeight + padding;
3410 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
3411 var bottomEndY = bottomStartY;
3412 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
3413 if (straightLineIntersections.length > 0) {
3414 return straightLineIntersections;
3415 }
3416 }
3417
3418 // Left segment, top to bottom
3419 if (doHeight) {
3420 var leftStartX = nodeX - halfWidth - padding;
3421 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
3422 var leftEndX = leftStartX;
3423 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
3424 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
3425 if (straightLineIntersections.length > 0) {
3426 return straightLineIntersections;
3427 }
3428 }
3429
3430 // Check intersections with arc segments
3431 var arcIntersections;
3432
3433 // Top Left
3434 {
3435 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
3436 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
3437 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding);
3438
3439 // Ensure the intersection is on the desired quarter of the circle
3440 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
3441 return [arcIntersections[0], arcIntersections[1]];
3442 }
3443 }
3444
3445 // Top Right
3446 {
3447 var topRightCenterX = nodeX + halfWidth - cornerRadius;
3448 var topRightCenterY = nodeY - halfHeight + cornerRadius;
3449 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding);
3450
3451 // Ensure the intersection is on the desired quarter of the circle
3452 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
3453 return [arcIntersections[0], arcIntersections[1]];
3454 }
3455 }
3456
3457 // Bottom Right
3458 {
3459 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
3460 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
3461 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding);
3462
3463 // Ensure the intersection is on the desired quarter of the circle
3464 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
3465 return [arcIntersections[0], arcIntersections[1]];
3466 }
3467 }
3468
3469 // Bottom Left
3470 {
3471 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
3472 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
3473 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding);
3474
3475 // Ensure the intersection is on the desired quarter of the circle
3476 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
3477 return [arcIntersections[0], arcIntersections[1]];
3478 }
3479 }
3480 return []; // if nothing
3481 };
3482
3483 var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
3484 var t = tolerance;
3485 var x1 = Math.min(lx1, lx2);
3486 var x2 = Math.max(lx1, lx2);
3487 var y1 = Math.min(ly1, ly2);
3488 var y2 = Math.max(ly1, ly2);
3489 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
3490 };
3491 var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
3492 var bb = {
3493 x1: Math.min(x1, x3, x2) - tolerance,
3494 x2: Math.max(x1, x3, x2) + tolerance,
3495 y1: Math.min(y1, y3, y2) - tolerance,
3496 y2: Math.max(y1, y3, y2) + tolerance
3497 };
3498
3499 // if outside the rough bounding box for the bezier, then it can't be a hit
3500 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
3501 // console.log('bezier out of rough bb')
3502 return false;
3503 } else {
3504 // console.log('do more expensive check');
3505 return true;
3506 }
3507 };
3508 var solveQuadratic = function solveQuadratic(a, b, c, val) {
3509 c -= val;
3510 var r = b * b - 4 * a * c;
3511 if (r < 0) {
3512 return [];
3513 }
3514 var sqrtR = Math.sqrt(r);
3515 var denom = 2 * a;
3516 var root1 = (-b + sqrtR) / denom;
3517 var root2 = (-b - sqrtR) / denom;
3518 return [root1, root2];
3519 };
3520 var solveCubic = function solveCubic(a, b, c, d, result) {
3521 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
3522 // r is the real component, i is the imaginary component
3523
3524 // An implementation of the Cardano method from the year 1545
3525 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
3526
3527 var epsilon = 0.00001;
3528
3529 // avoid division by zero while keeping the overall expression close in value
3530 if (a === 0) {
3531 a = epsilon;
3532 }
3533 b /= a;
3534 c /= a;
3535 d /= a;
3536 var discriminant, q, r, dum1, s, t, term1, r13;
3537 q = (3.0 * c - b * b) / 9.0;
3538 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
3539 r /= 54.0;
3540 discriminant = q * q * q + r * r;
3541 result[1] = 0;
3542 term1 = b / 3.0;
3543 if (discriminant > 0) {
3544 s = r + Math.sqrt(discriminant);
3545 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
3546 t = r - Math.sqrt(discriminant);
3547 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
3548 result[0] = -term1 + s + t;
3549 term1 += (s + t) / 2.0;
3550 result[4] = result[2] = -term1;
3551 term1 = Math.sqrt(3.0) * (-t + s) / 2;
3552 result[3] = term1;
3553 result[5] = -term1;
3554 return;
3555 }
3556 result[5] = result[3] = 0;
3557 if (discriminant === 0) {
3558 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
3559 result[0] = -term1 + 2.0 * r13;
3560 result[4] = result[2] = -(r13 + term1);
3561 return;
3562 }
3563 q = -q;
3564 dum1 = q * q * q;
3565 dum1 = Math.acos(r / Math.sqrt(dum1));
3566 r13 = 2.0 * Math.sqrt(q);
3567 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
3568 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
3569 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
3570 return;
3571 };
3572 var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
3573 // Find minimum distance by using the minimum of the distance
3574 // function between the given point and the curve
3575
3576 // This gives the coefficients of the resulting cubic equation
3577 // whose roots tell us where a possible minimum is
3578 // (Coefficients are divided by 4)
3579
3580 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;
3581 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;
3582 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;
3583 var d = 1.0 * x1 * x2 - x1 * x1 + x1 * x - x2 * x + y1 * y2 - y1 * y1 + y1 * y - y2 * y;
3584
3585 // debug("coefficients: " + a / a + ", " + b / a + ", " + c / a + ", " + d / a);
3586
3587 var roots = [];
3588
3589 // Use the cubic solving algorithm
3590 solveCubic(a, b, c, d, roots);
3591 var zeroThreshold = 0.0000001;
3592 var params = [];
3593 for (var index = 0; index < 6; index += 2) {
3594 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
3595 params.push(roots[index]);
3596 }
3597 }
3598 params.push(1.0);
3599 params.push(0.0);
3600 var minDistanceSquared = -1;
3601 var curX, curY, distSquared;
3602 for (var i = 0; i < params.length; i++) {
3603 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
3604 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
3605 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2);
3606 // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
3607 if (minDistanceSquared >= 0) {
3608 if (distSquared < minDistanceSquared) {
3609 minDistanceSquared = distSquared;
3610 }
3611 } else {
3612 minDistanceSquared = distSquared;
3613 }
3614 }
3615 return minDistanceSquared;
3616 };
3617 var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
3618 var offset = [x - x1, y - y1];
3619 var line = [x2 - x1, y2 - y1];
3620 var lineSq = line[0] * line[0] + line[1] * line[1];
3621 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
3622 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
3623 var adjSq = dotProduct * dotProduct / lineSq;
3624 if (dotProduct < 0) {
3625 return hypSq;
3626 }
3627 if (adjSq > lineSq) {
3628 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
3629 }
3630 return hypSq - adjSq;
3631 };
3632 var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
3633 var x1, y1, x2, y2;
3634 var y3;
3635
3636 // Intersect with vertical line through (x, y)
3637 var up = 0;
3638 // let down = 0;
3639 for (var i = 0; i < points.length / 2; i++) {
3640 x1 = points[i * 2];
3641 y1 = points[i * 2 + 1];
3642 if (i + 1 < points.length / 2) {
3643 x2 = points[(i + 1) * 2];
3644 y2 = points[(i + 1) * 2 + 1];
3645 } else {
3646 x2 = points[(i + 1 - points.length / 2) * 2];
3647 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
3648 }
3649 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
3650 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
3651 if (y3 > y) {
3652 up++;
3653 }
3654
3655 // if( y3 < y ){
3656 // down++;
3657 // }
3658 } else {
3659 continue;
3660 }
3661 }
3662 if (up % 2 === 0) {
3663 return false;
3664 } else {
3665 return true;
3666 }
3667 };
3668 var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
3669 var transformedPoints = new Array(basePoints.length);
3670
3671 // Gives negative angle
3672 var angle;
3673 if (direction[0] != null) {
3674 angle = Math.atan(direction[1] / direction[0]);
3675 if (direction[0] < 0) {
3676 angle = angle + Math.PI / 2;
3677 } else {
3678 angle = -angle - Math.PI / 2;
3679 }
3680 } else {
3681 angle = direction;
3682 }
3683 var cos = Math.cos(-angle);
3684 var sin = Math.sin(-angle);
3685
3686 // console.log("base: " + basePoints);
3687 for (var i = 0; i < transformedPoints.length / 2; i++) {
3688 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
3689 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
3690 transformedPoints[i * 2] += centerX;
3691 transformedPoints[i * 2 + 1] += centerY;
3692 }
3693 var points;
3694 if (padding > 0) {
3695 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3696 points = joinLines(expandedLineSet);
3697 } else {
3698 points = transformedPoints;
3699 }
3700 return pointInsidePolygonPoints(x, y, points);
3701 };
3702 var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height, corners) {
3703 var cutPolygonPoints = new Array(basePoints.length * 2);
3704 for (var i = 0; i < corners.length; i++) {
3705 var corner = corners[i];
3706 cutPolygonPoints[i * 4 + 0] = corner.startX;
3707 cutPolygonPoints[i * 4 + 1] = corner.startY;
3708 cutPolygonPoints[i * 4 + 2] = corner.stopX;
3709 cutPolygonPoints[i * 4 + 3] = corner.stopY;
3710 var squaredDistance = Math.pow(corner.cx - x, 2) + Math.pow(corner.cy - y, 2);
3711 if (squaredDistance <= Math.pow(corner.radius, 2)) {
3712 return true;
3713 }
3714 }
3715 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3716 };
3717 var joinLines = function joinLines(lineSet) {
3718 var vertices = new Array(lineSet.length / 2);
3719 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3720 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3721 for (var i = 0; i < lineSet.length / 4; i++) {
3722 currentLineStartX = lineSet[i * 4];
3723 currentLineStartY = lineSet[i * 4 + 1];
3724 currentLineEndX = lineSet[i * 4 + 2];
3725 currentLineEndY = lineSet[i * 4 + 3];
3726 if (i < lineSet.length / 4 - 1) {
3727 nextLineStartX = lineSet[(i + 1) * 4];
3728 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3729 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3730 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3731 } else {
3732 nextLineStartX = lineSet[0];
3733 nextLineStartY = lineSet[1];
3734 nextLineEndX = lineSet[2];
3735 nextLineEndY = lineSet[3];
3736 }
3737 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3738 vertices[i * 2] = intersection[0];
3739 vertices[i * 2 + 1] = intersection[1];
3740 }
3741 return vertices;
3742 };
3743 var expandPolygon = function expandPolygon(points, pad) {
3744 var expandedLineSet = new Array(points.length * 2);
3745 var currentPointX, currentPointY, nextPointX, nextPointY;
3746 for (var i = 0; i < points.length / 2; i++) {
3747 currentPointX = points[i * 2];
3748 currentPointY = points[i * 2 + 1];
3749 if (i < points.length / 2 - 1) {
3750 nextPointX = points[(i + 1) * 2];
3751 nextPointY = points[(i + 1) * 2 + 1];
3752 } else {
3753 nextPointX = points[0];
3754 nextPointY = points[1];
3755 }
3756
3757 // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3758
3759 // Assume CCW polygon winding
3760
3761 var offsetX = nextPointY - currentPointY;
3762 var offsetY = -(nextPointX - currentPointX);
3763
3764 // Normalize
3765 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3766 var normalizedOffsetX = offsetX / offsetLength;
3767 var normalizedOffsetY = offsetY / offsetLength;
3768 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3769 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3770 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3771 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3772 }
3773 return expandedLineSet;
3774 };
3775 var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3776 var dispX = centerX - x;
3777 var dispY = centerY - y;
3778 dispX /= ellipseWradius;
3779 dispY /= ellipseHradius;
3780 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3781 var newLength = len - 1;
3782 if (newLength < 0) {
3783 return [];
3784 }
3785 var lenProportion = newLength / len;
3786 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3787 };
3788 var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3789 x -= centerX;
3790 y -= centerY;
3791 x /= width / 2 + padding;
3792 y /= height / 2 + padding;
3793 return x * x + y * y <= 1;
3794 };
3795
3796 // Returns intersections of increasing distance from line's start point
3797 var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3798 // Calculate d, direction vector of line
3799 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3800 var f = [x1 - centerX, y1 - centerY];
3801 var a = d[0] * d[0] + d[1] * d[1];
3802 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3803 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3804 var discriminant = b * b - 4 * a * c;
3805 if (discriminant < 0) {
3806 return [];
3807 }
3808 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3809 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3810 var tMin = Math.min(t1, t2);
3811 var tMax = Math.max(t1, t2);
3812 var inRangeParams = [];
3813 if (tMin >= 0 && tMin <= 1) {
3814 inRangeParams.push(tMin);
3815 }
3816 if (tMax >= 0 && tMax <= 1) {
3817 inRangeParams.push(tMax);
3818 }
3819 if (inRangeParams.length === 0) {
3820 return [];
3821 }
3822 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3823 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3824 if (inRangeParams.length > 1) {
3825 if (inRangeParams[0] == inRangeParams[1]) {
3826 return [nearIntersectionX, nearIntersectionY];
3827 } else {
3828 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3829 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3830 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3831 }
3832 } else {
3833 return [nearIntersectionX, nearIntersectionY];
3834 }
3835 };
3836 var midOfThree = function midOfThree(a, b, c) {
3837 if (b <= a && a <= c || c <= a && a <= b) {
3838 return a;
3839 } else if (a <= b && b <= c || c <= b && b <= a) {
3840 return b;
3841 } else {
3842 return c;
3843 }
3844 };
3845
3846 // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3847 var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3848 var dx13 = x1 - x3;
3849 var dx21 = x2 - x1;
3850 var dx43 = x4 - x3;
3851 var dy13 = y1 - y3;
3852 var dy21 = y2 - y1;
3853 var dy43 = y4 - y3;
3854 var ua_t = dx43 * dy13 - dy43 * dx13;
3855 var ub_t = dx21 * dy13 - dy21 * dx13;
3856 var u_b = dy43 * dx21 - dx43 * dy21;
3857 if (u_b !== 0) {
3858 var ua = ua_t / u_b;
3859 var ub = ub_t / u_b;
3860 var flptThreshold = 0.001;
3861 var _min = 0 - flptThreshold;
3862 var _max = 1 + flptThreshold;
3863 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3864 return [x1 + ua * dx21, y1 + ua * dy21];
3865 } else {
3866 if (!infiniteLines) {
3867 return [];
3868 } else {
3869 return [x1 + ua * dx21, y1 + ua * dy21];
3870 }
3871 }
3872 } else {
3873 if (ua_t === 0 || ub_t === 0) {
3874 // Parallel, coincident lines. Check if overlap
3875
3876 // Check endpoint of second line
3877 if (midOfThree(x1, x2, x4) === x4) {
3878 return [x4, y4];
3879 }
3880
3881 // Check start point of second line
3882 if (midOfThree(x1, x2, x3) === x3) {
3883 return [x3, y3];
3884 }
3885
3886 // Endpoint of first line
3887 if (midOfThree(x3, x4, x2) === x2) {
3888 return [x2, y2];
3889 }
3890 return [];
3891 } else {
3892 // Parallel, non-coincident
3893 return [];
3894 }
3895 }
3896 };
3897
3898 // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3899 // intersect a node polygon (pts transformed)
3900 //
3901 // math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3902 // intersect the points (no transform)
3903 var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3904 var intersections = [];
3905 var intersection;
3906 var transformedPoints = new Array(basePoints.length);
3907 var doTransform = true;
3908 if (width == null) {
3909 doTransform = false;
3910 }
3911 var points;
3912 if (doTransform) {
3913 for (var i = 0; i < transformedPoints.length / 2; i++) {
3914 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3915 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3916 }
3917 if (padding > 0) {
3918 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3919 points = joinLines(expandedLineSet);
3920 } else {
3921 points = transformedPoints;
3922 }
3923 } else {
3924 points = basePoints;
3925 }
3926 var currentX, currentY, nextX, nextY;
3927 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3928 currentX = points[_i2 * 2];
3929 currentY = points[_i2 * 2 + 1];
3930 if (_i2 < points.length / 2 - 1) {
3931 nextX = points[(_i2 + 1) * 2];
3932 nextY = points[(_i2 + 1) * 2 + 1];
3933 } else {
3934 nextX = points[0];
3935 nextY = points[1];
3936 }
3937 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3938 if (intersection.length !== 0) {
3939 intersections.push(intersection[0], intersection[1]);
3940 }
3941 }
3942 return intersections;
3943 };
3944 var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding, corners) {
3945 var intersections = [];
3946 var intersection;
3947 var lines = new Array(basePoints.length * 2);
3948 corners.forEach(function (corner, i) {
3949 if (i === 0) {
3950 lines[lines.length - 2] = corner.startX;
3951 lines[lines.length - 1] = corner.startY;
3952 } else {
3953 lines[i * 4 - 2] = corner.startX;
3954 lines[i * 4 - 1] = corner.startY;
3955 }
3956 lines[i * 4] = corner.stopX;
3957 lines[i * 4 + 1] = corner.stopY;
3958 intersection = intersectLineCircle(x, y, centerX, centerY, corner.cx, corner.cy, corner.radius);
3959 if (intersection.length !== 0) {
3960 intersections.push(intersection[0], intersection[1]);
3961 }
3962 });
3963 for (var i = 0; i < lines.length / 4; i++) {
3964 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[i * 4], lines[i * 4 + 1], lines[i * 4 + 2], lines[i * 4 + 3], false);
3965 if (intersection.length !== 0) {
3966 intersections.push(intersection[0], intersection[1]);
3967 }
3968 }
3969 if (intersections.length > 2) {
3970 var lowestIntersection = [intersections[0], intersections[1]];
3971 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3972 for (var _i3 = 1; _i3 < intersections.length / 2; _i3++) {
3973 var squaredDistance = Math.pow(intersections[_i3 * 2] - x, 2) + Math.pow(intersections[_i3 * 2 + 1] - y, 2);
3974 if (squaredDistance <= lowestSquaredDistance) {
3975 lowestIntersection[0] = intersections[_i3 * 2];
3976 lowestIntersection[1] = intersections[_i3 * 2 + 1];
3977 lowestSquaredDistance = squaredDistance;
3978 }
3979 }
3980 return lowestIntersection;
3981 }
3982 return intersections;
3983 };
3984 var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3985 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3986 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3987 var lenRatio = (length - amount) / length;
3988 if (lenRatio < 0) {
3989 lenRatio = 0.00001;
3990 }
3991 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3992 };
3993 var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3994 var points = generateUnitNgonPoints(sides, rotationRadians);
3995 points = fitPolygonToSquare(points);
3996 return points;
3997 };
3998 var fitPolygonToSquare = function fitPolygonToSquare(points) {
3999 var x, y;
4000 var sides = points.length / 2;
4001 var minX = Infinity,
4002 minY = Infinity,
4003 maxX = -Infinity,
4004 maxY = -Infinity;
4005 for (var i = 0; i < sides; i++) {
4006 x = points[2 * i];
4007 y = points[2 * i + 1];
4008 minX = Math.min(minX, x);
4009 maxX = Math.max(maxX, x);
4010 minY = Math.min(minY, y);
4011 maxY = Math.max(maxY, y);
4012 }
4013
4014 // stretch factors
4015 var sx = 2 / (maxX - minX);
4016 var sy = 2 / (maxY - minY);
4017 for (var _i4 = 0; _i4 < sides; _i4++) {
4018 x = points[2 * _i4] = points[2 * _i4] * sx;
4019 y = points[2 * _i4 + 1] = points[2 * _i4 + 1] * sy;
4020 minX = Math.min(minX, x);
4021 maxX = Math.max(maxX, x);
4022 minY = Math.min(minY, y);
4023 maxY = Math.max(maxY, y);
4024 }
4025 if (minY < -1) {
4026 for (var _i5 = 0; _i5 < sides; _i5++) {
4027 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] + (-1 - minY);
4028 }
4029 }
4030 return points;
4031 };
4032 var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
4033 var increment = 1.0 / sides * 2 * Math.PI;
4034 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
4035 startAngle += rotationRadians;
4036 var points = new Array(sides * 2);
4037 var currentAngle;
4038 for (var i = 0; i < sides; i++) {
4039 currentAngle = i * increment + startAngle;
4040 points[2 * i] = Math.cos(currentAngle); // x
4041 points[2 * i + 1] = Math.sin(-currentAngle); // y
4042 }
4043
4044 return points;
4045 };
4046
4047 // Set the default radius, unless half of width or height is smaller than default
4048 var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
4049 return Math.min(width / 4, height / 4, 8);
4050 };
4051
4052 // Set the default radius
4053 var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
4054 return Math.min(width / 10, height / 10, 8);
4055 };
4056 var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
4057 return 8;
4058 };
4059 var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
4060 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
4061 };
4062
4063 // get curve width, height, and control point position offsets as a percentage of node height / width
4064 var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
4065 return {
4066 heightOffset: Math.min(15, 0.05 * height),
4067 widthOffset: Math.min(100, 0.25 * width),
4068 ctrlPtOffsetPct: 0.05
4069 };
4070 };
4071
4072 var pageRankDefaults = defaults$g({
4073 dampingFactor: 0.8,
4074 precision: 0.000001,
4075 iterations: 200,
4076 weight: function weight(edge) {
4077 return 1;
4078 }
4079 });
4080 var elesfn$o = {
4081 pageRank: function pageRank(options) {
4082 var _pageRankDefaults = pageRankDefaults(options),
4083 dampingFactor = _pageRankDefaults.dampingFactor,
4084 precision = _pageRankDefaults.precision,
4085 iterations = _pageRankDefaults.iterations,
4086 weight = _pageRankDefaults.weight;
4087 var cy = this._private.cy;
4088 var _this$byGroup = this.byGroup(),
4089 nodes = _this$byGroup.nodes,
4090 edges = _this$byGroup.edges;
4091 var numNodes = nodes.length;
4092 var numNodesSqd = numNodes * numNodes;
4093 var numEdges = edges.length;
4094
4095 // Construct transposed adjacency matrix
4096 // First lets have a zeroed matrix of the right size
4097 // We'll also keep track of the sum of each column
4098 var matrix = new Array(numNodesSqd);
4099 var columnSum = new Array(numNodes);
4100 var additionalProb = (1 - dampingFactor) / numNodes;
4101
4102 // Create null matrix
4103 for (var i = 0; i < numNodes; i++) {
4104 for (var j = 0; j < numNodes; j++) {
4105 var n = i * numNodes + j;
4106 matrix[n] = 0;
4107 }
4108 columnSum[i] = 0;
4109 }
4110
4111 // Now, process edges
4112 for (var _i = 0; _i < numEdges; _i++) {
4113 var edge = edges[_i];
4114 var srcId = edge.data('source');
4115 var tgtId = edge.data('target');
4116
4117 // Don't include loops in the matrix
4118 if (srcId === tgtId) {
4119 continue;
4120 }
4121 var s = nodes.indexOfId(srcId);
4122 var t = nodes.indexOfId(tgtId);
4123 var w = weight(edge);
4124 var _n = t * numNodes + s;
4125
4126 // Update matrix
4127 matrix[_n] += w;
4128
4129 // Update column sum
4130 columnSum[s] += w;
4131 }
4132
4133 // Add additional probability based on damping factor
4134 // Also, take into account columns that have sum = 0
4135 var p = 1.0 / numNodes + additionalProb; // Shorthand
4136
4137 // Traverse matrix, column by column
4138 for (var _j = 0; _j < numNodes; _j++) {
4139 if (columnSum[_j] === 0) {
4140 // No 'links' out from node jth, assume equal probability for each possible node
4141 for (var _i2 = 0; _i2 < numNodes; _i2++) {
4142 var _n2 = _i2 * numNodes + _j;
4143 matrix[_n2] = p;
4144 }
4145 } else {
4146 // Node jth has outgoing link, compute normalized probabilities
4147 for (var _i3 = 0; _i3 < numNodes; _i3++) {
4148 var _n3 = _i3 * numNodes + _j;
4149 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
4150 }
4151 }
4152 }
4153
4154 // Compute dominant eigenvector using power method
4155 var eigenvector = new Array(numNodes);
4156 var temp = new Array(numNodes);
4157 var previous;
4158
4159 // Start with a vector of all 1's
4160 // Also, initialize a null vector which will be used as shorthand
4161 for (var _i4 = 0; _i4 < numNodes; _i4++) {
4162 eigenvector[_i4] = 1;
4163 }
4164 for (var iter = 0; iter < iterations; iter++) {
4165 // Temp array with all 0's
4166 for (var _i5 = 0; _i5 < numNodes; _i5++) {
4167 temp[_i5] = 0;
4168 }
4169
4170 // Multiply matrix with previous result
4171 for (var _i6 = 0; _i6 < numNodes; _i6++) {
4172 for (var _j2 = 0; _j2 < numNodes; _j2++) {
4173 var _n4 = _i6 * numNodes + _j2;
4174 temp[_i6] += matrix[_n4] * eigenvector[_j2];
4175 }
4176 }
4177 inPlaceSumNormalize(temp);
4178 previous = eigenvector;
4179 eigenvector = temp;
4180 temp = previous;
4181 var diff = 0;
4182 // Compute difference (squared module) of both vectors
4183 for (var _i7 = 0; _i7 < numNodes; _i7++) {
4184 var delta = previous[_i7] - eigenvector[_i7];
4185 diff += delta * delta;
4186 }
4187
4188 // If difference is less than the desired threshold, stop iterating
4189 if (diff < precision) {
4190 break;
4191 }
4192 }
4193
4194 // Construct result
4195 var res = {
4196 rank: function rank(node) {
4197 node = cy.collection(node)[0];
4198 return eigenvector[nodes.indexOf(node)];
4199 }
4200 };
4201 return res;
4202 } // pageRank
4203 }; // elesfn
4204
4205 var defaults$f = defaults$g({
4206 root: null,
4207 weight: function weight(edge) {
4208 return 1;
4209 },
4210 directed: false,
4211 alpha: 0
4212 });
4213 var elesfn$n = {
4214 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
4215 options = defaults$f(options);
4216 var cy = this.cy();
4217 var nodes = this.nodes();
4218 var numNodes = nodes.length;
4219 if (!options.directed) {
4220 var degrees = {};
4221 var maxDegree = 0;
4222 for (var i = 0; i < numNodes; i++) {
4223 var node = nodes[i];
4224
4225 // add current node to the current options object and call degreeCentrality
4226 options.root = node;
4227 var currDegree = this.degreeCentrality(options);
4228 if (maxDegree < currDegree.degree) {
4229 maxDegree = currDegree.degree;
4230 }
4231 degrees[node.id()] = currDegree.degree;
4232 }
4233 return {
4234 degree: function degree(node) {
4235 if (maxDegree === 0) {
4236 return 0;
4237 }
4238 if (string(node)) {
4239 // from is a selector string
4240 node = cy.filter(node);
4241 }
4242 return degrees[node.id()] / maxDegree;
4243 }
4244 };
4245 } else {
4246 var indegrees = {};
4247 var outdegrees = {};
4248 var maxIndegree = 0;
4249 var maxOutdegree = 0;
4250 for (var _i = 0; _i < numNodes; _i++) {
4251 var _node = nodes[_i];
4252 var id = _node.id();
4253
4254 // add current node to the current options object and call degreeCentrality
4255 options.root = _node;
4256 var _currDegree = this.degreeCentrality(options);
4257 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
4258 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
4259 indegrees[id] = _currDegree.indegree;
4260 outdegrees[id] = _currDegree.outdegree;
4261 }
4262 return {
4263 indegree: function indegree(node) {
4264 if (maxIndegree == 0) {
4265 return 0;
4266 }
4267 if (string(node)) {
4268 // from is a selector string
4269 node = cy.filter(node);
4270 }
4271 return indegrees[node.id()] / maxIndegree;
4272 },
4273 outdegree: function outdegree(node) {
4274 if (maxOutdegree === 0) {
4275 return 0;
4276 }
4277 if (string(node)) {
4278 // from is a selector string
4279 node = cy.filter(node);
4280 }
4281 return outdegrees[node.id()] / maxOutdegree;
4282 }
4283 };
4284 }
4285 },
4286 // degreeCentralityNormalized
4287
4288 // Implemented from the algorithm in Opsahl's paper
4289 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
4290 // check the heading 2 "Degree"
4291 degreeCentrality: function degreeCentrality(options) {
4292 options = defaults$f(options);
4293 var cy = this.cy();
4294 var callingEles = this;
4295 var _options = options,
4296 root = _options.root,
4297 weight = _options.weight,
4298 directed = _options.directed,
4299 alpha = _options.alpha;
4300 root = cy.collection(root)[0];
4301 if (!directed) {
4302 var connEdges = root.connectedEdges().intersection(callingEles);
4303 var k = connEdges.length;
4304 var s = 0;
4305
4306 // Now, sum edge weights
4307 for (var i = 0; i < connEdges.length; i++) {
4308 s += weight(connEdges[i]);
4309 }
4310 return {
4311 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
4312 };
4313 } else {
4314 var edges = root.connectedEdges();
4315 var incoming = edges.filter(function (edge) {
4316 return edge.target().same(root) && callingEles.has(edge);
4317 });
4318 var outgoing = edges.filter(function (edge) {
4319 return edge.source().same(root) && callingEles.has(edge);
4320 });
4321 var k_in = incoming.length;
4322 var k_out = outgoing.length;
4323 var s_in = 0;
4324 var s_out = 0;
4325
4326 // Now, sum incoming edge weights
4327 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
4328 s_in += weight(incoming[_i2]);
4329 }
4330
4331 // Now, sum outgoing edge weights
4332 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
4333 s_out += weight(outgoing[_i3]);
4334 }
4335 return {
4336 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
4337 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
4338 };
4339 }
4340 } // degreeCentrality
4341 }; // elesfn
4342
4343 // nice, short mathematical alias
4344 elesfn$n.dc = elesfn$n.degreeCentrality;
4345 elesfn$n.dcn = elesfn$n.degreeCentralityNormalised = elesfn$n.degreeCentralityNormalized;
4346
4347 var defaults$e = defaults$g({
4348 harmonic: true,
4349 weight: function weight() {
4350 return 1;
4351 },
4352 directed: false,
4353 root: null
4354 });
4355 var elesfn$m = {
4356 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
4357 var _defaults = defaults$e(options),
4358 harmonic = _defaults.harmonic,
4359 weight = _defaults.weight,
4360 directed = _defaults.directed;
4361 var cy = this.cy();
4362 var closenesses = {};
4363 var maxCloseness = 0;
4364 var nodes = this.nodes();
4365 var fw = this.floydWarshall({
4366 weight: weight,
4367 directed: directed
4368 });
4369
4370 // Compute closeness for every node and find the maximum closeness
4371 for (var i = 0; i < nodes.length; i++) {
4372 var currCloseness = 0;
4373 var node_i = nodes[i];
4374 for (var j = 0; j < nodes.length; j++) {
4375 if (i !== j) {
4376 var d = fw.distance(node_i, nodes[j]);
4377 if (harmonic) {
4378 currCloseness += 1 / d;
4379 } else {
4380 currCloseness += d;
4381 }
4382 }
4383 }
4384 if (!harmonic) {
4385 currCloseness = 1 / currCloseness;
4386 }
4387 if (maxCloseness < currCloseness) {
4388 maxCloseness = currCloseness;
4389 }
4390 closenesses[node_i.id()] = currCloseness;
4391 }
4392 return {
4393 closeness: function closeness(node) {
4394 if (maxCloseness == 0) {
4395 return 0;
4396 }
4397 if (string(node)) {
4398 // from is a selector string
4399 node = cy.filter(node)[0].id();
4400 } else {
4401 // from is a node
4402 node = node.id();
4403 }
4404 return closenesses[node] / maxCloseness;
4405 }
4406 };
4407 },
4408 // Implemented from pseudocode from wikipedia
4409 closenessCentrality: function closenessCentrality(options) {
4410 var _defaults2 = defaults$e(options),
4411 root = _defaults2.root,
4412 weight = _defaults2.weight,
4413 directed = _defaults2.directed,
4414 harmonic = _defaults2.harmonic;
4415 root = this.filter(root)[0];
4416
4417 // we need distance from this node to every other node
4418 var dijkstra = this.dijkstra({
4419 root: root,
4420 weight: weight,
4421 directed: directed
4422 });
4423 var totalDistance = 0;
4424 var nodes = this.nodes();
4425 for (var i = 0; i < nodes.length; i++) {
4426 var n = nodes[i];
4427 if (!n.same(root)) {
4428 var d = dijkstra.distanceTo(n);
4429 if (harmonic) {
4430 totalDistance += 1 / d;
4431 } else {
4432 totalDistance += d;
4433 }
4434 }
4435 }
4436 return harmonic ? totalDistance : 1 / totalDistance;
4437 } // closenessCentrality
4438 }; // elesfn
4439
4440 // nice, short mathematical alias
4441 elesfn$m.cc = elesfn$m.closenessCentrality;
4442 elesfn$m.ccn = elesfn$m.closenessCentralityNormalised = elesfn$m.closenessCentralityNormalized;
4443
4444 var defaults$d = defaults$g({
4445 weight: null,
4446 directed: false
4447 });
4448 var elesfn$l = {
4449 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
4450 betweennessCentrality: function betweennessCentrality(options) {
4451 var _defaults = defaults$d(options),
4452 directed = _defaults.directed,
4453 weight = _defaults.weight;
4454 var weighted = weight != null;
4455 var cy = this.cy();
4456
4457 // starting
4458 var V = this.nodes();
4459 var A = {};
4460 var _C = {};
4461 var max = 0;
4462 var C = {
4463 set: function set(key, val) {
4464 _C[key] = val;
4465 if (val > max) {
4466 max = val;
4467 }
4468 },
4469 get: function get(key) {
4470 return _C[key];
4471 }
4472 };
4473
4474 // A contains the neighborhoods of every node
4475 for (var i = 0; i < V.length; i++) {
4476 var v = V[i];
4477 var vid = v.id();
4478 if (directed) {
4479 A[vid] = v.outgoers().nodes(); // get outgoers of every node
4480 } else {
4481 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
4482 }
4483
4484 C.set(vid, 0);
4485 }
4486 var _loop = function _loop(s) {
4487 var sid = V[s].id();
4488 var S = []; // stack
4489 var P = {};
4490 var g = {};
4491 var d = {};
4492 var Q = new heap(function (a, b) {
4493 return d[a] - d[b];
4494 }); // queue
4495
4496 // init dictionaries
4497 for (var _i = 0; _i < V.length; _i++) {
4498 var _vid = V[_i].id();
4499 P[_vid] = [];
4500 g[_vid] = 0;
4501 d[_vid] = Infinity;
4502 }
4503 g[sid] = 1; // sigma
4504 d[sid] = 0; // distance to s
4505
4506 Q.push(sid);
4507 while (!Q.empty()) {
4508 var _v = Q.pop();
4509 S.push(_v);
4510 if (weighted) {
4511 for (var j = 0; j < A[_v].length; j++) {
4512 var w = A[_v][j];
4513 var vEle = cy.getElementById(_v);
4514 var edge = void 0;
4515 if (vEle.edgesTo(w).length > 0) {
4516 edge = vEle.edgesTo(w)[0];
4517 } else {
4518 edge = w.edgesTo(vEle)[0];
4519 }
4520 var edgeWeight = weight(edge);
4521 w = w.id();
4522 if (d[w] > d[_v] + edgeWeight) {
4523 d[w] = d[_v] + edgeWeight;
4524 if (Q.nodes.indexOf(w) < 0) {
4525 //if w is not in Q
4526 Q.push(w);
4527 } else {
4528 // update position if w is in Q
4529 Q.updateItem(w);
4530 }
4531 g[w] = 0;
4532 P[w] = [];
4533 }
4534 if (d[w] == d[_v] + edgeWeight) {
4535 g[w] = g[w] + g[_v];
4536 P[w].push(_v);
4537 }
4538 }
4539 } else {
4540 for (var _j = 0; _j < A[_v].length; _j++) {
4541 var _w = A[_v][_j].id();
4542 if (d[_w] == Infinity) {
4543 Q.push(_w);
4544 d[_w] = d[_v] + 1;
4545 }
4546 if (d[_w] == d[_v] + 1) {
4547 g[_w] = g[_w] + g[_v];
4548 P[_w].push(_v);
4549 }
4550 }
4551 }
4552 }
4553 var e = {};
4554 for (var _i2 = 0; _i2 < V.length; _i2++) {
4555 e[V[_i2].id()] = 0;
4556 }
4557 while (S.length > 0) {
4558 var _w2 = S.pop();
4559 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
4560 var _v2 = P[_w2][_j2];
4561 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
4562 }
4563 if (_w2 != V[s].id()) {
4564 C.set(_w2, C.get(_w2) + e[_w2]);
4565 }
4566 }
4567 };
4568 for (var s = 0; s < V.length; s++) {
4569 _loop(s);
4570 }
4571 var ret = {
4572 betweenness: function betweenness(node) {
4573 var id = cy.collection(node).id();
4574 return C.get(id);
4575 },
4576 betweennessNormalized: function betweennessNormalized(node) {
4577 if (max == 0) {
4578 return 0;
4579 }
4580 var id = cy.collection(node).id();
4581 return C.get(id) / max;
4582 }
4583 };
4584
4585 // alias
4586 ret.betweennessNormalised = ret.betweennessNormalized;
4587 return ret;
4588 } // betweennessCentrality
4589 }; // elesfn
4590
4591 // nice, short mathematical alias
4592 elesfn$l.bc = elesfn$l.betweennessCentrality;
4593
4594 // Implemented by Zoe Xi @zoexi for GSOC 2016
4595
4596 /* eslint-disable no-unused-vars */
4597 var defaults$c = defaults$g({
4598 expandFactor: 2,
4599 // affects time of computation and cluster granularity to some extent: M * M
4600 inflateFactor: 2,
4601 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4602 multFactor: 1,
4603 // optional self loops for each node. Use a neutral value to improve cluster computations.
4604 maxIterations: 20,
4605 // maximum number of iterations of the MCL algorithm in a single run
4606 attributes: [
4607 // attributes/features used to group nodes, ie. similarity values between nodes
4608 function (edge) {
4609 return 1;
4610 }]
4611 });
4612 /* eslint-enable */
4613
4614 var setOptions$3 = function setOptions(options) {
4615 return defaults$c(options);
4616 };
4617 /* eslint-enable */
4618
4619 var getSimilarity$1 = function getSimilarity(edge, attributes) {
4620 var total = 0;
4621 for (var i = 0; i < attributes.length; i++) {
4622 total += attributes[i](edge);
4623 }
4624 return total;
4625 };
4626 var addLoops = function addLoops(M, n, val) {
4627 for (var i = 0; i < n; i++) {
4628 M[i * n + i] = val;
4629 }
4630 };
4631 var normalize = function normalize(M, n) {
4632 var sum;
4633 for (var col = 0; col < n; col++) {
4634 sum = 0;
4635 for (var row = 0; row < n; row++) {
4636 sum += M[row * n + col];
4637 }
4638 for (var _row = 0; _row < n; _row++) {
4639 M[_row * n + col] = M[_row * n + col] / sum;
4640 }
4641 }
4642 };
4643
4644 // TODO: blocked matrix multiplication?
4645 var mmult = function mmult(A, B, n) {
4646 var C = new Array(n * n);
4647 for (var i = 0; i < n; i++) {
4648 for (var j = 0; j < n; j++) {
4649 C[i * n + j] = 0;
4650 }
4651 for (var k = 0; k < n; k++) {
4652 for (var _j = 0; _j < n; _j++) {
4653 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4654 }
4655 }
4656 }
4657 return C;
4658 };
4659 var expand = function expand(M, n, expandFactor /** power **/) {
4660 var _M = M.slice(0);
4661 for (var p = 1; p < expandFactor; p++) {
4662 M = mmult(M, _M, n);
4663 }
4664 return M;
4665 };
4666 var inflate = function inflate(M, n, inflateFactor /** r **/) {
4667 var _M = new Array(n * n);
4668
4669 // M(i,j) ^ inflatePower
4670 for (var i = 0; i < n * n; i++) {
4671 _M[i] = Math.pow(M[i], inflateFactor);
4672 }
4673 normalize(_M, n);
4674 return _M;
4675 };
4676 var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4677 // Check that both matrices have the same elements (i,j)
4678 for (var i = 0; i < n2; i++) {
4679 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4680 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4681 if (v1 !== v2) {
4682 return false;
4683 }
4684 }
4685 return true;
4686 };
4687 var assign$2 = function assign(M, n, nodes, cy) {
4688 var clusters = [];
4689 for (var i = 0; i < n; i++) {
4690 var cluster = [];
4691 for (var j = 0; j < n; j++) {
4692 // Row-wise attractors and elements that they attract belong in same cluster
4693 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4694 cluster.push(nodes[j]);
4695 }
4696 }
4697 if (cluster.length !== 0) {
4698 clusters.push(cy.collection(cluster));
4699 }
4700 }
4701 return clusters;
4702 };
4703 var isDuplicate = function isDuplicate(c1, c2) {
4704 for (var i = 0; i < c1.length; i++) {
4705 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4706 return false;
4707 }
4708 }
4709 return true;
4710 };
4711 var removeDuplicates = function removeDuplicates(clusters) {
4712 for (var i = 0; i < clusters.length; i++) {
4713 for (var j = 0; j < clusters.length; j++) {
4714 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4715 clusters.splice(j, 1);
4716 }
4717 }
4718 }
4719 return clusters;
4720 };
4721 var markovClustering = function markovClustering(options) {
4722 var nodes = this.nodes();
4723 var edges = this.edges();
4724 var cy = this.cy();
4725
4726 // Set parameters of algorithm:
4727 var opts = setOptions$3(options);
4728
4729 // Map each node to its position in node array
4730 var id2position = {};
4731 for (var i = 0; i < nodes.length; i++) {
4732 id2position[nodes[i].id()] = i;
4733 }
4734
4735 // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4736 var n = nodes.length,
4737 n2 = n * n;
4738 var M = new Array(n2),
4739 _M;
4740 for (var _i = 0; _i < n2; _i++) {
4741 M[_i] = 0;
4742 }
4743 for (var e = 0; e < edges.length; e++) {
4744 var edge = edges[e];
4745 var _i2 = id2position[edge.source().id()];
4746 var j = id2position[edge.target().id()];
4747 var sim = getSimilarity$1(edge, opts.attributes);
4748 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4749 M[j * n + _i2] += sim;
4750 }
4751
4752 // Begin Markov cluster algorithm
4753
4754 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4755 addLoops(M, n, opts.multFactor);
4756
4757 // Step 2: M = normalize( M );
4758 normalize(M, n);
4759 var isStillMoving = true;
4760 var iterations = 0;
4761 while (isStillMoving && iterations < opts.maxIterations) {
4762 isStillMoving = false;
4763
4764 // Step 3:
4765 _M = expand(M, n, opts.expandFactor);
4766
4767 // Step 4:
4768 M = inflate(_M, n, opts.inflateFactor);
4769
4770 // Step 5: check to see if ~steady state has been reached
4771 if (!hasConverged(M, _M, n2, 4)) {
4772 isStillMoving = true;
4773 }
4774 iterations++;
4775 }
4776
4777 // Build clusters from matrix
4778 var clusters = assign$2(M, n, nodes, cy);
4779
4780 // Remove duplicate clusters due to symmetry of graph and M matrix
4781 clusters = removeDuplicates(clusters);
4782 return clusters;
4783 };
4784 var markovClustering$1 = {
4785 markovClustering: markovClustering,
4786 mcl: markovClustering
4787 };
4788
4789 // Common distance metrics for clustering algorithms
4790 var identity = function identity(x) {
4791 return x;
4792 };
4793 var absDiff = function absDiff(p, q) {
4794 return Math.abs(q - p);
4795 };
4796 var addAbsDiff = function addAbsDiff(total, p, q) {
4797 return total + absDiff(p, q);
4798 };
4799 var addSquaredDiff = function addSquaredDiff(total, p, q) {
4800 return total + Math.pow(q - p, 2);
4801 };
4802 var sqrt = function sqrt(x) {
4803 return Math.sqrt(x);
4804 };
4805 var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4806 return Math.max(currentMax, absDiff(p, q));
4807 };
4808 var getDistance = function getDistance(length, getP, getQ, init, visit) {
4809 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4810 var ret = init;
4811 var p, q;
4812 for (var dim = 0; dim < length; dim++) {
4813 p = getP(dim);
4814 q = getQ(dim);
4815 ret = visit(ret, p, q);
4816 }
4817 return post(ret);
4818 };
4819 var distances = {
4820 euclidean: function euclidean(length, getP, getQ) {
4821 if (length >= 2) {
4822 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4823 } else {
4824 // for single attr case, more efficient to avoid sqrt
4825 return getDistance(length, getP, getQ, 0, addAbsDiff);
4826 }
4827 },
4828 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4829 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4830 },
4831 manhattan: function manhattan(length, getP, getQ) {
4832 return getDistance(length, getP, getQ, 0, addAbsDiff);
4833 },
4834 max: function max(length, getP, getQ) {
4835 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4836 }
4837 };
4838
4839 // in case the user accidentally doesn't use camel case
4840 distances['squared-euclidean'] = distances['squaredEuclidean'];
4841 distances['squaredeuclidean'] = distances['squaredEuclidean'];
4842 function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4843 var impl;
4844 if (fn$6(method)) {
4845 impl = method;
4846 } else {
4847 impl = distances[method] || distances.euclidean;
4848 }
4849 if (length === 0 && fn$6(method)) {
4850 return impl(nodeP, nodeQ);
4851 } else {
4852 return impl(length, getP, getQ, nodeP, nodeQ);
4853 }
4854 }
4855
4856 var defaults$b = defaults$g({
4857 k: 2,
4858 m: 2,
4859 sensitivityThreshold: 0.0001,
4860 distance: 'euclidean',
4861 maxIterations: 10,
4862 attributes: [],
4863 testMode: false,
4864 testCentroids: null
4865 });
4866 var setOptions$2 = function setOptions(options) {
4867 return defaults$b(options);
4868 };
4869
4870 var getDist = function getDist(type, node, centroid, attributes, mode) {
4871 var noNodeP = mode !== 'kMedoids';
4872 var getP = noNodeP ? function (i) {
4873 return centroid[i];
4874 } : function (i) {
4875 return attributes[i](centroid);
4876 };
4877 var getQ = function getQ(i) {
4878 return attributes[i](node);
4879 };
4880 var nodeP = centroid;
4881 var nodeQ = node;
4882 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4883 };
4884 var randomCentroids = function randomCentroids(nodes, k, attributes) {
4885 var ndim = attributes.length;
4886 var min = new Array(ndim);
4887 var max = new Array(ndim);
4888 var centroids = new Array(k);
4889 var centroid = null;
4890
4891 // Find min, max values for each attribute dimension
4892 for (var i = 0; i < ndim; i++) {
4893 min[i] = nodes.min(attributes[i]).value;
4894 max[i] = nodes.max(attributes[i]).value;
4895 }
4896
4897 // Build k centroids, each represented as an n-dim feature vector
4898 for (var c = 0; c < k; c++) {
4899 centroid = [];
4900 for (var _i = 0; _i < ndim; _i++) {
4901 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4902 }
4903
4904 centroids[c] = centroid;
4905 }
4906 return centroids;
4907 };
4908 var classify = function classify(node, centroids, distance, attributes, type) {
4909 var min = Infinity;
4910 var index = 0;
4911 for (var i = 0; i < centroids.length; i++) {
4912 var dist = getDist(distance, node, centroids[i], attributes, type);
4913 if (dist < min) {
4914 min = dist;
4915 index = i;
4916 }
4917 }
4918 return index;
4919 };
4920 var buildCluster = function buildCluster(centroid, nodes, assignment) {
4921 var cluster = [];
4922 var node = null;
4923 for (var n = 0; n < nodes.length; n++) {
4924 node = nodes[n];
4925 if (assignment[node.id()] === centroid) {
4926 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4927 cluster.push(node);
4928 }
4929 }
4930 return cluster;
4931 };
4932 var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4933 return Math.abs(v2 - v1) <= sensitivityThreshold;
4934 };
4935 var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4936 for (var i = 0; i < v1.length; i++) {
4937 for (var j = 0; j < v1[i].length; j++) {
4938 var diff = Math.abs(v1[i][j] - v2[i][j]);
4939 if (diff > sensitivityThreshold) {
4940 return false;
4941 }
4942 }
4943 }
4944 return true;
4945 };
4946 var seenBefore = function seenBefore(node, medoids, n) {
4947 for (var i = 0; i < n; i++) {
4948 if (node === medoids[i]) return true;
4949 }
4950 return false;
4951 };
4952 var randomMedoids = function randomMedoids(nodes, k) {
4953 var medoids = new Array(k);
4954
4955 // For small data sets, the probability of medoid conflict is greater,
4956 // so we need to check to see if we've already seen or chose this node before.
4957 if (nodes.length < 50) {
4958 // Randomly select k medoids from the n nodes
4959 for (var i = 0; i < k; i++) {
4960 var node = nodes[Math.floor(Math.random() * nodes.length)];
4961
4962 // If we've already chosen this node to be a medoid, don't choose it again (for small data sets).
4963 // Instead choose a different random node.
4964 while (seenBefore(node, medoids, i)) {
4965 node = nodes[Math.floor(Math.random() * nodes.length)];
4966 }
4967 medoids[i] = node;
4968 }
4969 } else {
4970 // Relatively large data set, so pretty safe to not check and just select random nodes
4971 for (var _i2 = 0; _i2 < k; _i2++) {
4972 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4973 }
4974 }
4975 return medoids;
4976 };
4977 var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4978 var cost = 0;
4979 for (var n = 0; n < cluster.length; n++) {
4980 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4981 }
4982 return cost;
4983 };
4984 var kMeans = function kMeans(options) {
4985 var cy = this.cy();
4986 var nodes = this.nodes();
4987 var node = null;
4988
4989 // Set parameters of algorithm: # of clusters, distance metric, etc.
4990 var opts = setOptions$2(options);
4991
4992 // Begin k-means algorithm
4993 var clusters = new Array(opts.k);
4994 var assignment = {};
4995 var centroids;
4996
4997 // Step 1: Initialize centroid positions
4998 if (opts.testMode) {
4999 if (typeof opts.testCentroids === 'number') {
5000 // TODO: implement a seeded random number generator.
5001 opts.testCentroids;
5002 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5003 } else if (_typeof(opts.testCentroids) === 'object') {
5004 centroids = opts.testCentroids;
5005 } else {
5006 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5007 }
5008 } else {
5009 centroids = randomCentroids(nodes, opts.k, opts.attributes);
5010 }
5011 var isStillMoving = true;
5012 var iterations = 0;
5013 while (isStillMoving && iterations < opts.maxIterations) {
5014 // Step 2: Assign nodes to the nearest centroid
5015 for (var n = 0; n < nodes.length; n++) {
5016 node = nodes[n];
5017 // Determine which cluster this node belongs to: node id => cluster #
5018 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
5019 }
5020
5021 // Step 3: For each of the k clusters, update its centroid
5022 isStillMoving = false;
5023 for (var c = 0; c < opts.k; c++) {
5024 // Get all nodes that belong to this cluster
5025 var cluster = buildCluster(c, nodes, assignment);
5026 if (cluster.length === 0) {
5027 // If cluster is empty, break out early & move to next cluster
5028 continue;
5029 }
5030
5031 // Update centroids by calculating avg of all nodes within the cluster.
5032 var ndim = opts.attributes.length;
5033 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
5034 var newCentroid = new Array(ndim);
5035 var sum = new Array(ndim);
5036 for (var d = 0; d < ndim; d++) {
5037 sum[d] = 0.0;
5038 for (var i = 0; i < cluster.length; i++) {
5039 node = cluster[i];
5040 sum[d] += opts.attributes[d](node);
5041 }
5042 newCentroid[d] = sum[d] / cluster.length;
5043
5044 // Check to see if algorithm has converged, i.e. when centroids no longer change
5045 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
5046 isStillMoving = true;
5047 }
5048 }
5049 centroids[c] = newCentroid;
5050 clusters[c] = cy.collection(cluster);
5051 }
5052 iterations++;
5053 }
5054 return clusters;
5055 };
5056 var kMedoids = function kMedoids(options) {
5057 var cy = this.cy();
5058 var nodes = this.nodes();
5059 var node = null;
5060 var opts = setOptions$2(options);
5061
5062 // Begin k-medoids algorithm
5063 var clusters = new Array(opts.k);
5064 var medoids;
5065 var assignment = {};
5066 var curCost;
5067 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
5068
5069 // Step 1: Initialize k medoids
5070 if (opts.testMode) {
5071 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
5072 medoids = opts.testCentroids;
5073 } else {
5074 medoids = randomMedoids(nodes, opts.k);
5075 }
5076 } else {
5077 medoids = randomMedoids(nodes, opts.k);
5078 }
5079 var isStillMoving = true;
5080 var iterations = 0;
5081 while (isStillMoving && iterations < opts.maxIterations) {
5082 // Step 2: Assign nodes to the nearest medoid
5083 for (var n = 0; n < nodes.length; n++) {
5084 node = nodes[n];
5085 // Determine which cluster this node belongs to: node id => cluster #
5086 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
5087 }
5088 isStillMoving = false;
5089 // Step 3: For each medoid m, and for each node associated with mediod m,
5090 // select the node with the lowest configuration cost as new medoid.
5091 for (var m = 0; m < medoids.length; m++) {
5092 // Get all nodes that belong to this medoid
5093 var cluster = buildCluster(m, nodes, assignment);
5094 if (cluster.length === 0) {
5095 // If cluster is empty, break out early & move to next cluster
5096 continue;
5097 }
5098 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
5099
5100 // Select different medoid if its configuration has the lowest cost
5101 for (var _n = 0; _n < cluster.length; _n++) {
5102 curCost = findCost(cluster[_n], cluster, opts.attributes);
5103 if (curCost < minCosts[m]) {
5104 minCosts[m] = curCost;
5105 medoids[m] = cluster[_n];
5106 isStillMoving = true;
5107 }
5108 }
5109 clusters[m] = cy.collection(cluster);
5110 }
5111 iterations++;
5112 }
5113 return clusters;
5114 };
5115 var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
5116 var numerator, denominator;
5117 for (var n = 0; n < nodes.length; n++) {
5118 for (var c = 0; c < centroids.length; c++) {
5119 weight[n][c] = Math.pow(U[n][c], opts.m);
5120 }
5121 }
5122 for (var _c = 0; _c < centroids.length; _c++) {
5123 for (var dim = 0; dim < opts.attributes.length; dim++) {
5124 numerator = 0;
5125 denominator = 0;
5126 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
5127 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
5128 denominator += weight[_n2][_c];
5129 }
5130 centroids[_c][dim] = numerator / denominator;
5131 }
5132 }
5133 };
5134 var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
5135 // Save previous step
5136 for (var i = 0; i < U.length; i++) {
5137 _U[i] = U[i].slice();
5138 }
5139 var sum, numerator, denominator;
5140 var pow = 2 / (opts.m - 1);
5141 for (var c = 0; c < centroids.length; c++) {
5142 for (var n = 0; n < nodes.length; n++) {
5143 sum = 0;
5144 for (var k = 0; k < centroids.length; k++) {
5145 // against all other centroids
5146 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
5147 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
5148 sum += Math.pow(numerator / denominator, pow);
5149 }
5150 U[n][c] = 1 / sum;
5151 }
5152 }
5153 };
5154 var assign$1 = function assign(nodes, U, opts, cy) {
5155 var clusters = new Array(opts.k);
5156 for (var c = 0; c < clusters.length; c++) {
5157 clusters[c] = [];
5158 }
5159 var max;
5160 var index;
5161 for (var n = 0; n < U.length; n++) {
5162 // for each node (U is N x C matrix)
5163 max = -Infinity;
5164 index = -1;
5165 // Determine which cluster the node is most likely to belong in
5166 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
5167 if (U[n][_c2] > max) {
5168 max = U[n][_c2];
5169 index = _c2;
5170 }
5171 }
5172 clusters[index].push(nodes[n]);
5173 }
5174
5175 // Turn every array into a collection of nodes
5176 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
5177 clusters[_c3] = cy.collection(clusters[_c3]);
5178 }
5179 return clusters;
5180 };
5181 var fuzzyCMeans = function fuzzyCMeans(options) {
5182 var cy = this.cy();
5183 var nodes = this.nodes();
5184 var opts = setOptions$2(options);
5185
5186 // Begin fuzzy c-means algorithm
5187 var clusters;
5188 var centroids;
5189 var U;
5190 var _U;
5191 var weight;
5192
5193 // Step 1: Initialize letiables.
5194 _U = new Array(nodes.length);
5195 for (var i = 0; i < nodes.length; i++) {
5196 // N x C matrix
5197 _U[i] = new Array(opts.k);
5198 }
5199 U = new Array(nodes.length);
5200 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
5201 // N x C matrix
5202 U[_i3] = new Array(opts.k);
5203 }
5204 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
5205 var total = 0;
5206 for (var j = 0; j < opts.k; j++) {
5207 U[_i4][j] = Math.random();
5208 total += U[_i4][j];
5209 }
5210 for (var _j = 0; _j < opts.k; _j++) {
5211 U[_i4][_j] = U[_i4][_j] / total;
5212 }
5213 }
5214 centroids = new Array(opts.k);
5215 for (var _i5 = 0; _i5 < opts.k; _i5++) {
5216 centroids[_i5] = new Array(opts.attributes.length);
5217 }
5218 weight = new Array(nodes.length);
5219 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
5220 // N x C matrix
5221 weight[_i6] = new Array(opts.k);
5222 }
5223 // end init FCM
5224
5225 var isStillMoving = true;
5226 var iterations = 0;
5227 while (isStillMoving && iterations < opts.maxIterations) {
5228 isStillMoving = false;
5229
5230 // Step 2: Calculate the centroids for each step.
5231 updateCentroids(centroids, nodes, U, weight, opts);
5232
5233 // Step 3: Update the partition matrix U.
5234 updateMembership(U, _U, centroids, nodes, opts);
5235
5236 // Step 4: Check for convergence.
5237 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
5238 isStillMoving = true;
5239 }
5240 iterations++;
5241 }
5242
5243 // Assign nodes to clusters with highest probability.
5244 clusters = assign$1(nodes, U, opts, cy);
5245 return {
5246 clusters: clusters,
5247 degreeOfMembership: U
5248 };
5249 };
5250 var kClustering = {
5251 kMeans: kMeans,
5252 kMedoids: kMedoids,
5253 fuzzyCMeans: fuzzyCMeans,
5254 fcm: fuzzyCMeans
5255 };
5256
5257 // Implemented by Zoe Xi @zoexi for GSOC 2016
5258 var defaults$a = defaults$g({
5259 distance: 'euclidean',
5260 // distance metric to compare nodes
5261 linkage: 'min',
5262 // linkage criterion : how to determine the distance between clusters of nodes
5263 mode: 'threshold',
5264 // mode:'threshold' => clusters must be threshold distance apart
5265 threshold: Infinity,
5266 // the distance threshold
5267 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
5268 addDendrogram: false,
5269 // whether to add the dendrogram to the graph for viz
5270 dendrogramDepth: 0,
5271 // depth at which dendrogram branches are merged into the returned clusters
5272 attributes: [] // array of attr functions
5273 });
5274
5275 var linkageAliases = {
5276 'single': 'min',
5277 'complete': 'max'
5278 };
5279 var setOptions$1 = function setOptions(options) {
5280 var opts = defaults$a(options);
5281 var preferredAlias = linkageAliases[opts.linkage];
5282 if (preferredAlias != null) {
5283 opts.linkage = preferredAlias;
5284 }
5285 return opts;
5286 };
5287 var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
5288 // Find two closest clusters from cached mins
5289 var minKey = 0;
5290 var min = Infinity;
5291 var dist;
5292 var attrs = opts.attributes;
5293 var getDist = function getDist(n1, n2) {
5294 return clusteringDistance(opts.distance, attrs.length, function (i) {
5295 return attrs[i](n1);
5296 }, function (i) {
5297 return attrs[i](n2);
5298 }, n1, n2);
5299 };
5300 for (var i = 0; i < clusters.length; i++) {
5301 var key = clusters[i].key;
5302 var _dist = dists[key][mins[key]];
5303 if (_dist < min) {
5304 minKey = key;
5305 min = _dist;
5306 }
5307 }
5308 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
5309 return false;
5310 }
5311 var c1 = index[minKey];
5312 var c2 = index[mins[minKey]];
5313 var merged;
5314
5315 // Merge two closest clusters
5316 if (opts.mode === 'dendrogram') {
5317 merged = {
5318 left: c1,
5319 right: c2,
5320 key: c1.key
5321 };
5322 } else {
5323 merged = {
5324 value: c1.value.concat(c2.value),
5325 key: c1.key
5326 };
5327 }
5328 clusters[c1.index] = merged;
5329 clusters.splice(c2.index, 1);
5330 index[c1.key] = merged;
5331
5332 // Update distances with new merged cluster
5333 for (var _i = 0; _i < clusters.length; _i++) {
5334 var cur = clusters[_i];
5335 if (c1.key === cur.key) {
5336 dist = Infinity;
5337 } else if (opts.linkage === 'min') {
5338 dist = dists[c1.key][cur.key];
5339 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
5340 dist = dists[c2.key][cur.key];
5341 }
5342 } else if (opts.linkage === 'max') {
5343 dist = dists[c1.key][cur.key];
5344 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
5345 dist = dists[c2.key][cur.key];
5346 }
5347 } else if (opts.linkage === 'mean') {
5348 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
5349 } else {
5350 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
5351 }
5352 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
5353 }
5354
5355 // Update cached mins
5356 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
5357 var key1 = clusters[_i2].key;
5358 if (mins[key1] === c1.key || mins[key1] === c2.key) {
5359 var _min = key1;
5360 for (var j = 0; j < clusters.length; j++) {
5361 var key2 = clusters[j].key;
5362 if (dists[key1][key2] < dists[key1][_min]) {
5363 _min = key2;
5364 }
5365 }
5366 mins[key1] = _min;
5367 }
5368 clusters[_i2].index = _i2;
5369 }
5370
5371 // Clean up meta data used for clustering
5372 c1.key = c2.key = c1.index = c2.index = null;
5373 return true;
5374 };
5375 var getAllChildren = function getAllChildren(root, arr, cy) {
5376 if (!root) return;
5377 if (root.value) {
5378 arr.push(root.value);
5379 } else {
5380 if (root.left) getAllChildren(root.left, arr);
5381 if (root.right) getAllChildren(root.right, arr);
5382 }
5383 };
5384 var buildDendrogram = function buildDendrogram(root, cy) {
5385 if (!root) return '';
5386 if (root.left && root.right) {
5387 var leftStr = buildDendrogram(root.left, cy);
5388 var rightStr = buildDendrogram(root.right, cy);
5389 var node = cy.add({
5390 group: 'nodes',
5391 data: {
5392 id: leftStr + ',' + rightStr
5393 }
5394 });
5395 cy.add({
5396 group: 'edges',
5397 data: {
5398 source: leftStr,
5399 target: node.id()
5400 }
5401 });
5402 cy.add({
5403 group: 'edges',
5404 data: {
5405 source: rightStr,
5406 target: node.id()
5407 }
5408 });
5409 return node.id();
5410 } else if (root.value) {
5411 return root.value.id();
5412 }
5413 };
5414 var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
5415 if (!root) return [];
5416 var left = [],
5417 right = [],
5418 leaves = [];
5419 if (k === 0) {
5420 // don't cut tree, simply return all nodes as 1 single cluster
5421 if (root.left) getAllChildren(root.left, left);
5422 if (root.right) getAllChildren(root.right, right);
5423 leaves = left.concat(right);
5424 return [cy.collection(leaves)];
5425 } else if (k === 1) {
5426 // cut at root
5427
5428 if (root.value) {
5429 // leaf node
5430 return [cy.collection(root.value)];
5431 } else {
5432 if (root.left) getAllChildren(root.left, left);
5433 if (root.right) getAllChildren(root.right, right);
5434 return [cy.collection(left), cy.collection(right)];
5435 }
5436 } else {
5437 if (root.value) {
5438 return [cy.collection(root.value)];
5439 } else {
5440 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
5441 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
5442 return left.concat(right);
5443 }
5444 }
5445 };
5446
5447 var hierarchicalClustering = function hierarchicalClustering(options) {
5448 var cy = this.cy();
5449 var nodes = this.nodes();
5450
5451 // Set parameters of algorithm: linkage type, distance metric, etc.
5452 var opts = setOptions$1(options);
5453 var attrs = opts.attributes;
5454 var getDist = function getDist(n1, n2) {
5455 return clusteringDistance(opts.distance, attrs.length, function (i) {
5456 return attrs[i](n1);
5457 }, function (i) {
5458 return attrs[i](n2);
5459 }, n1, n2);
5460 };
5461
5462 // Begin hierarchical algorithm
5463 var clusters = [];
5464 var dists = []; // distances between each pair of clusters
5465 var mins = []; // closest cluster for each cluster
5466 var index = []; // hash of all clusters by key
5467
5468 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5469 for (var n = 0; n < nodes.length; n++) {
5470 var cluster = {
5471 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5472 key: n,
5473 index: n
5474 };
5475 clusters[n] = cluster;
5476 index[n] = cluster;
5477 dists[n] = [];
5478 mins[n] = 0;
5479 }
5480
5481 // Calculate the distance between each pair of clusters
5482 for (var i = 0; i < clusters.length; i++) {
5483 for (var j = 0; j <= i; j++) {
5484 var dist = void 0;
5485 if (opts.mode === 'dendrogram') {
5486 // modes store cluster values differently
5487 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5488 } else {
5489 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5490 }
5491 dists[i][j] = dist;
5492 dists[j][i] = dist;
5493 if (dist < dists[i][mins[i]]) {
5494 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5495 }
5496 }
5497 }
5498
5499 // Find the closest pair of clusters and merge them into a single cluster.
5500 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5501 var merged = mergeClosest(clusters, index, dists, mins, opts);
5502 while (merged) {
5503 merged = mergeClosest(clusters, index, dists, mins, opts);
5504 }
5505 var retClusters;
5506
5507 // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5508 // in addition to returning the clusters.
5509 if (opts.mode === 'dendrogram') {
5510 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5511 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5512 } else {
5513 // Regular mode simply returns the clusters
5514
5515 retClusters = new Array(clusters.length);
5516 clusters.forEach(function (cluster, i) {
5517 // Clean up meta data used for clustering
5518 cluster.key = cluster.index = null;
5519 retClusters[i] = cy.collection(cluster.value);
5520 });
5521 }
5522 return retClusters;
5523 };
5524 var hierarchicalClustering$1 = {
5525 hierarchicalClustering: hierarchicalClustering,
5526 hca: hierarchicalClustering
5527 };
5528
5529 // Implemented by Zoe Xi @zoexi for GSOC 2016
5530 var defaults$9 = defaults$g({
5531 distance: 'euclidean',
5532 // distance metric to compare attributes between two nodes
5533 preference: 'median',
5534 // suitability of a data point to serve as an exemplar
5535 damping: 0.8,
5536 // damping factor between [0.5, 1)
5537 maxIterations: 1000,
5538 // max number of iterations to run
5539 minIterations: 100,
5540 // min number of iterations to run in order for clustering to stop
5541 attributes: [// functions to quantify the similarity between any two points
5542 // e.g. node => node.data('weight')
5543 ]
5544 });
5545 var setOptions = function setOptions(options) {
5546 var dmp = options.damping;
5547 var pref = options.preference;
5548 if (!(0.5 <= dmp && dmp < 1)) {
5549 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5550 }
5551 var validPrefs = ['median', 'mean', 'min', 'max'];
5552 if (!(validPrefs.some(function (v) {
5553 return v === pref;
5554 }) || number$1(pref))) {
5555 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5556 return "'".concat(p, "'");
5557 }).join(', '), "] or a number. Got: ").concat(pref));
5558 }
5559 return defaults$9(options);
5560 };
5561
5562 var getSimilarity = function getSimilarity(type, n1, n2, attributes) {
5563 var attr = function attr(n, i) {
5564 return attributes[i](n);
5565 };
5566
5567 // nb negative because similarity should have an inverse relationship to distance
5568 return -clusteringDistance(type, attributes.length, function (i) {
5569 return attr(n1, i);
5570 }, function (i) {
5571 return attr(n2, i);
5572 }, n1, n2);
5573 };
5574 var getPreference = function getPreference(S, preference) {
5575 // larger preference = greater # of clusters
5576 var p = null;
5577 if (preference === 'median') {
5578 p = median(S);
5579 } else if (preference === 'mean') {
5580 p = mean(S);
5581 } else if (preference === 'min') {
5582 p = min(S);
5583 } else if (preference === 'max') {
5584 p = max(S);
5585 } else {
5586 // Custom preference number, as set by user
5587 p = preference;
5588 }
5589 return p;
5590 };
5591 var findExemplars = function findExemplars(n, R, A) {
5592 var indices = [];
5593 for (var i = 0; i < n; i++) {
5594 if (R[i * n + i] + A[i * n + i] > 0) {
5595 indices.push(i);
5596 }
5597 }
5598 return indices;
5599 };
5600 var assignClusters = function assignClusters(n, S, exemplars) {
5601 var clusters = [];
5602 for (var i = 0; i < n; i++) {
5603 var index = -1;
5604 var max = -Infinity;
5605 for (var ei = 0; ei < exemplars.length; ei++) {
5606 var e = exemplars[ei];
5607 if (S[i * n + e] > max) {
5608 index = e;
5609 max = S[i * n + e];
5610 }
5611 }
5612 if (index > 0) {
5613 clusters.push(index);
5614 }
5615 }
5616 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5617 clusters[exemplars[_ei]] = exemplars[_ei];
5618 }
5619 return clusters;
5620 };
5621 var assign = function assign(n, S, exemplars) {
5622 var clusters = assignClusters(n, S, exemplars);
5623 for (var ei = 0; ei < exemplars.length; ei++) {
5624 var ii = [];
5625 for (var c = 0; c < clusters.length; c++) {
5626 if (clusters[c] === exemplars[ei]) {
5627 ii.push(c);
5628 }
5629 }
5630 var maxI = -1;
5631 var maxSum = -Infinity;
5632 for (var i = 0; i < ii.length; i++) {
5633 var sum = 0;
5634 for (var j = 0; j < ii.length; j++) {
5635 sum += S[ii[j] * n + ii[i]];
5636 }
5637 if (sum > maxSum) {
5638 maxI = i;
5639 maxSum = sum;
5640 }
5641 }
5642 exemplars[ei] = ii[maxI];
5643 }
5644 clusters = assignClusters(n, S, exemplars);
5645 return clusters;
5646 };
5647 var affinityPropagation = function affinityPropagation(options) {
5648 var cy = this.cy();
5649 var nodes = this.nodes();
5650 var opts = setOptions(options);
5651
5652 // Map each node to its position in node array
5653 var id2position = {};
5654 for (var i = 0; i < nodes.length; i++) {
5655 id2position[nodes[i].id()] = i;
5656 }
5657
5658 // Begin affinity propagation algorithm
5659
5660 var n; // number of data points
5661 var n2; // size of matrices
5662 var S; // similarity matrix (1D array)
5663 var p; // preference/suitability of a data point to serve as an exemplar
5664 var R; // responsibility matrix (1D array)
5665 var A; // availability matrix (1D array)
5666
5667 n = nodes.length;
5668 n2 = n * n;
5669
5670 // Initialize and build S similarity matrix
5671 S = new Array(n2);
5672 for (var _i = 0; _i < n2; _i++) {
5673 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5674 }
5675
5676 for (var _i2 = 0; _i2 < n; _i2++) {
5677 for (var j = 0; j < n; j++) {
5678 if (_i2 !== j) {
5679 S[_i2 * n + j] = getSimilarity(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5680 }
5681 }
5682 }
5683
5684 // Place preferences on the diagonal of S
5685 p = getPreference(S, opts.preference);
5686 for (var _i3 = 0; _i3 < n; _i3++) {
5687 S[_i3 * n + _i3] = p;
5688 }
5689
5690 // Initialize R responsibility matrix
5691 R = new Array(n2);
5692 for (var _i4 = 0; _i4 < n2; _i4++) {
5693 R[_i4] = 0.0;
5694 }
5695
5696 // Initialize A availability matrix
5697 A = new Array(n2);
5698 for (var _i5 = 0; _i5 < n2; _i5++) {
5699 A[_i5] = 0.0;
5700 }
5701 var old = new Array(n);
5702 var Rp = new Array(n);
5703 var se = new Array(n);
5704 for (var _i6 = 0; _i6 < n; _i6++) {
5705 old[_i6] = 0.0;
5706 Rp[_i6] = 0.0;
5707 se[_i6] = 0;
5708 }
5709 var e = new Array(n * opts.minIterations);
5710 for (var _i7 = 0; _i7 < e.length; _i7++) {
5711 e[_i7] = 0;
5712 }
5713 var iter;
5714 for (iter = 0; iter < opts.maxIterations; iter++) {
5715 // main algorithmic loop
5716
5717 // Update R responsibility matrix
5718 for (var _i8 = 0; _i8 < n; _i8++) {
5719 var max = -Infinity,
5720 max2 = -Infinity,
5721 maxI = -1,
5722 AS = 0.0;
5723 for (var _j = 0; _j < n; _j++) {
5724 old[_j] = R[_i8 * n + _j];
5725 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5726 if (AS >= max) {
5727 max2 = max;
5728 max = AS;
5729 maxI = _j;
5730 } else if (AS > max2) {
5731 max2 = AS;
5732 }
5733 }
5734 for (var _j2 = 0; _j2 < n; _j2++) {
5735 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5736 }
5737 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5738 }
5739
5740 // Update A availability matrix
5741 for (var _i9 = 0; _i9 < n; _i9++) {
5742 var sum = 0;
5743 for (var _j3 = 0; _j3 < n; _j3++) {
5744 old[_j3] = A[_j3 * n + _i9];
5745 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5746 sum += Rp[_j3];
5747 }
5748 sum -= Rp[_i9];
5749 Rp[_i9] = R[_i9 * n + _i9];
5750 sum += Rp[_i9];
5751 for (var _j4 = 0; _j4 < n; _j4++) {
5752 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5753 }
5754 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5755 }
5756
5757 // Check for convergence
5758 var K = 0;
5759 for (var _i10 = 0; _i10 < n; _i10++) {
5760 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5761 e[iter % opts.minIterations * n + _i10] = E;
5762 K += E;
5763 }
5764 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5765 var _sum = 0;
5766 for (var _i11 = 0; _i11 < n; _i11++) {
5767 se[_i11] = 0;
5768 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5769 se[_i11] += e[_j5 * n + _i11];
5770 }
5771 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5772 _sum++;
5773 }
5774 }
5775 if (_sum === n) {
5776 // then we have convergence
5777 break;
5778 }
5779 }
5780 }
5781
5782 // Identify exemplars (cluster centers)
5783 var exemplarsIndices = findExemplars(n, R, A);
5784
5785 // Assign nodes to clusters
5786 var clusterIndices = assign(n, S, exemplarsIndices);
5787 var clusters = {};
5788 for (var c = 0; c < exemplarsIndices.length; c++) {
5789 clusters[exemplarsIndices[c]] = [];
5790 }
5791 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5792 var pos = id2position[nodes[_i12].id()];
5793 var clusterIndex = clusterIndices[pos];
5794 if (clusterIndex != null) {
5795 // the node may have not been assigned a cluster if no valid attributes were specified
5796 clusters[clusterIndex].push(nodes[_i12]);
5797 }
5798 }
5799 var retClusters = new Array(exemplarsIndices.length);
5800 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5801 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5802 }
5803 return retClusters;
5804 };
5805 var affinityPropagation$1 = {
5806 affinityPropagation: affinityPropagation,
5807 ap: affinityPropagation
5808 };
5809
5810 var hierholzerDefaults = defaults$g({
5811 root: undefined,
5812 directed: false
5813 });
5814 var elesfn$k = {
5815 hierholzer: function hierholzer(options) {
5816 if (!plainObject(options)) {
5817 var args = arguments;
5818 options = {
5819 root: args[0],
5820 directed: args[1]
5821 };
5822 }
5823 var _hierholzerDefaults = hierholzerDefaults(options),
5824 root = _hierholzerDefaults.root,
5825 directed = _hierholzerDefaults.directed;
5826 var eles = this;
5827 var dflag = false;
5828 var oddIn;
5829 var oddOut;
5830 var startVertex;
5831 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5832 var nodes = {};
5833 var edges = {};
5834 if (directed) {
5835 eles.forEach(function (ele) {
5836 var id = ele.id();
5837 if (ele.isNode()) {
5838 var ind = ele.indegree(true);
5839 var outd = ele.outdegree(true);
5840 var d1 = ind - outd;
5841 var d2 = outd - ind;
5842 if (d1 == 1) {
5843 if (oddIn) dflag = true;else oddIn = id;
5844 } else if (d2 == 1) {
5845 if (oddOut) dflag = true;else oddOut = id;
5846 } else if (d2 > 1 || d1 > 1) {
5847 dflag = true;
5848 }
5849 nodes[id] = [];
5850 ele.outgoers().forEach(function (e) {
5851 if (e.isEdge()) nodes[id].push(e.id());
5852 });
5853 } else {
5854 edges[id] = [undefined, ele.target().id()];
5855 }
5856 });
5857 } else {
5858 eles.forEach(function (ele) {
5859 var id = ele.id();
5860 if (ele.isNode()) {
5861 var d = ele.degree(true);
5862 if (d % 2) {
5863 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5864 }
5865 nodes[id] = [];
5866 ele.connectedEdges().forEach(function (e) {
5867 return nodes[id].push(e.id());
5868 });
5869 } else {
5870 edges[id] = [ele.source().id(), ele.target().id()];
5871 }
5872 });
5873 }
5874 var result = {
5875 found: false,
5876 trail: undefined
5877 };
5878 if (dflag) return result;else if (oddOut && oddIn) {
5879 if (directed) {
5880 if (startVertex && oddOut != startVertex) {
5881 return result;
5882 }
5883 startVertex = oddOut;
5884 } else {
5885 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5886 return result;
5887 } else if (!startVertex) {
5888 startVertex = oddOut;
5889 }
5890 }
5891 } else {
5892 if (!startVertex) startVertex = eles[0].id();
5893 }
5894 var walk = function walk(v) {
5895 var currentNode = v;
5896 var subtour = [v];
5897 var adj, adjTail, adjHead;
5898 while (nodes[currentNode].length) {
5899 adj = nodes[currentNode].shift();
5900 adjTail = edges[adj][0];
5901 adjHead = edges[adj][1];
5902 if (currentNode != adjHead) {
5903 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5904 return e != adj;
5905 });
5906 currentNode = adjHead;
5907 } else if (!directed && currentNode != adjTail) {
5908 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5909 return e != adj;
5910 });
5911 currentNode = adjTail;
5912 }
5913 subtour.unshift(adj);
5914 subtour.unshift(currentNode);
5915 }
5916 return subtour;
5917 };
5918 var trail = [];
5919 var subtour = [];
5920 subtour = walk(startVertex);
5921 while (subtour.length != 1) {
5922 if (nodes[subtour[0]].length == 0) {
5923 trail.unshift(eles.getElementById(subtour.shift()));
5924 trail.unshift(eles.getElementById(subtour.shift()));
5925 } else {
5926 subtour = walk(subtour.shift()).concat(subtour);
5927 }
5928 }
5929 trail.unshift(eles.getElementById(subtour.shift())); // final node
5930
5931 for (var d in nodes) {
5932 if (nodes[d].length) {
5933 return result;
5934 }
5935 }
5936 result.found = true;
5937 result.trail = this.spawn(trail, true);
5938 return result;
5939 }
5940 };
5941
5942 var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5943 var eles = this;
5944 var nodes = {};
5945 var id = 0;
5946 var edgeCount = 0;
5947 var components = [];
5948 var stack = [];
5949 var visitedEdges = {};
5950 var buildComponent = function buildComponent(x, y) {
5951 var i = stack.length - 1;
5952 var cutset = [];
5953 var component = eles.spawn();
5954 while (stack[i].x != x || stack[i].y != y) {
5955 cutset.push(stack.pop().edge);
5956 i--;
5957 }
5958 cutset.push(stack.pop().edge);
5959 cutset.forEach(function (edge) {
5960 var connectedNodes = edge.connectedNodes().intersection(eles);
5961 component.merge(edge);
5962 connectedNodes.forEach(function (node) {
5963 var nodeId = node.id();
5964 var connectedEdges = node.connectedEdges().intersection(eles);
5965 component.merge(node);
5966 if (!nodes[nodeId].cutVertex) {
5967 component.merge(connectedEdges);
5968 } else {
5969 component.merge(connectedEdges.filter(function (edge) {
5970 return edge.isLoop();
5971 }));
5972 }
5973 });
5974 });
5975 components.push(component);
5976 };
5977 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5978 if (root === parent) edgeCount += 1;
5979 nodes[currentNode] = {
5980 id: id,
5981 low: id++,
5982 cutVertex: false
5983 };
5984 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5985 if (edges.size() === 0) {
5986 components.push(eles.spawn(eles.getElementById(currentNode)));
5987 } else {
5988 var sourceId, targetId, otherNodeId, edgeId;
5989 edges.forEach(function (edge) {
5990 sourceId = edge.source().id();
5991 targetId = edge.target().id();
5992 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5993 if (otherNodeId !== parent) {
5994 edgeId = edge.id();
5995 if (!visitedEdges[edgeId]) {
5996 visitedEdges[edgeId] = true;
5997 stack.push({
5998 x: currentNode,
5999 y: otherNodeId,
6000 edge: edge
6001 });
6002 }
6003 if (!(otherNodeId in nodes)) {
6004 biconnectedSearch(root, otherNodeId, currentNode);
6005 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
6006 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
6007 nodes[currentNode].cutVertex = true;
6008 buildComponent(currentNode, otherNodeId);
6009 }
6010 } else {
6011 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
6012 }
6013 }
6014 });
6015 }
6016 };
6017 eles.forEach(function (ele) {
6018 if (ele.isNode()) {
6019 var nodeId = ele.id();
6020 if (!(nodeId in nodes)) {
6021 edgeCount = 0;
6022 biconnectedSearch(nodeId, nodeId);
6023 nodes[nodeId].cutVertex = edgeCount > 1;
6024 }
6025 }
6026 });
6027 var cutVertices = Object.keys(nodes).filter(function (id) {
6028 return nodes[id].cutVertex;
6029 }).map(function (id) {
6030 return eles.getElementById(id);
6031 });
6032 return {
6033 cut: eles.spawn(cutVertices),
6034 components: components
6035 };
6036 };
6037 var hopcroftTarjanBiconnected$1 = {
6038 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
6039 htbc: hopcroftTarjanBiconnected,
6040 htb: hopcroftTarjanBiconnected,
6041 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
6042 };
6043
6044 var tarjanStronglyConnected = function tarjanStronglyConnected() {
6045 var eles = this;
6046 var nodes = {};
6047 var index = 0;
6048 var components = [];
6049 var stack = [];
6050 var cut = eles.spawn(eles);
6051 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
6052 stack.push(sourceNodeId);
6053 nodes[sourceNodeId] = {
6054 index: index,
6055 low: index++,
6056 explored: false
6057 };
6058 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
6059 connectedEdges.forEach(function (edge) {
6060 var targetNodeId = edge.target().id();
6061 if (targetNodeId !== sourceNodeId) {
6062 if (!(targetNodeId in nodes)) {
6063 stronglyConnectedSearch(targetNodeId);
6064 }
6065 if (!nodes[targetNodeId].explored) {
6066 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
6067 }
6068 }
6069 });
6070 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
6071 var componentNodes = eles.spawn();
6072 for (;;) {
6073 var nodeId = stack.pop();
6074 componentNodes.merge(eles.getElementById(nodeId));
6075 nodes[nodeId].low = nodes[sourceNodeId].index;
6076 nodes[nodeId].explored = true;
6077 if (nodeId === sourceNodeId) {
6078 break;
6079 }
6080 }
6081 var componentEdges = componentNodes.edgesWith(componentNodes);
6082 var component = componentNodes.merge(componentEdges);
6083 components.push(component);
6084 cut = cut.difference(component);
6085 }
6086 };
6087 eles.forEach(function (ele) {
6088 if (ele.isNode()) {
6089 var nodeId = ele.id();
6090 if (!(nodeId in nodes)) {
6091 stronglyConnectedSearch(nodeId);
6092 }
6093 }
6094 });
6095 return {
6096 cut: cut,
6097 components: components
6098 };
6099 };
6100 var tarjanStronglyConnected$1 = {
6101 tarjanStronglyConnected: tarjanStronglyConnected,
6102 tsc: tarjanStronglyConnected,
6103 tscc: tarjanStronglyConnected,
6104 tarjanStronglyConnectedComponents: tarjanStronglyConnected
6105 };
6106
6107 var elesfn$j = {};
6108 [elesfn$v, elesfn$u, elesfn$t, elesfn$s, elesfn$r, elesfn$q, elesfn$p, elesfn$o, elesfn$n, elesfn$m, elesfn$l, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$k, hopcroftTarjanBiconnected$1, tarjanStronglyConnected$1].forEach(function (props) {
6109 extend(elesfn$j, props);
6110 });
6111
6112 /*!
6113 Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
6114 Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
6115 Licensed under The MIT License (http://opensource.org/licenses/MIT)
6116 */
6117
6118 /* promise states [Promises/A+ 2.1] */
6119 var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
6120 var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
6121 var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
6122
6123 /* promise object constructor */
6124 var api = function api(executor) {
6125 /* optionally support non-constructor/plain-function call */
6126 if (!(this instanceof api)) return new api(executor);
6127
6128 /* initialize object */
6129 this.id = 'Thenable/1.0.7';
6130 this.state = STATE_PENDING; /* initial state */
6131 this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
6132 this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
6133 this.onFulfilled = []; /* initial handlers */
6134 this.onRejected = []; /* initial handlers */
6135
6136 /* provide optional information-hiding proxy */
6137 this.proxy = {
6138 then: this.then.bind(this)
6139 };
6140
6141 /* support optional executor function */
6142 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
6143 };
6144
6145 /* promise API methods */
6146 api.prototype = {
6147 /* promise resolving methods */
6148 fulfill: function fulfill(value) {
6149 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
6150 },
6151 reject: function reject(value) {
6152 return deliver(this, STATE_REJECTED, 'rejectReason', value);
6153 },
6154 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
6155 then: function then(onFulfilled, onRejected) {
6156 var curr = this;
6157 var next = new api(); /* [Promises/A+ 2.2.7] */
6158 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill')); /* [Promises/A+ 2.2.2/2.2.6] */
6159 curr.onRejected.push(resolver(onRejected, next, 'reject')); /* [Promises/A+ 2.2.3/2.2.6] */
6160 execute(curr);
6161 return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */
6162 }
6163 };
6164
6165 /* deliver an action */
6166 var deliver = function deliver(curr, state, name, value) {
6167 if (curr.state === STATE_PENDING) {
6168 curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
6169 curr[name] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
6170 execute(curr);
6171 }
6172 return curr;
6173 };
6174
6175 /* execute all handlers */
6176 var execute = function execute(curr) {
6177 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
6178 };
6179
6180 /* execute particular set of handlers */
6181 var execute_handlers = function execute_handlers(curr, name, value) {
6182 /* global setImmediate: true */
6183 /* global setTimeout: true */
6184
6185 /* short-circuit processing */
6186 if (curr[name].length === 0) return;
6187
6188 /* iterate over all handlers, exactly once */
6189 var handlers = curr[name];
6190 curr[name] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
6191 var func = function func() {
6192 for (var i = 0; i < handlers.length; i++) {
6193 handlers[i](value);
6194 } /* [Promises/A+ 2.2.5] */
6195 };
6196
6197 /* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
6198 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
6199 };
6200
6201 /* generate a resolver function */
6202 var resolver = function resolver(cb, next, method) {
6203 return function (value) {
6204 if (typeof cb !== 'function') /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
6205 next[method].call(next, value); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */else {
6206 var result;
6207 try {
6208 result = cb(value);
6209 } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */ catch (e) {
6210 next.reject(e); /* [Promises/A+ 2.2.7.2] */
6211 return;
6212 }
6213 resolve(next, result); /* [Promises/A+ 2.2.7.1] */
6214 }
6215 };
6216 };
6217
6218 /* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */
6219 var resolve = function resolve(promise, x) {
6220 /* sanity check arguments */ /* [Promises/A+ 2.3.1] */
6221 if (promise === x || promise.proxy === x) {
6222 promise.reject(new TypeError('cannot resolve promise with itself'));
6223 return;
6224 }
6225
6226 /* surgically check for a "then" method
6227 (mainly to just call the "getter" of "then" only once) */
6228 var then;
6229 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
6230 try {
6231 then = x.then;
6232 } /* [Promises/A+ 2.3.3.1, 3.5] */ catch (e) {
6233 promise.reject(e); /* [Promises/A+ 2.3.3.2] */
6234 return;
6235 }
6236 }
6237
6238 /* handle own Thenables [Promises/A+ 2.3.2]
6239 and similar "thenables" [Promises/A+ 2.3.3] */
6240 if (typeof then === 'function') {
6241 var resolved = false;
6242 try {
6243 /* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */
6244 then.call(x, /* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
6245 function (y) {
6246 if (resolved) return;
6247 resolved = true; /* [Promises/A+ 2.3.3.3.3] */
6248 if (y === x) /* [Promises/A+ 3.6] */
6249 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
6250 }, /* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
6251 function (r) {
6252 if (resolved) return;
6253 resolved = true; /* [Promises/A+ 2.3.3.3.3] */
6254 promise.reject(r);
6255 });
6256 } catch (e) {
6257 if (!resolved) /* [Promises/A+ 2.3.3.3.3] */
6258 promise.reject(e); /* [Promises/A+ 2.3.3.3.4] */
6259 }
6260
6261 return;
6262 }
6263
6264 /* handle other values */
6265 promise.fulfill(x); /* [Promises/A+ 2.3.4, 2.3.3.4] */
6266 };
6267
6268 // so we always have Promise.all()
6269 api.all = function (ps) {
6270 return new api(function (resolveAll, rejectAll) {
6271 var vals = new Array(ps.length);
6272 var doneCount = 0;
6273 var fulfill = function fulfill(i, val) {
6274 vals[i] = val;
6275 doneCount++;
6276 if (doneCount === ps.length) {
6277 resolveAll(vals);
6278 }
6279 };
6280 for (var i = 0; i < ps.length; i++) {
6281 (function (i) {
6282 var p = ps[i];
6283 var isPromise = p != null && p.then != null;
6284 if (isPromise) {
6285 p.then(function (val) {
6286 fulfill(i, val);
6287 }, function (err) {
6288 rejectAll(err);
6289 });
6290 } else {
6291 var val = p;
6292 fulfill(i, val);
6293 }
6294 })(i);
6295 }
6296 });
6297 };
6298 api.resolve = function (val) {
6299 return new api(function (resolve, reject) {
6300 resolve(val);
6301 });
6302 };
6303 api.reject = function (val) {
6304 return new api(function (resolve, reject) {
6305 reject(val);
6306 });
6307 };
6308 var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6309
6310 var Animation = function Animation(target, opts, opts2) {
6311 var isCore = core(target);
6312 var isEle = !isCore;
6313 var _p = this._private = extend({
6314 duration: 1000
6315 }, opts, opts2);
6316 _p.target = target;
6317 _p.style = _p.style || _p.css;
6318 _p.started = false;
6319 _p.playing = false;
6320 _p.hooked = false;
6321 _p.applying = false;
6322 _p.progress = 0;
6323 _p.completes = [];
6324 _p.frames = [];
6325 if (_p.complete && fn$6(_p.complete)) {
6326 _p.completes.push(_p.complete);
6327 }
6328 if (isEle) {
6329 var pos = target.position();
6330 _p.startPosition = _p.startPosition || {
6331 x: pos.x,
6332 y: pos.y
6333 };
6334 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6335 }
6336 if (isCore) {
6337 var pan = target.pan();
6338 _p.startPan = {
6339 x: pan.x,
6340 y: pan.y
6341 };
6342 _p.startZoom = target.zoom();
6343 }
6344
6345 // for future timeline/animations impl
6346 this.length = 1;
6347 this[0] = this;
6348 };
6349 var anifn = Animation.prototype;
6350 extend(anifn, {
6351 instanceString: function instanceString() {
6352 return 'animation';
6353 },
6354 hook: function hook() {
6355 var _p = this._private;
6356 if (!_p.hooked) {
6357 // add to target's animation queue
6358 var q;
6359 var tAni = _p.target._private.animation;
6360 if (_p.queue) {
6361 q = tAni.queue;
6362 } else {
6363 q = tAni.current;
6364 }
6365 q.push(this);
6366
6367 // add to the animation loop pool
6368 if (elementOrCollection(_p.target)) {
6369 _p.target.cy().addToAnimationPool(_p.target);
6370 }
6371 _p.hooked = true;
6372 }
6373 return this;
6374 },
6375 play: function play() {
6376 var _p = this._private;
6377
6378 // autorewind
6379 if (_p.progress === 1) {
6380 _p.progress = 0;
6381 }
6382 _p.playing = true;
6383 _p.started = false; // needs to be started by animation loop
6384 _p.stopped = false;
6385 this.hook();
6386
6387 // the animation loop will start the animation...
6388
6389 return this;
6390 },
6391 playing: function playing() {
6392 return this._private.playing;
6393 },
6394 apply: function apply() {
6395 var _p = this._private;
6396 _p.applying = true;
6397 _p.started = false; // needs to be started by animation loop
6398 _p.stopped = false;
6399 this.hook();
6400
6401 // the animation loop will apply the animation at this progress
6402
6403 return this;
6404 },
6405 applying: function applying() {
6406 return this._private.applying;
6407 },
6408 pause: function pause() {
6409 var _p = this._private;
6410 _p.playing = false;
6411 _p.started = false;
6412 return this;
6413 },
6414 stop: function stop() {
6415 var _p = this._private;
6416 _p.playing = false;
6417 _p.started = false;
6418 _p.stopped = true; // to be removed from animation queues
6419
6420 return this;
6421 },
6422 rewind: function rewind() {
6423 return this.progress(0);
6424 },
6425 fastforward: function fastforward() {
6426 return this.progress(1);
6427 },
6428 time: function time(t) {
6429 var _p = this._private;
6430 if (t === undefined) {
6431 return _p.progress * _p.duration;
6432 } else {
6433 return this.progress(t / _p.duration);
6434 }
6435 },
6436 progress: function progress(p) {
6437 var _p = this._private;
6438 var wasPlaying = _p.playing;
6439 if (p === undefined) {
6440 return _p.progress;
6441 } else {
6442 if (wasPlaying) {
6443 this.pause();
6444 }
6445 _p.progress = p;
6446 _p.started = false;
6447 if (wasPlaying) {
6448 this.play();
6449 }
6450 }
6451 return this;
6452 },
6453 completed: function completed() {
6454 return this._private.progress === 1;
6455 },
6456 reverse: function reverse() {
6457 var _p = this._private;
6458 var wasPlaying = _p.playing;
6459 if (wasPlaying) {
6460 this.pause();
6461 }
6462 _p.progress = 1 - _p.progress;
6463 _p.started = false;
6464 var swap = function swap(a, b) {
6465 var _pa = _p[a];
6466 if (_pa == null) {
6467 return;
6468 }
6469 _p[a] = _p[b];
6470 _p[b] = _pa;
6471 };
6472 swap('zoom', 'startZoom');
6473 swap('pan', 'startPan');
6474 swap('position', 'startPosition');
6475
6476 // swap styles
6477 if (_p.style) {
6478 for (var i = 0; i < _p.style.length; i++) {
6479 var prop = _p.style[i];
6480 var name = prop.name;
6481 var startStyleProp = _p.startStyle[name];
6482 _p.startStyle[name] = prop;
6483 _p.style[i] = startStyleProp;
6484 }
6485 }
6486 if (wasPlaying) {
6487 this.play();
6488 }
6489 return this;
6490 },
6491 promise: function promise(type) {
6492 var _p = this._private;
6493 var arr;
6494 switch (type) {
6495 case 'frame':
6496 arr = _p.frames;
6497 break;
6498 default:
6499 case 'complete':
6500 case 'completed':
6501 arr = _p.completes;
6502 }
6503 return new Promise$1(function (resolve, reject) {
6504 arr.push(function () {
6505 resolve();
6506 });
6507 });
6508 }
6509 });
6510 anifn.complete = anifn.completed;
6511 anifn.run = anifn.play;
6512 anifn.running = anifn.playing;
6513
6514 var define$3 = {
6515 animated: function animated() {
6516 return function animatedImpl() {
6517 var self = this;
6518 var selfIsArrayLike = self.length !== undefined;
6519 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6520 var cy = this._private.cy || this;
6521 if (!cy.styleEnabled()) {
6522 return false;
6523 }
6524 var ele = all[0];
6525 if (ele) {
6526 return ele._private.animation.current.length > 0;
6527 }
6528 };
6529 },
6530 // animated
6531
6532 clearQueue: function clearQueue() {
6533 return function clearQueueImpl() {
6534 var self = this;
6535 var selfIsArrayLike = self.length !== undefined;
6536 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6537 var cy = this._private.cy || this;
6538 if (!cy.styleEnabled()) {
6539 return this;
6540 }
6541 for (var i = 0; i < all.length; i++) {
6542 var ele = all[i];
6543 ele._private.animation.queue = [];
6544 }
6545 return this;
6546 };
6547 },
6548 // clearQueue
6549
6550 delay: function delay() {
6551 return function delayImpl(time, complete) {
6552 var cy = this._private.cy || this;
6553 if (!cy.styleEnabled()) {
6554 return this;
6555 }
6556 return this.animate({
6557 delay: time,
6558 duration: time,
6559 complete: complete
6560 });
6561 };
6562 },
6563 // delay
6564
6565 delayAnimation: function delayAnimation() {
6566 return function delayAnimationImpl(time, complete) {
6567 var cy = this._private.cy || this;
6568 if (!cy.styleEnabled()) {
6569 return this;
6570 }
6571 return this.animation({
6572 delay: time,
6573 duration: time,
6574 complete: complete
6575 });
6576 };
6577 },
6578 // delay
6579
6580 animation: function animation() {
6581 return function animationImpl(properties, params) {
6582 var self = this;
6583 var selfIsArrayLike = self.length !== undefined;
6584 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6585 var cy = this._private.cy || this;
6586 var isCore = !selfIsArrayLike;
6587 var isEles = !isCore;
6588 if (!cy.styleEnabled()) {
6589 return this;
6590 }
6591 var style = cy.style();
6592 properties = extend({}, properties, params);
6593 var propertiesEmpty = Object.keys(properties).length === 0;
6594 if (propertiesEmpty) {
6595 return new Animation(all[0], properties); // nothing to animate
6596 }
6597
6598 if (properties.duration === undefined) {
6599 properties.duration = 400;
6600 }
6601 switch (properties.duration) {
6602 case 'slow':
6603 properties.duration = 600;
6604 break;
6605 case 'fast':
6606 properties.duration = 200;
6607 break;
6608 }
6609 if (isEles) {
6610 properties.style = style.getPropsList(properties.style || properties.css);
6611 properties.css = undefined;
6612 }
6613 if (isEles && properties.renderedPosition != null) {
6614 var rpos = properties.renderedPosition;
6615 var pan = cy.pan();
6616 var zoom = cy.zoom();
6617 properties.position = renderedToModelPosition(rpos, zoom, pan);
6618 }
6619
6620 // override pan w/ panBy if set
6621 if (isCore && properties.panBy != null) {
6622 var panBy = properties.panBy;
6623 var cyPan = cy.pan();
6624 properties.pan = {
6625 x: cyPan.x + panBy.x,
6626 y: cyPan.y + panBy.y
6627 };
6628 }
6629
6630 // override pan w/ center if set
6631 var center = properties.center || properties.centre;
6632 if (isCore && center != null) {
6633 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6634 if (centerPan != null) {
6635 properties.pan = centerPan;
6636 }
6637 }
6638
6639 // override pan & zoom w/ fit if set
6640 if (isCore && properties.fit != null) {
6641 var fit = properties.fit;
6642 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6643 if (fitVp != null) {
6644 properties.pan = fitVp.pan;
6645 properties.zoom = fitVp.zoom;
6646 }
6647 }
6648
6649 // override zoom (& potentially pan) w/ zoom obj if set
6650 if (isCore && plainObject(properties.zoom)) {
6651 var vp = cy.getZoomedViewport(properties.zoom);
6652 if (vp != null) {
6653 if (vp.zoomed) {
6654 properties.zoom = vp.zoom;
6655 }
6656 if (vp.panned) {
6657 properties.pan = vp.pan;
6658 }
6659 } else {
6660 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6661 }
6662 }
6663
6664 return new Animation(all[0], properties);
6665 };
6666 },
6667 // animate
6668
6669 animate: function animate() {
6670 return function animateImpl(properties, params) {
6671 var self = this;
6672 var selfIsArrayLike = self.length !== undefined;
6673 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6674 var cy = this._private.cy || this;
6675 if (!cy.styleEnabled()) {
6676 return this;
6677 }
6678 if (params) {
6679 properties = extend({}, properties, params);
6680 }
6681
6682 // manually hook and run the animation
6683 for (var i = 0; i < all.length; i++) {
6684 var ele = all[i];
6685 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6686 var ani = ele.animation(properties, queue ? {
6687 queue: true
6688 } : undefined);
6689 ani.play();
6690 }
6691 return this; // chaining
6692 };
6693 },
6694
6695 // animate
6696
6697 stop: function stop() {
6698 return function stopImpl(clearQueue, jumpToEnd) {
6699 var self = this;
6700 var selfIsArrayLike = self.length !== undefined;
6701 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6702 var cy = this._private.cy || this;
6703 if (!cy.styleEnabled()) {
6704 return this;
6705 }
6706 for (var i = 0; i < all.length; i++) {
6707 var ele = all[i];
6708 var _p = ele._private;
6709 var anis = _p.animation.current;
6710 for (var j = 0; j < anis.length; j++) {
6711 var ani = anis[j];
6712 var ani_p = ani._private;
6713 if (jumpToEnd) {
6714 // next iteration of the animation loop, the animation
6715 // will go straight to the end and be removed
6716 ani_p.duration = 0;
6717 }
6718 }
6719
6720 // clear the queue of future animations
6721 if (clearQueue) {
6722 _p.animation.queue = [];
6723 }
6724 if (!jumpToEnd) {
6725 _p.animation.current = [];
6726 }
6727 }
6728
6729 // we have to notify (the animation loop doesn't do it for us on `stop`)
6730 cy.notify('draw');
6731 return this;
6732 };
6733 } // stop
6734 }; // define
6735
6736 /**
6737 * Checks if `value` is classified as an `Array` object.
6738 *
6739 * @static
6740 * @memberOf _
6741 * @since 0.1.0
6742 * @category Lang
6743 * @param {*} value The value to check.
6744 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
6745 * @example
6746 *
6747 * _.isArray([1, 2, 3]);
6748 * // => true
6749 *
6750 * _.isArray(document.body.children);
6751 * // => false
6752 *
6753 * _.isArray('abc');
6754 * // => false
6755 *
6756 * _.isArray(_.noop);
6757 * // => false
6758 */
6759 var isArray = Array.isArray;
6760
6761 var isArray_1 = isArray;
6762
6763 /** Used to match property names within property paths. */
6764 var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
6765 reIsPlainProp = /^\w*$/;
6766
6767 /**
6768 * Checks if `value` is a property name and not a property path.
6769 *
6770 * @private
6771 * @param {*} value The value to check.
6772 * @param {Object} [object] The object to query keys on.
6773 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
6774 */
6775 function isKey(value, object) {
6776 if (isArray_1(value)) {
6777 return false;
6778 }
6779 var type = typeof value;
6780 if (type == 'number' || type == 'symbol' || type == 'boolean' ||
6781 value == null || isSymbol_1(value)) {
6782 return true;
6783 }
6784 return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
6785 (object != null && value in Object(object));
6786 }
6787
6788 var _isKey = isKey;
6789
6790 /** `Object#toString` result references. */
6791 var asyncTag = '[object AsyncFunction]',
6792 funcTag = '[object Function]',
6793 genTag = '[object GeneratorFunction]',
6794 proxyTag = '[object Proxy]';
6795
6796 /**
6797 * Checks if `value` is classified as a `Function` object.
6798 *
6799 * @static
6800 * @memberOf _
6801 * @since 0.1.0
6802 * @category Lang
6803 * @param {*} value The value to check.
6804 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
6805 * @example
6806 *
6807 * _.isFunction(_);
6808 * // => true
6809 *
6810 * _.isFunction(/abc/);
6811 * // => false
6812 */
6813 function isFunction(value) {
6814 if (!isObject_1(value)) {
6815 return false;
6816 }
6817 // The use of `Object#toString` avoids issues with the `typeof` operator
6818 // in Safari 9 which returns 'object' for typed arrays and other constructors.
6819 var tag = _baseGetTag(value);
6820 return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
6821 }
6822
6823 var isFunction_1 = isFunction;
6824
6825 /** Used to detect overreaching core-js shims. */
6826 var coreJsData = _root['__core-js_shared__'];
6827
6828 var _coreJsData = coreJsData;
6829
6830 /** Used to detect methods masquerading as native. */
6831 var maskSrcKey = (function() {
6832 var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
6833 return uid ? ('Symbol(src)_1.' + uid) : '';
6834 }());
6835
6836 /**
6837 * Checks if `func` has its source masked.
6838 *
6839 * @private
6840 * @param {Function} func The function to check.
6841 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
6842 */
6843 function isMasked(func) {
6844 return !!maskSrcKey && (maskSrcKey in func);
6845 }
6846
6847 var _isMasked = isMasked;
6848
6849 /** Used for built-in method references. */
6850 var funcProto$1 = Function.prototype;
6851
6852 /** Used to resolve the decompiled source of functions. */
6853 var funcToString$1 = funcProto$1.toString;
6854
6855 /**
6856 * Converts `func` to its source code.
6857 *
6858 * @private
6859 * @param {Function} func The function to convert.
6860 * @returns {string} Returns the source code.
6861 */
6862 function toSource(func) {
6863 if (func != null) {
6864 try {
6865 return funcToString$1.call(func);
6866 } catch (e) {}
6867 try {
6868 return (func + '');
6869 } catch (e) {}
6870 }
6871 return '';
6872 }
6873
6874 var _toSource = toSource;
6875
6876 /**
6877 * Used to match `RegExp`
6878 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
6879 */
6880 var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
6881
6882 /** Used to detect host constructors (Safari). */
6883 var reIsHostCtor = /^\[object .+?Constructor\]$/;
6884
6885 /** Used for built-in method references. */
6886 var funcProto = Function.prototype,
6887 objectProto$3 = Object.prototype;
6888
6889 /** Used to resolve the decompiled source of functions. */
6890 var funcToString = funcProto.toString;
6891
6892 /** Used to check objects for own properties. */
6893 var hasOwnProperty$3 = objectProto$3.hasOwnProperty;
6894
6895 /** Used to detect if a method is native. */
6896 var reIsNative = RegExp('^' +
6897 funcToString.call(hasOwnProperty$3).replace(reRegExpChar, '\\$&')
6898 .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
6899 );
6900
6901 /**
6902 * The base implementation of `_.isNative` without bad shim checks.
6903 *
6904 * @private
6905 * @param {*} value The value to check.
6906 * @returns {boolean} Returns `true` if `value` is a native function,
6907 * else `false`.
6908 */
6909 function baseIsNative(value) {
6910 if (!isObject_1(value) || _isMasked(value)) {
6911 return false;
6912 }
6913 var pattern = isFunction_1(value) ? reIsNative : reIsHostCtor;
6914 return pattern.test(_toSource(value));
6915 }
6916
6917 var _baseIsNative = baseIsNative;
6918
6919 /**
6920 * Gets the value at `key` of `object`.
6921 *
6922 * @private
6923 * @param {Object} [object] The object to query.
6924 * @param {string} key The key of the property to get.
6925 * @returns {*} Returns the property value.
6926 */
6927 function getValue$1(object, key) {
6928 return object == null ? undefined : object[key];
6929 }
6930
6931 var _getValue = getValue$1;
6932
6933 /**
6934 * Gets the native function at `key` of `object`.
6935 *
6936 * @private
6937 * @param {Object} object The object to query.
6938 * @param {string} key The key of the method to get.
6939 * @returns {*} Returns the function if it's native, else `undefined`.
6940 */
6941 function getNative(object, key) {
6942 var value = _getValue(object, key);
6943 return _baseIsNative(value) ? value : undefined;
6944 }
6945
6946 var _getNative = getNative;
6947
6948 /* Built-in method references that are verified to be native. */
6949 var nativeCreate = _getNative(Object, 'create');
6950
6951 var _nativeCreate = nativeCreate;
6952
6953 /**
6954 * Removes all key-value entries from the hash.
6955 *
6956 * @private
6957 * @name clear
6958 * @memberOf Hash
6959 */
6960 function hashClear() {
6961 this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
6962 this.size = 0;
6963 }
6964
6965 var _hashClear = hashClear;
6966
6967 /**
6968 * Removes `key` and its value from the hash.
6969 *
6970 * @private
6971 * @name delete
6972 * @memberOf Hash
6973 * @param {Object} hash The hash to modify.
6974 * @param {string} key The key of the value to remove.
6975 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
6976 */
6977 function hashDelete(key) {
6978 var result = this.has(key) && delete this.__data__[key];
6979 this.size -= result ? 1 : 0;
6980 return result;
6981 }
6982
6983 var _hashDelete = hashDelete;
6984
6985 /** Used to stand-in for `undefined` hash values. */
6986 var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';
6987
6988 /** Used for built-in method references. */
6989 var objectProto$2 = Object.prototype;
6990
6991 /** Used to check objects for own properties. */
6992 var hasOwnProperty$2 = objectProto$2.hasOwnProperty;
6993
6994 /**
6995 * Gets the hash value for `key`.
6996 *
6997 * @private
6998 * @name get
6999 * @memberOf Hash
7000 * @param {string} key The key of the value to get.
7001 * @returns {*} Returns the entry value.
7002 */
7003 function hashGet(key) {
7004 var data = this.__data__;
7005 if (_nativeCreate) {
7006 var result = data[key];
7007 return result === HASH_UNDEFINED$1 ? undefined : result;
7008 }
7009 return hasOwnProperty$2.call(data, key) ? data[key] : undefined;
7010 }
7011
7012 var _hashGet = hashGet;
7013
7014 /** Used for built-in method references. */
7015 var objectProto$1 = Object.prototype;
7016
7017 /** Used to check objects for own properties. */
7018 var hasOwnProperty$1 = objectProto$1.hasOwnProperty;
7019
7020 /**
7021 * Checks if a hash value for `key` exists.
7022 *
7023 * @private
7024 * @name has
7025 * @memberOf Hash
7026 * @param {string} key The key of the entry to check.
7027 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7028 */
7029 function hashHas(key) {
7030 var data = this.__data__;
7031 return _nativeCreate ? (data[key] !== undefined) : hasOwnProperty$1.call(data, key);
7032 }
7033
7034 var _hashHas = hashHas;
7035
7036 /** Used to stand-in for `undefined` hash values. */
7037 var HASH_UNDEFINED = '__lodash_hash_undefined__';
7038
7039 /**
7040 * Sets the hash `key` to `value`.
7041 *
7042 * @private
7043 * @name set
7044 * @memberOf Hash
7045 * @param {string} key The key of the value to set.
7046 * @param {*} value The value to set.
7047 * @returns {Object} Returns the hash instance.
7048 */
7049 function hashSet(key, value) {
7050 var data = this.__data__;
7051 this.size += this.has(key) ? 0 : 1;
7052 data[key] = (_nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
7053 return this;
7054 }
7055
7056 var _hashSet = hashSet;
7057
7058 /**
7059 * Creates a hash object.
7060 *
7061 * @private
7062 * @constructor
7063 * @param {Array} [entries] The key-value pairs to cache.
7064 */
7065 function Hash(entries) {
7066 var index = -1,
7067 length = entries == null ? 0 : entries.length;
7068
7069 this.clear();
7070 while (++index < length) {
7071 var entry = entries[index];
7072 this.set(entry[0], entry[1]);
7073 }
7074 }
7075
7076 // Add methods to `Hash`.
7077 Hash.prototype.clear = _hashClear;
7078 Hash.prototype['delete'] = _hashDelete;
7079 Hash.prototype.get = _hashGet;
7080 Hash.prototype.has = _hashHas;
7081 Hash.prototype.set = _hashSet;
7082
7083 var _Hash = Hash;
7084
7085 /**
7086 * Removes all key-value entries from the list cache.
7087 *
7088 * @private
7089 * @name clear
7090 * @memberOf ListCache
7091 */
7092 function listCacheClear() {
7093 this.__data__ = [];
7094 this.size = 0;
7095 }
7096
7097 var _listCacheClear = listCacheClear;
7098
7099 /**
7100 * Performs a
7101 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7102 * comparison between two values to determine if they are equivalent.
7103 *
7104 * @static
7105 * @memberOf _
7106 * @since 4.0.0
7107 * @category Lang
7108 * @param {*} value The value to compare.
7109 * @param {*} other The other value to compare.
7110 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
7111 * @example
7112 *
7113 * var object = { 'a': 1 };
7114 * var other = { 'a': 1 };
7115 *
7116 * _.eq(object, object);
7117 * // => true
7118 *
7119 * _.eq(object, other);
7120 * // => false
7121 *
7122 * _.eq('a', 'a');
7123 * // => true
7124 *
7125 * _.eq('a', Object('a'));
7126 * // => false
7127 *
7128 * _.eq(NaN, NaN);
7129 * // => true
7130 */
7131 function eq(value, other) {
7132 return value === other || (value !== value && other !== other);
7133 }
7134
7135 var eq_1 = eq;
7136
7137 /**
7138 * Gets the index at which the `key` is found in `array` of key-value pairs.
7139 *
7140 * @private
7141 * @param {Array} array The array to inspect.
7142 * @param {*} key The key to search for.
7143 * @returns {number} Returns the index of the matched value, else `-1`.
7144 */
7145 function assocIndexOf(array, key) {
7146 var length = array.length;
7147 while (length--) {
7148 if (eq_1(array[length][0], key)) {
7149 return length;
7150 }
7151 }
7152 return -1;
7153 }
7154
7155 var _assocIndexOf = assocIndexOf;
7156
7157 /** Used for built-in method references. */
7158 var arrayProto = Array.prototype;
7159
7160 /** Built-in value references. */
7161 var splice = arrayProto.splice;
7162
7163 /**
7164 * Removes `key` and its value from the list cache.
7165 *
7166 * @private
7167 * @name delete
7168 * @memberOf ListCache
7169 * @param {string} key The key of the value to remove.
7170 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7171 */
7172 function listCacheDelete(key) {
7173 var data = this.__data__,
7174 index = _assocIndexOf(data, key);
7175
7176 if (index < 0) {
7177 return false;
7178 }
7179 var lastIndex = data.length - 1;
7180 if (index == lastIndex) {
7181 data.pop();
7182 } else {
7183 splice.call(data, index, 1);
7184 }
7185 --this.size;
7186 return true;
7187 }
7188
7189 var _listCacheDelete = listCacheDelete;
7190
7191 /**
7192 * Gets the list cache value for `key`.
7193 *
7194 * @private
7195 * @name get
7196 * @memberOf ListCache
7197 * @param {string} key The key of the value to get.
7198 * @returns {*} Returns the entry value.
7199 */
7200 function listCacheGet(key) {
7201 var data = this.__data__,
7202 index = _assocIndexOf(data, key);
7203
7204 return index < 0 ? undefined : data[index][1];
7205 }
7206
7207 var _listCacheGet = listCacheGet;
7208
7209 /**
7210 * Checks if a list cache value for `key` exists.
7211 *
7212 * @private
7213 * @name has
7214 * @memberOf ListCache
7215 * @param {string} key The key of the entry to check.
7216 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7217 */
7218 function listCacheHas(key) {
7219 return _assocIndexOf(this.__data__, key) > -1;
7220 }
7221
7222 var _listCacheHas = listCacheHas;
7223
7224 /**
7225 * Sets the list cache `key` to `value`.
7226 *
7227 * @private
7228 * @name set
7229 * @memberOf ListCache
7230 * @param {string} key The key of the value to set.
7231 * @param {*} value The value to set.
7232 * @returns {Object} Returns the list cache instance.
7233 */
7234 function listCacheSet(key, value) {
7235 var data = this.__data__,
7236 index = _assocIndexOf(data, key);
7237
7238 if (index < 0) {
7239 ++this.size;
7240 data.push([key, value]);
7241 } else {
7242 data[index][1] = value;
7243 }
7244 return this;
7245 }
7246
7247 var _listCacheSet = listCacheSet;
7248
7249 /**
7250 * Creates an list cache object.
7251 *
7252 * @private
7253 * @constructor
7254 * @param {Array} [entries] The key-value pairs to cache.
7255 */
7256 function ListCache(entries) {
7257 var index = -1,
7258 length = entries == null ? 0 : entries.length;
7259
7260 this.clear();
7261 while (++index < length) {
7262 var entry = entries[index];
7263 this.set(entry[0], entry[1]);
7264 }
7265 }
7266
7267 // Add methods to `ListCache`.
7268 ListCache.prototype.clear = _listCacheClear;
7269 ListCache.prototype['delete'] = _listCacheDelete;
7270 ListCache.prototype.get = _listCacheGet;
7271 ListCache.prototype.has = _listCacheHas;
7272 ListCache.prototype.set = _listCacheSet;
7273
7274 var _ListCache = ListCache;
7275
7276 /* Built-in method references that are verified to be native. */
7277 var Map$1 = _getNative(_root, 'Map');
7278
7279 var _Map = Map$1;
7280
7281 /**
7282 * Removes all key-value entries from the map.
7283 *
7284 * @private
7285 * @name clear
7286 * @memberOf MapCache
7287 */
7288 function mapCacheClear() {
7289 this.size = 0;
7290 this.__data__ = {
7291 'hash': new _Hash,
7292 'map': new (_Map || _ListCache),
7293 'string': new _Hash
7294 };
7295 }
7296
7297 var _mapCacheClear = mapCacheClear;
7298
7299 /**
7300 * Checks if `value` is suitable for use as unique object key.
7301 *
7302 * @private
7303 * @param {*} value The value to check.
7304 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
7305 */
7306 function isKeyable(value) {
7307 var type = typeof value;
7308 return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
7309 ? (value !== '__proto__')
7310 : (value === null);
7311 }
7312
7313 var _isKeyable = isKeyable;
7314
7315 /**
7316 * Gets the data for `map`.
7317 *
7318 * @private
7319 * @param {Object} map The map to query.
7320 * @param {string} key The reference key.
7321 * @returns {*} Returns the map data.
7322 */
7323 function getMapData(map, key) {
7324 var data = map.__data__;
7325 return _isKeyable(key)
7326 ? data[typeof key == 'string' ? 'string' : 'hash']
7327 : data.map;
7328 }
7329
7330 var _getMapData = getMapData;
7331
7332 /**
7333 * Removes `key` and its value from the map.
7334 *
7335 * @private
7336 * @name delete
7337 * @memberOf MapCache
7338 * @param {string} key The key of the value to remove.
7339 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
7340 */
7341 function mapCacheDelete(key) {
7342 var result = _getMapData(this, key)['delete'](key);
7343 this.size -= result ? 1 : 0;
7344 return result;
7345 }
7346
7347 var _mapCacheDelete = mapCacheDelete;
7348
7349 /**
7350 * Gets the map value for `key`.
7351 *
7352 * @private
7353 * @name get
7354 * @memberOf MapCache
7355 * @param {string} key The key of the value to get.
7356 * @returns {*} Returns the entry value.
7357 */
7358 function mapCacheGet(key) {
7359 return _getMapData(this, key).get(key);
7360 }
7361
7362 var _mapCacheGet = mapCacheGet;
7363
7364 /**
7365 * Checks if a map value for `key` exists.
7366 *
7367 * @private
7368 * @name has
7369 * @memberOf MapCache
7370 * @param {string} key The key of the entry to check.
7371 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
7372 */
7373 function mapCacheHas(key) {
7374 return _getMapData(this, key).has(key);
7375 }
7376
7377 var _mapCacheHas = mapCacheHas;
7378
7379 /**
7380 * Sets the map `key` to `value`.
7381 *
7382 * @private
7383 * @name set
7384 * @memberOf MapCache
7385 * @param {string} key The key of the value to set.
7386 * @param {*} value The value to set.
7387 * @returns {Object} Returns the map cache instance.
7388 */
7389 function mapCacheSet(key, value) {
7390 var data = _getMapData(this, key),
7391 size = data.size;
7392
7393 data.set(key, value);
7394 this.size += data.size == size ? 0 : 1;
7395 return this;
7396 }
7397
7398 var _mapCacheSet = mapCacheSet;
7399
7400 /**
7401 * Creates a map cache object to store key-value pairs.
7402 *
7403 * @private
7404 * @constructor
7405 * @param {Array} [entries] The key-value pairs to cache.
7406 */
7407 function MapCache(entries) {
7408 var index = -1,
7409 length = entries == null ? 0 : entries.length;
7410
7411 this.clear();
7412 while (++index < length) {
7413 var entry = entries[index];
7414 this.set(entry[0], entry[1]);
7415 }
7416 }
7417
7418 // Add methods to `MapCache`.
7419 MapCache.prototype.clear = _mapCacheClear;
7420 MapCache.prototype['delete'] = _mapCacheDelete;
7421 MapCache.prototype.get = _mapCacheGet;
7422 MapCache.prototype.has = _mapCacheHas;
7423 MapCache.prototype.set = _mapCacheSet;
7424
7425 var _MapCache = MapCache;
7426
7427 /** Error message constants. */
7428 var FUNC_ERROR_TEXT = 'Expected a function';
7429
7430 /**
7431 * Creates a function that memoizes the result of `func`. If `resolver` is
7432 * provided, it determines the cache key for storing the result based on the
7433 * arguments provided to the memoized function. By default, the first argument
7434 * provided to the memoized function is used as the map cache key. The `func`
7435 * is invoked with the `this` binding of the memoized function.
7436 *
7437 * **Note:** The cache is exposed as the `cache` property on the memoized
7438 * function. Its creation may be customized by replacing the `_.memoize.Cache`
7439 * constructor with one whose instances implement the
7440 * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
7441 * method interface of `clear`, `delete`, `get`, `has`, and `set`.
7442 *
7443 * @static
7444 * @memberOf _
7445 * @since 0.1.0
7446 * @category Function
7447 * @param {Function} func The function to have its output memoized.
7448 * @param {Function} [resolver] The function to resolve the cache key.
7449 * @returns {Function} Returns the new memoized function.
7450 * @example
7451 *
7452 * var object = { 'a': 1, 'b': 2 };
7453 * var other = { 'c': 3, 'd': 4 };
7454 *
7455 * var values = _.memoize(_.values);
7456 * values(object);
7457 * // => [1, 2]
7458 *
7459 * values(other);
7460 * // => [3, 4]
7461 *
7462 * object.a = 2;
7463 * values(object);
7464 * // => [1, 2]
7465 *
7466 * // Modify the result cache.
7467 * values.cache.set(object, ['a', 'b']);
7468 * values(object);
7469 * // => ['a', 'b']
7470 *
7471 * // Replace `_.memoize.Cache`.
7472 * _.memoize.Cache = WeakMap;
7473 */
7474 function memoize(func, resolver) {
7475 if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
7476 throw new TypeError(FUNC_ERROR_TEXT);
7477 }
7478 var memoized = function() {
7479 var args = arguments,
7480 key = resolver ? resolver.apply(this, args) : args[0],
7481 cache = memoized.cache;
7482
7483 if (cache.has(key)) {
7484 return cache.get(key);
7485 }
7486 var result = func.apply(this, args);
7487 memoized.cache = cache.set(key, result) || cache;
7488 return result;
7489 };
7490 memoized.cache = new (memoize.Cache || _MapCache);
7491 return memoized;
7492 }
7493
7494 // Expose `MapCache`.
7495 memoize.Cache = _MapCache;
7496
7497 var memoize_1 = memoize;
7498
7499 /** Used as the maximum memoize cache size. */
7500 var MAX_MEMOIZE_SIZE = 500;
7501
7502 /**
7503 * A specialized version of `_.memoize` which clears the memoized function's
7504 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
7505 *
7506 * @private
7507 * @param {Function} func The function to have its output memoized.
7508 * @returns {Function} Returns the new memoized function.
7509 */
7510 function memoizeCapped(func) {
7511 var result = memoize_1(func, function(key) {
7512 if (cache.size === MAX_MEMOIZE_SIZE) {
7513 cache.clear();
7514 }
7515 return key;
7516 });
7517
7518 var cache = result.cache;
7519 return result;
7520 }
7521
7522 var _memoizeCapped = memoizeCapped;
7523
7524 /** Used to match property names within property paths. */
7525 var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
7526
7527 /** Used to match backslashes in property paths. */
7528 var reEscapeChar = /\\(\\)?/g;
7529
7530 /**
7531 * Converts `string` to a property path array.
7532 *
7533 * @private
7534 * @param {string} string The string to convert.
7535 * @returns {Array} Returns the property path array.
7536 */
7537 var stringToPath = _memoizeCapped(function(string) {
7538 var result = [];
7539 if (string.charCodeAt(0) === 46 /* . */) {
7540 result.push('');
7541 }
7542 string.replace(rePropName, function(match, number, quote, subString) {
7543 result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
7544 });
7545 return result;
7546 });
7547
7548 var _stringToPath = stringToPath;
7549
7550 /**
7551 * A specialized version of `_.map` for arrays without support for iteratee
7552 * shorthands.
7553 *
7554 * @private
7555 * @param {Array} [array] The array to iterate over.
7556 * @param {Function} iteratee The function invoked per iteration.
7557 * @returns {Array} Returns the new mapped array.
7558 */
7559 function arrayMap(array, iteratee) {
7560 var index = -1,
7561 length = array == null ? 0 : array.length,
7562 result = Array(length);
7563
7564 while (++index < length) {
7565 result[index] = iteratee(array[index], index, array);
7566 }
7567 return result;
7568 }
7569
7570 var _arrayMap = arrayMap;
7571
7572 /** Used as references for various `Number` constants. */
7573 var INFINITY$1 = 1 / 0;
7574
7575 /** Used to convert symbols to primitives and strings. */
7576 var symbolProto = _Symbol ? _Symbol.prototype : undefined,
7577 symbolToString = symbolProto ? symbolProto.toString : undefined;
7578
7579 /**
7580 * The base implementation of `_.toString` which doesn't convert nullish
7581 * values to empty strings.
7582 *
7583 * @private
7584 * @param {*} value The value to process.
7585 * @returns {string} Returns the string.
7586 */
7587 function baseToString(value) {
7588 // Exit early for strings to avoid a performance hit in some environments.
7589 if (typeof value == 'string') {
7590 return value;
7591 }
7592 if (isArray_1(value)) {
7593 // Recursively convert values (susceptible to call stack limits).
7594 return _arrayMap(value, baseToString) + '';
7595 }
7596 if (isSymbol_1(value)) {
7597 return symbolToString ? symbolToString.call(value) : '';
7598 }
7599 var result = (value + '');
7600 return (result == '0' && (1 / value) == -INFINITY$1) ? '-0' : result;
7601 }
7602
7603 var _baseToString = baseToString;
7604
7605 /**
7606 * Converts `value` to a string. An empty string is returned for `null`
7607 * and `undefined` values. The sign of `-0` is preserved.
7608 *
7609 * @static
7610 * @memberOf _
7611 * @since 4.0.0
7612 * @category Lang
7613 * @param {*} value The value to convert.
7614 * @returns {string} Returns the converted string.
7615 * @example
7616 *
7617 * _.toString(null);
7618 * // => ''
7619 *
7620 * _.toString(-0);
7621 * // => '-0'
7622 *
7623 * _.toString([1, 2, 3]);
7624 * // => '1,2,3'
7625 */
7626 function toString$1(value) {
7627 return value == null ? '' : _baseToString(value);
7628 }
7629
7630 var toString_1 = toString$1;
7631
7632 /**
7633 * Casts `value` to a path array if it's not one.
7634 *
7635 * @private
7636 * @param {*} value The value to inspect.
7637 * @param {Object} [object] The object to query keys on.
7638 * @returns {Array} Returns the cast property path array.
7639 */
7640 function castPath(value, object) {
7641 if (isArray_1(value)) {
7642 return value;
7643 }
7644 return _isKey(value, object) ? [value] : _stringToPath(toString_1(value));
7645 }
7646
7647 var _castPath = castPath;
7648
7649 /** Used as references for various `Number` constants. */
7650 var INFINITY = 1 / 0;
7651
7652 /**
7653 * Converts `value` to a string key if it's not a string or symbol.
7654 *
7655 * @private
7656 * @param {*} value The value to inspect.
7657 * @returns {string|symbol} Returns the key.
7658 */
7659 function toKey(value) {
7660 if (typeof value == 'string' || isSymbol_1(value)) {
7661 return value;
7662 }
7663 var result = (value + '');
7664 return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
7665 }
7666
7667 var _toKey = toKey;
7668
7669 /**
7670 * The base implementation of `_.get` without support for default values.
7671 *
7672 * @private
7673 * @param {Object} object The object to query.
7674 * @param {Array|string} path The path of the property to get.
7675 * @returns {*} Returns the resolved value.
7676 */
7677 function baseGet(object, path) {
7678 path = _castPath(path, object);
7679
7680 var index = 0,
7681 length = path.length;
7682
7683 while (object != null && index < length) {
7684 object = object[_toKey(path[index++])];
7685 }
7686 return (index && index == length) ? object : undefined;
7687 }
7688
7689 var _baseGet = baseGet;
7690
7691 /**
7692 * Gets the value at `path` of `object`. If the resolved value is
7693 * `undefined`, the `defaultValue` is returned in its place.
7694 *
7695 * @static
7696 * @memberOf _
7697 * @since 3.7.0
7698 * @category Object
7699 * @param {Object} object The object to query.
7700 * @param {Array|string} path The path of the property to get.
7701 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
7702 * @returns {*} Returns the resolved value.
7703 * @example
7704 *
7705 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
7706 *
7707 * _.get(object, 'a[0].b.c');
7708 * // => 3
7709 *
7710 * _.get(object, ['a', '0', 'b', 'c']);
7711 * // => 3
7712 *
7713 * _.get(object, 'a.b.c', 'default');
7714 * // => 'default'
7715 */
7716 function get(object, path, defaultValue) {
7717 var result = object == null ? undefined : _baseGet(object, path);
7718 return result === undefined ? defaultValue : result;
7719 }
7720
7721 var get_1 = get;
7722
7723 var defineProperty = (function() {
7724 try {
7725 var func = _getNative(Object, 'defineProperty');
7726 func({}, '', {});
7727 return func;
7728 } catch (e) {}
7729 }());
7730
7731 var _defineProperty = defineProperty;
7732
7733 /**
7734 * The base implementation of `assignValue` and `assignMergeValue` without
7735 * value checks.
7736 *
7737 * @private
7738 * @param {Object} object The object to modify.
7739 * @param {string} key The key of the property to assign.
7740 * @param {*} value The value to assign.
7741 */
7742 function baseAssignValue(object, key, value) {
7743 if (key == '__proto__' && _defineProperty) {
7744 _defineProperty(object, key, {
7745 'configurable': true,
7746 'enumerable': true,
7747 'value': value,
7748 'writable': true
7749 });
7750 } else {
7751 object[key] = value;
7752 }
7753 }
7754
7755 var _baseAssignValue = baseAssignValue;
7756
7757 /** Used for built-in method references. */
7758 var objectProto = Object.prototype;
7759
7760 /** Used to check objects for own properties. */
7761 var hasOwnProperty = objectProto.hasOwnProperty;
7762
7763 /**
7764 * Assigns `value` to `key` of `object` if the existing value is not equivalent
7765 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
7766 * for equality comparisons.
7767 *
7768 * @private
7769 * @param {Object} object The object to modify.
7770 * @param {string} key The key of the property to assign.
7771 * @param {*} value The value to assign.
7772 */
7773 function assignValue(object, key, value) {
7774 var objValue = object[key];
7775 if (!(hasOwnProperty.call(object, key) && eq_1(objValue, value)) ||
7776 (value === undefined && !(key in object))) {
7777 _baseAssignValue(object, key, value);
7778 }
7779 }
7780
7781 var _assignValue = assignValue;
7782
7783 /** Used as references for various `Number` constants. */
7784 var MAX_SAFE_INTEGER = 9007199254740991;
7785
7786 /** Used to detect unsigned integer values. */
7787 var reIsUint = /^(?:0|[1-9]\d*)$/;
7788
7789 /**
7790 * Checks if `value` is a valid array-like index.
7791 *
7792 * @private
7793 * @param {*} value The value to check.
7794 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
7795 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
7796 */
7797 function isIndex(value, length) {
7798 var type = typeof value;
7799 length = length == null ? MAX_SAFE_INTEGER : length;
7800
7801 return !!length &&
7802 (type == 'number' ||
7803 (type != 'symbol' && reIsUint.test(value))) &&
7804 (value > -1 && value % 1 == 0 && value < length);
7805 }
7806
7807 var _isIndex = isIndex;
7808
7809 /**
7810 * The base implementation of `_.set`.
7811 *
7812 * @private
7813 * @param {Object} object The object to modify.
7814 * @param {Array|string} path The path of the property to set.
7815 * @param {*} value The value to set.
7816 * @param {Function} [customizer] The function to customize path creation.
7817 * @returns {Object} Returns `object`.
7818 */
7819 function baseSet(object, path, value, customizer) {
7820 if (!isObject_1(object)) {
7821 return object;
7822 }
7823 path = _castPath(path, object);
7824
7825 var index = -1,
7826 length = path.length,
7827 lastIndex = length - 1,
7828 nested = object;
7829
7830 while (nested != null && ++index < length) {
7831 var key = _toKey(path[index]),
7832 newValue = value;
7833
7834 if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
7835 return object;
7836 }
7837
7838 if (index != lastIndex) {
7839 var objValue = nested[key];
7840 newValue = customizer ? customizer(objValue, key, nested) : undefined;
7841 if (newValue === undefined) {
7842 newValue = isObject_1(objValue)
7843 ? objValue
7844 : (_isIndex(path[index + 1]) ? [] : {});
7845 }
7846 }
7847 _assignValue(nested, key, newValue);
7848 nested = nested[key];
7849 }
7850 return object;
7851 }
7852
7853 var _baseSet = baseSet;
7854
7855 /**
7856 * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
7857 * it's created. Arrays are created for missing index properties while objects
7858 * are created for all other missing properties. Use `_.setWith` to customize
7859 * `path` creation.
7860 *
7861 * **Note:** This method mutates `object`.
7862 *
7863 * @static
7864 * @memberOf _
7865 * @since 3.7.0
7866 * @category Object
7867 * @param {Object} object The object to modify.
7868 * @param {Array|string} path The path of the property to set.
7869 * @param {*} value The value to set.
7870 * @returns {Object} Returns `object`.
7871 * @example
7872 *
7873 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
7874 *
7875 * _.set(object, 'a[0].b.c', 4);
7876 * console.log(object.a[0].b.c);
7877 * // => 4
7878 *
7879 * _.set(object, ['x', '0', 'y', 'z'], 5);
7880 * console.log(object.x[0].y.z);
7881 * // => 5
7882 */
7883 function set(object, path, value) {
7884 return object == null ? object : _baseSet(object, path, value);
7885 }
7886
7887 var set_1 = set;
7888
7889 /**
7890 * Copies the values of `source` to `array`.
7891 *
7892 * @private
7893 * @param {Array} source The array to copy values from.
7894 * @param {Array} [array=[]] The array to copy values to.
7895 * @returns {Array} Returns `array`.
7896 */
7897 function copyArray(source, array) {
7898 var index = -1,
7899 length = source.length;
7900
7901 array || (array = Array(length));
7902 while (++index < length) {
7903 array[index] = source[index];
7904 }
7905 return array;
7906 }
7907
7908 var _copyArray = copyArray;
7909
7910 /**
7911 * Converts `value` to a property path array.
7912 *
7913 * @static
7914 * @memberOf _
7915 * @since 4.0.0
7916 * @category Util
7917 * @param {*} value The value to convert.
7918 * @returns {Array} Returns the new property path array.
7919 * @example
7920 *
7921 * _.toPath('a.b.c');
7922 * // => ['a', 'b', 'c']
7923 *
7924 * _.toPath('a[0].b.c');
7925 * // => ['a', '0', 'b', 'c']
7926 */
7927 function toPath(value) {
7928 if (isArray_1(value)) {
7929 return _arrayMap(value, _toKey);
7930 }
7931 return isSymbol_1(value) ? [value] : _copyArray(_stringToPath(toString_1(value)));
7932 }
7933
7934 var toPath_1 = toPath;
7935
7936 var define$2 = {
7937 // access data field
7938 data: function data(params) {
7939 var defaults = {
7940 field: 'data',
7941 bindingEvent: 'data',
7942 allowBinding: false,
7943 allowSetting: false,
7944 allowGetting: false,
7945 settingEvent: 'data',
7946 settingTriggersEvent: false,
7947 triggerFnName: 'trigger',
7948 immutableKeys: {},
7949 // key => true if immutable
7950 updateStyle: false,
7951 beforeGet: function beforeGet(self) {},
7952 beforeSet: function beforeSet(self, obj) {},
7953 onSet: function onSet(self) {},
7954 canSet: function canSet(self) {
7955 return true;
7956 }
7957 };
7958 params = extend({}, defaults, params);
7959 return function dataImpl(name, value) {
7960 var p = params;
7961 var self = this;
7962 var selfIsArrayLike = self.length !== undefined;
7963 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
7964 var single = selfIsArrayLike ? self[0] : self;
7965
7966 // .data('foo', ...)
7967 if (string(name)) {
7968 // set or get property
7969 var isPathLike = name.indexOf('.') !== -1; // there might be a normal field with a dot
7970 var path = isPathLike && toPath_1(name);
7971
7972 // .data('foo')
7973 if (p.allowGetting && value === undefined) {
7974 // get
7975
7976 var ret;
7977 if (single) {
7978 p.beforeGet(single);
7979
7980 // check if it's path and a field with the same name doesn't exist
7981 if (path && single._private[p.field][name] === undefined) {
7982 ret = get_1(single._private[p.field], path);
7983 } else {
7984 ret = single._private[p.field][name];
7985 }
7986 }
7987 return ret;
7988
7989 // .data('foo', 'bar')
7990 } else if (p.allowSetting && value !== undefined) {
7991 // set
7992 var valid = !p.immutableKeys[name];
7993 if (valid) {
7994 var change = _defineProperty$1({}, name, value);
7995 p.beforeSet(self, change);
7996 for (var i = 0, l = all.length; i < l; i++) {
7997 var ele = all[i];
7998 if (p.canSet(ele)) {
7999 if (path && single._private[p.field][name] === undefined) {
8000 set_1(ele._private[p.field], path, value);
8001 } else {
8002 ele._private[p.field][name] = value;
8003 }
8004 }
8005 }
8006
8007 // update mappers if asked
8008 if (p.updateStyle) {
8009 self.updateStyle();
8010 }
8011
8012 // call onSet callback
8013 p.onSet(self);
8014 if (p.settingTriggersEvent) {
8015 self[p.triggerFnName](p.settingEvent);
8016 }
8017 }
8018 }
8019
8020 // .data({ 'foo': 'bar' })
8021 } else if (p.allowSetting && plainObject(name)) {
8022 // extend
8023 var obj = name;
8024 var k, v;
8025 var keys = Object.keys(obj);
8026 p.beforeSet(self, obj);
8027 for (var _i = 0; _i < keys.length; _i++) {
8028 k = keys[_i];
8029 v = obj[k];
8030 var _valid = !p.immutableKeys[k];
8031 if (_valid) {
8032 for (var j = 0; j < all.length; j++) {
8033 var _ele = all[j];
8034 if (p.canSet(_ele)) {
8035 _ele._private[p.field][k] = v;
8036 }
8037 }
8038 }
8039 }
8040
8041 // update mappers if asked
8042 if (p.updateStyle) {
8043 self.updateStyle();
8044 }
8045
8046 // call onSet callback
8047 p.onSet(self);
8048 if (p.settingTriggersEvent) {
8049 self[p.triggerFnName](p.settingEvent);
8050 }
8051
8052 // .data(function(){ ... })
8053 } else if (p.allowBinding && fn$6(name)) {
8054 // bind to event
8055 var fn = name;
8056 self.on(p.bindingEvent, fn);
8057
8058 // .data()
8059 } else if (p.allowGetting && name === undefined) {
8060 // get whole object
8061 var _ret;
8062 if (single) {
8063 p.beforeGet(single);
8064 _ret = single._private[p.field];
8065 }
8066 return _ret;
8067 }
8068 return self; // maintain chainability
8069 }; // function
8070 },
8071
8072 // data
8073
8074 // remove data field
8075 removeData: function removeData(params) {
8076 var defaults = {
8077 field: 'data',
8078 event: 'data',
8079 triggerFnName: 'trigger',
8080 triggerEvent: false,
8081 immutableKeys: {} // key => true if immutable
8082 };
8083
8084 params = extend({}, defaults, params);
8085 return function removeDataImpl(names) {
8086 var p = params;
8087 var self = this;
8088 var selfIsArrayLike = self.length !== undefined;
8089 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
8090
8091 // .removeData('foo bar')
8092 if (string(names)) {
8093 // then get the list of keys, and delete them
8094 var keys = names.split(/\s+/);
8095 var l = keys.length;
8096 for (var i = 0; i < l; i++) {
8097 // delete each non-empty key
8098 var key = keys[i];
8099 if (emptyString(key)) {
8100 continue;
8101 }
8102 var valid = !p.immutableKeys[key]; // not valid if immutable
8103 if (valid) {
8104 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
8105 all[i_a]._private[p.field][key] = undefined;
8106 }
8107 }
8108 }
8109 if (p.triggerEvent) {
8110 self[p.triggerFnName](p.event);
8111 }
8112
8113 // .removeData()
8114 } else if (names === undefined) {
8115 // then delete all keys
8116
8117 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
8118 var _privateFields = all[_i_a]._private[p.field];
8119 var _keys = Object.keys(_privateFields);
8120 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
8121 var _key = _keys[_i2];
8122 var validKeyToDelete = !p.immutableKeys[_key];
8123 if (validKeyToDelete) {
8124 _privateFields[_key] = undefined;
8125 }
8126 }
8127 }
8128 if (p.triggerEvent) {
8129 self[p.triggerFnName](p.event);
8130 }
8131 }
8132 return self; // maintain chaining
8133 }; // function
8134 } // removeData
8135 }; // define
8136
8137 var define$1 = {
8138 eventAliasesOn: function eventAliasesOn(proto) {
8139 var p = proto;
8140 p.addListener = p.listen = p.bind = p.on;
8141 p.unlisten = p.unbind = p.off = p.removeListener;
8142 p.trigger = p.emit;
8143
8144 // this is just a wrapper alias of .on()
8145 p.pon = p.promiseOn = function (events, selector) {
8146 var self = this;
8147 var args = Array.prototype.slice.call(arguments, 0);
8148 return new Promise$1(function (resolve, reject) {
8149 var callback = function callback(e) {
8150 self.off.apply(self, offArgs);
8151 resolve(e);
8152 };
8153 var onArgs = args.concat([callback]);
8154 var offArgs = onArgs.concat([]);
8155 self.on.apply(self, onArgs);
8156 });
8157 };
8158 }
8159 }; // define
8160
8161 // use this module to cherry pick functions into your prototype
8162 var define = {};
8163 [define$3, define$2, define$1].forEach(function (m) {
8164 extend(define, m);
8165 });
8166
8167 var elesfn$i = {
8168 animate: define.animate(),
8169 animation: define.animation(),
8170 animated: define.animated(),
8171 clearQueue: define.clearQueue(),
8172 delay: define.delay(),
8173 delayAnimation: define.delayAnimation(),
8174 stop: define.stop()
8175 };
8176
8177 var elesfn$h = {
8178 classes: function classes(_classes) {
8179 var self = this;
8180 if (_classes === undefined) {
8181 var ret = [];
8182 self[0]._private.classes.forEach(function (cls) {
8183 return ret.push(cls);
8184 });
8185 return ret;
8186 } else if (!array(_classes)) {
8187 // extract classes from string
8188 _classes = (_classes || '').match(/\S+/g) || [];
8189 }
8190 var changed = [];
8191 var classesSet = new Set$1(_classes);
8192
8193 // check and update each ele
8194 for (var j = 0; j < self.length; j++) {
8195 var ele = self[j];
8196 var _p = ele._private;
8197 var eleClasses = _p.classes;
8198 var changedEle = false;
8199
8200 // check if ele has all of the passed classes
8201 for (var i = 0; i < _classes.length; i++) {
8202 var cls = _classes[i];
8203 var eleHasClass = eleClasses.has(cls);
8204 if (!eleHasClass) {
8205 changedEle = true;
8206 break;
8207 }
8208 }
8209
8210 // check if ele has classes outside of those passed
8211 if (!changedEle) {
8212 changedEle = eleClasses.size !== _classes.length;
8213 }
8214 if (changedEle) {
8215 _p.classes = classesSet;
8216 changed.push(ele);
8217 }
8218 }
8219
8220 // trigger update style on those eles that had class changes
8221 if (changed.length > 0) {
8222 this.spawn(changed).updateStyle().emit('class');
8223 }
8224 return self;
8225 },
8226 addClass: function addClass(classes) {
8227 return this.toggleClass(classes, true);
8228 },
8229 hasClass: function hasClass(className) {
8230 var ele = this[0];
8231 return ele != null && ele._private.classes.has(className);
8232 },
8233 toggleClass: function toggleClass(classes, toggle) {
8234 if (!array(classes)) {
8235 // extract classes from string
8236 classes = classes.match(/\S+/g) || [];
8237 }
8238 var self = this;
8239 var toggleUndefd = toggle === undefined;
8240 var changed = []; // eles who had classes changed
8241
8242 for (var i = 0, il = self.length; i < il; i++) {
8243 var ele = self[i];
8244 var eleClasses = ele._private.classes;
8245 var changedEle = false;
8246 for (var j = 0; j < classes.length; j++) {
8247 var cls = classes[j];
8248 var hasClass = eleClasses.has(cls);
8249 var changedNow = false;
8250 if (toggle || toggleUndefd && !hasClass) {
8251 eleClasses.add(cls);
8252 changedNow = true;
8253 } else if (!toggle || toggleUndefd && hasClass) {
8254 eleClasses["delete"](cls);
8255 changedNow = true;
8256 }
8257 if (!changedEle && changedNow) {
8258 changed.push(ele);
8259 changedEle = true;
8260 }
8261 } // for j classes
8262 } // for i eles
8263
8264 // trigger update style on those eles that had class changes
8265 if (changed.length > 0) {
8266 this.spawn(changed).updateStyle().emit('class');
8267 }
8268 return self;
8269 },
8270 removeClass: function removeClass(classes) {
8271 return this.toggleClass(classes, false);
8272 },
8273 flashClass: function flashClass(classes, duration) {
8274 var self = this;
8275 if (duration == null) {
8276 duration = 250;
8277 } else if (duration === 0) {
8278 return self; // nothing to do really
8279 }
8280
8281 self.addClass(classes);
8282 setTimeout(function () {
8283 self.removeClass(classes);
8284 }, duration);
8285 return self;
8286 }
8287 };
8288 elesfn$h.className = elesfn$h.classNames = elesfn$h.classes;
8289
8290 // tokens in the query language
8291 var tokens = {
8292 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
8293 // chars we need to escape in let names, etc
8294 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
8295 // binary comparison op (used in data selectors)
8296 boolOp: '\\?|\\!|\\^',
8297 // boolean (unary) operators (used in data selectors)
8298 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
8299 // string literals (used in data selectors) -- doublequotes | singlequotes
8300 number: number,
8301 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
8302 meta: 'degree|indegree|outdegree',
8303 // allowed metadata fields (i.e. allowed functions to use from Collection)
8304 separator: '\\s*,\\s*',
8305 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
8306 descendant: '\\s+',
8307 child: '\\s+>\\s+',
8308 subject: '\\$',
8309 group: 'node|edge|\\*',
8310 directedEdge: '\\s+->\\s+',
8311 undirectedEdge: '\\s+<->\\s+'
8312 };
8313 tokens.variable = '(?:[\\w-.]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name can have letters, numbers, dashes, and periods
8314 tokens.className = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a class name has the same rules as a variable except it can't have a '.' in the name
8315 tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
8316 tokens.id = tokens.variable; // an element id (follows variable conventions)
8317
8318 (function () {
8319 var ops, op, i;
8320
8321 // add @ variants to comparatorOp
8322 ops = tokens.comparatorOp.split('|');
8323 for (i = 0; i < ops.length; i++) {
8324 op = ops[i];
8325 tokens.comparatorOp += '|@' + op;
8326 }
8327
8328 // add ! variants to comparatorOp
8329 ops = tokens.comparatorOp.split('|');
8330 for (i = 0; i < ops.length; i++) {
8331 op = ops[i];
8332 if (op.indexOf('!') >= 0) {
8333 continue;
8334 } // skip ops that explicitly contain !
8335 if (op === '=') {
8336 continue;
8337 } // skip = b/c != is explicitly defined
8338
8339 tokens.comparatorOp += '|\\!' + op;
8340 }
8341 })();
8342
8343 /**
8344 * Make a new query object
8345 *
8346 * @prop type {Type} The type enum (int) of the query
8347 * @prop checks List of checks to make against an ele to test for a match
8348 */
8349 var newQuery = function newQuery() {
8350 return {
8351 checks: []
8352 };
8353 };
8354
8355 /**
8356 * A check type enum-like object. Uses integer values for fast match() lookup.
8357 * The ordering does not matter as long as the ints are unique.
8358 */
8359 var Type = {
8360 /** E.g. node */
8361 GROUP: 0,
8362 /** A collection of elements */
8363 COLLECTION: 1,
8364 /** A filter(ele) function */
8365 FILTER: 2,
8366 /** E.g. [foo > 1] */
8367 DATA_COMPARE: 3,
8368 /** E.g. [foo] */
8369 DATA_EXIST: 4,
8370 /** E.g. [?foo] */
8371 DATA_BOOL: 5,
8372 /** E.g. [[degree > 2]] */
8373 META_COMPARE: 6,
8374 /** E.g. :selected */
8375 STATE: 7,
8376 /** E.g. #foo */
8377 ID: 8,
8378 /** E.g. .foo */
8379 CLASS: 9,
8380 /** E.g. #foo <-> #bar */
8381 UNDIRECTED_EDGE: 10,
8382 /** E.g. #foo -> #bar */
8383 DIRECTED_EDGE: 11,
8384 /** E.g. $#foo -> #bar */
8385 NODE_SOURCE: 12,
8386 /** E.g. #foo -> $#bar */
8387 NODE_TARGET: 13,
8388 /** E.g. $#foo <-> #bar */
8389 NODE_NEIGHBOR: 14,
8390 /** E.g. #foo > #bar */
8391 CHILD: 15,
8392 /** E.g. #foo #bar */
8393 DESCENDANT: 16,
8394 /** E.g. $#foo > #bar */
8395 PARENT: 17,
8396 /** E.g. $#foo #bar */
8397 ANCESTOR: 18,
8398 /** E.g. #foo > $bar > #baz */
8399 COMPOUND_SPLIT: 19,
8400 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
8401 TRUE: 20
8402 };
8403
8404 var stateSelectors = [{
8405 selector: ':selected',
8406 matches: function matches(ele) {
8407 return ele.selected();
8408 }
8409 }, {
8410 selector: ':unselected',
8411 matches: function matches(ele) {
8412 return !ele.selected();
8413 }
8414 }, {
8415 selector: ':selectable',
8416 matches: function matches(ele) {
8417 return ele.selectable();
8418 }
8419 }, {
8420 selector: ':unselectable',
8421 matches: function matches(ele) {
8422 return !ele.selectable();
8423 }
8424 }, {
8425 selector: ':locked',
8426 matches: function matches(ele) {
8427 return ele.locked();
8428 }
8429 }, {
8430 selector: ':unlocked',
8431 matches: function matches(ele) {
8432 return !ele.locked();
8433 }
8434 }, {
8435 selector: ':visible',
8436 matches: function matches(ele) {
8437 return ele.visible();
8438 }
8439 }, {
8440 selector: ':hidden',
8441 matches: function matches(ele) {
8442 return !ele.visible();
8443 }
8444 }, {
8445 selector: ':transparent',
8446 matches: function matches(ele) {
8447 return ele.transparent();
8448 }
8449 }, {
8450 selector: ':grabbed',
8451 matches: function matches(ele) {
8452 return ele.grabbed();
8453 }
8454 }, {
8455 selector: ':free',
8456 matches: function matches(ele) {
8457 return !ele.grabbed();
8458 }
8459 }, {
8460 selector: ':removed',
8461 matches: function matches(ele) {
8462 return ele.removed();
8463 }
8464 }, {
8465 selector: ':inside',
8466 matches: function matches(ele) {
8467 return !ele.removed();
8468 }
8469 }, {
8470 selector: ':grabbable',
8471 matches: function matches(ele) {
8472 return ele.grabbable();
8473 }
8474 }, {
8475 selector: ':ungrabbable',
8476 matches: function matches(ele) {
8477 return !ele.grabbable();
8478 }
8479 }, {
8480 selector: ':animated',
8481 matches: function matches(ele) {
8482 return ele.animated();
8483 }
8484 }, {
8485 selector: ':unanimated',
8486 matches: function matches(ele) {
8487 return !ele.animated();
8488 }
8489 }, {
8490 selector: ':parent',
8491 matches: function matches(ele) {
8492 return ele.isParent();
8493 }
8494 }, {
8495 selector: ':childless',
8496 matches: function matches(ele) {
8497 return ele.isChildless();
8498 }
8499 }, {
8500 selector: ':child',
8501 matches: function matches(ele) {
8502 return ele.isChild();
8503 }
8504 }, {
8505 selector: ':orphan',
8506 matches: function matches(ele) {
8507 return ele.isOrphan();
8508 }
8509 }, {
8510 selector: ':nonorphan',
8511 matches: function matches(ele) {
8512 return ele.isChild();
8513 }
8514 }, {
8515 selector: ':compound',
8516 matches: function matches(ele) {
8517 if (ele.isNode()) {
8518 return ele.isParent();
8519 } else {
8520 return ele.source().isParent() || ele.target().isParent();
8521 }
8522 }
8523 }, {
8524 selector: ':loop',
8525 matches: function matches(ele) {
8526 return ele.isLoop();
8527 }
8528 }, {
8529 selector: ':simple',
8530 matches: function matches(ele) {
8531 return ele.isSimple();
8532 }
8533 }, {
8534 selector: ':active',
8535 matches: function matches(ele) {
8536 return ele.active();
8537 }
8538 }, {
8539 selector: ':inactive',
8540 matches: function matches(ele) {
8541 return !ele.active();
8542 }
8543 }, {
8544 selector: ':backgrounding',
8545 matches: function matches(ele) {
8546 return ele.backgrounding();
8547 }
8548 }, {
8549 selector: ':nonbackgrounding',
8550 matches: function matches(ele) {
8551 return !ele.backgrounding();
8552 }
8553 }].sort(function (a, b) {
8554 // n.b. selectors that are starting substrings of others must have the longer ones first
8555 return descending(a.selector, b.selector);
8556 });
8557 var lookup = function () {
8558 var selToFn = {};
8559 var s;
8560 for (var i = 0; i < stateSelectors.length; i++) {
8561 s = stateSelectors[i];
8562 selToFn[s.selector] = s.matches;
8563 }
8564 return selToFn;
8565 }();
8566 var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
8567 return lookup[sel](ele);
8568 };
8569 var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
8570 return s.selector;
8571 }).join('|') + ')';
8572
8573 // when a token like a variable has escaped meta characters, we need to clean the backslashes out
8574 // so that values get compared properly in Selector.filter()
8575 var cleanMetaChars = function cleanMetaChars(str) {
8576 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
8577 return $1;
8578 });
8579 };
8580 var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
8581 selector[selector.length - 1] = replacementQuery;
8582 };
8583
8584 // NOTE: add new expression syntax here to have it recognised by the parser;
8585 // - a query contains all adjacent (i.e. no separator in between) expressions;
8586 // - the current query is stored in selector[i]
8587 // - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
8588 var exprs = [{
8589 name: 'group',
8590 // just used for identifying when debugging
8591 query: true,
8592 regex: '(' + tokens.group + ')',
8593 populate: function populate(selector, query, _ref) {
8594 var _ref2 = _slicedToArray(_ref, 1),
8595 group = _ref2[0];
8596 query.checks.push({
8597 type: Type.GROUP,
8598 value: group === '*' ? group : group + 's'
8599 });
8600 }
8601 }, {
8602 name: 'state',
8603 query: true,
8604 regex: stateSelectorRegex,
8605 populate: function populate(selector, query, _ref3) {
8606 var _ref4 = _slicedToArray(_ref3, 1),
8607 state = _ref4[0];
8608 query.checks.push({
8609 type: Type.STATE,
8610 value: state
8611 });
8612 }
8613 }, {
8614 name: 'id',
8615 query: true,
8616 regex: '\\#(' + tokens.id + ')',
8617 populate: function populate(selector, query, _ref5) {
8618 var _ref6 = _slicedToArray(_ref5, 1),
8619 id = _ref6[0];
8620 query.checks.push({
8621 type: Type.ID,
8622 value: cleanMetaChars(id)
8623 });
8624 }
8625 }, {
8626 name: 'className',
8627 query: true,
8628 regex: '\\.(' + tokens.className + ')',
8629 populate: function populate(selector, query, _ref7) {
8630 var _ref8 = _slicedToArray(_ref7, 1),
8631 className = _ref8[0];
8632 query.checks.push({
8633 type: Type.CLASS,
8634 value: cleanMetaChars(className)
8635 });
8636 }
8637 }, {
8638 name: 'dataExists',
8639 query: true,
8640 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
8641 populate: function populate(selector, query, _ref9) {
8642 var _ref10 = _slicedToArray(_ref9, 1),
8643 variable = _ref10[0];
8644 query.checks.push({
8645 type: Type.DATA_EXIST,
8646 field: cleanMetaChars(variable)
8647 });
8648 }
8649 }, {
8650 name: 'dataCompare',
8651 query: true,
8652 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
8653 populate: function populate(selector, query, _ref11) {
8654 var _ref12 = _slicedToArray(_ref11, 3),
8655 variable = _ref12[0],
8656 comparatorOp = _ref12[1],
8657 value = _ref12[2];
8658 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
8659 if (valueIsString) {
8660 value = value.substring(1, value.length - 1);
8661 } else {
8662 value = parseFloat(value);
8663 }
8664 query.checks.push({
8665 type: Type.DATA_COMPARE,
8666 field: cleanMetaChars(variable),
8667 operator: comparatorOp,
8668 value: value
8669 });
8670 }
8671 }, {
8672 name: 'dataBool',
8673 query: true,
8674 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
8675 populate: function populate(selector, query, _ref13) {
8676 var _ref14 = _slicedToArray(_ref13, 2),
8677 boolOp = _ref14[0],
8678 variable = _ref14[1];
8679 query.checks.push({
8680 type: Type.DATA_BOOL,
8681 field: cleanMetaChars(variable),
8682 operator: boolOp
8683 });
8684 }
8685 }, {
8686 name: 'metaCompare',
8687 query: true,
8688 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
8689 populate: function populate(selector, query, _ref15) {
8690 var _ref16 = _slicedToArray(_ref15, 3),
8691 meta = _ref16[0],
8692 comparatorOp = _ref16[1],
8693 number = _ref16[2];
8694 query.checks.push({
8695 type: Type.META_COMPARE,
8696 field: cleanMetaChars(meta),
8697 operator: comparatorOp,
8698 value: parseFloat(number)
8699 });
8700 }
8701 }, {
8702 name: 'nextQuery',
8703 separator: true,
8704 regex: tokens.separator,
8705 populate: function populate(selector, query) {
8706 var currentSubject = selector.currentSubject;
8707 var edgeCount = selector.edgeCount;
8708 var compoundCount = selector.compoundCount;
8709 var lastQ = selector[selector.length - 1];
8710 if (currentSubject != null) {
8711 lastQ.subject = currentSubject;
8712 selector.currentSubject = null;
8713 }
8714 lastQ.edgeCount = edgeCount;
8715 lastQ.compoundCount = compoundCount;
8716 selector.edgeCount = 0;
8717 selector.compoundCount = 0;
8718
8719 // go on to next query
8720 var nextQuery = selector[selector.length++] = newQuery();
8721 return nextQuery; // this is the new query to be filled by the following exprs
8722 }
8723 }, {
8724 name: 'directedEdge',
8725 separator: true,
8726 regex: tokens.directedEdge,
8727 populate: function populate(selector, query) {
8728 if (selector.currentSubject == null) {
8729 // undirected edge
8730 var edgeQuery = newQuery();
8731 var source = query;
8732 var target = newQuery();
8733 edgeQuery.checks.push({
8734 type: Type.DIRECTED_EDGE,
8735 source: source,
8736 target: target
8737 });
8738
8739 // the query in the selector should be the edge rather than the source
8740 replaceLastQuery(selector, query, edgeQuery);
8741 selector.edgeCount++;
8742
8743 // we're now populating the target query with expressions that follow
8744 return target;
8745 } else {
8746 // source/target
8747 var srcTgtQ = newQuery();
8748 var _source = query;
8749 var _target = newQuery();
8750 srcTgtQ.checks.push({
8751 type: Type.NODE_SOURCE,
8752 source: _source,
8753 target: _target
8754 });
8755
8756 // the query in the selector should be the neighbourhood rather than the node
8757 replaceLastQuery(selector, query, srcTgtQ);
8758 selector.edgeCount++;
8759 return _target; // now populating the target with the following expressions
8760 }
8761 }
8762 }, {
8763 name: 'undirectedEdge',
8764 separator: true,
8765 regex: tokens.undirectedEdge,
8766 populate: function populate(selector, query) {
8767 if (selector.currentSubject == null) {
8768 // undirected edge
8769 var edgeQuery = newQuery();
8770 var source = query;
8771 var target = newQuery();
8772 edgeQuery.checks.push({
8773 type: Type.UNDIRECTED_EDGE,
8774 nodes: [source, target]
8775 });
8776
8777 // the query in the selector should be the edge rather than the source
8778 replaceLastQuery(selector, query, edgeQuery);
8779 selector.edgeCount++;
8780
8781 // we're now populating the target query with expressions that follow
8782 return target;
8783 } else {
8784 // neighbourhood
8785 var nhoodQ = newQuery();
8786 var node = query;
8787 var neighbor = newQuery();
8788 nhoodQ.checks.push({
8789 type: Type.NODE_NEIGHBOR,
8790 node: node,
8791 neighbor: neighbor
8792 });
8793
8794 // the query in the selector should be the neighbourhood rather than the node
8795 replaceLastQuery(selector, query, nhoodQ);
8796 return neighbor; // now populating the neighbor with following expressions
8797 }
8798 }
8799 }, {
8800 name: 'child',
8801 separator: true,
8802 regex: tokens.child,
8803 populate: function populate(selector, query) {
8804 if (selector.currentSubject == null) {
8805 // default: child query
8806 var parentChildQuery = newQuery();
8807 var child = newQuery();
8808 var parent = selector[selector.length - 1];
8809 parentChildQuery.checks.push({
8810 type: Type.CHILD,
8811 parent: parent,
8812 child: child
8813 });
8814
8815 // the query in the selector should be the '>' itself
8816 replaceLastQuery(selector, query, parentChildQuery);
8817 selector.compoundCount++;
8818
8819 // we're now populating the child query with expressions that follow
8820 return child;
8821 } else if (selector.currentSubject === query) {
8822 // compound split query
8823 var compound = newQuery();
8824 var left = selector[selector.length - 1];
8825 var right = newQuery();
8826 var subject = newQuery();
8827 var _child = newQuery();
8828 var _parent = newQuery();
8829
8830 // set up the root compound q
8831 compound.checks.push({
8832 type: Type.COMPOUND_SPLIT,
8833 left: left,
8834 right: right,
8835 subject: subject
8836 });
8837
8838 // populate the subject and replace the q at the old spot (within left) with TRUE
8839 subject.checks = query.checks; // take the checks from the left
8840 query.checks = [{
8841 type: Type.TRUE
8842 }]; // checks under left refs the subject implicitly
8843
8844 // set up the right q
8845 _parent.checks.push({
8846 type: Type.TRUE
8847 }); // parent implicitly refs the subject
8848 right.checks.push({
8849 type: Type.PARENT,
8850 // type is swapped on right side queries
8851 parent: _parent,
8852 child: _child // empty for now
8853 });
8854
8855 replaceLastQuery(selector, left, compound);
8856
8857 // update the ref since we moved things around for `query`
8858 selector.currentSubject = subject;
8859 selector.compoundCount++;
8860 return _child; // now populating the right side's child
8861 } else {
8862 // parent query
8863 // info for parent query
8864 var _parent2 = newQuery();
8865 var _child2 = newQuery();
8866 var pcQChecks = [{
8867 type: Type.PARENT,
8868 parent: _parent2,
8869 child: _child2
8870 }];
8871
8872 // the parent-child query takes the place of the query previously being populated
8873 _parent2.checks = query.checks; // the previous query contains the checks for the parent
8874 query.checks = pcQChecks; // pc query takes over
8875
8876 selector.compoundCount++;
8877 return _child2; // we're now populating the child
8878 }
8879 }
8880 }, {
8881 name: 'descendant',
8882 separator: true,
8883 regex: tokens.descendant,
8884 populate: function populate(selector, query) {
8885 if (selector.currentSubject == null) {
8886 // default: descendant query
8887 var ancChQuery = newQuery();
8888 var descendant = newQuery();
8889 var ancestor = selector[selector.length - 1];
8890 ancChQuery.checks.push({
8891 type: Type.DESCENDANT,
8892 ancestor: ancestor,
8893 descendant: descendant
8894 });
8895
8896 // the query in the selector should be the '>' itself
8897 replaceLastQuery(selector, query, ancChQuery);
8898 selector.compoundCount++;
8899
8900 // we're now populating the descendant query with expressions that follow
8901 return descendant;
8902 } else if (selector.currentSubject === query) {
8903 // compound split query
8904 var compound = newQuery();
8905 var left = selector[selector.length - 1];
8906 var right = newQuery();
8907 var subject = newQuery();
8908 var _descendant = newQuery();
8909 var _ancestor = newQuery();
8910
8911 // set up the root compound q
8912 compound.checks.push({
8913 type: Type.COMPOUND_SPLIT,
8914 left: left,
8915 right: right,
8916 subject: subject
8917 });
8918
8919 // populate the subject and replace the q at the old spot (within left) with TRUE
8920 subject.checks = query.checks; // take the checks from the left
8921 query.checks = [{
8922 type: Type.TRUE
8923 }]; // checks under left refs the subject implicitly
8924
8925 // set up the right q
8926 _ancestor.checks.push({
8927 type: Type.TRUE
8928 }); // ancestor implicitly refs the subject
8929 right.checks.push({
8930 type: Type.ANCESTOR,
8931 // type is swapped on right side queries
8932 ancestor: _ancestor,
8933 descendant: _descendant // empty for now
8934 });
8935
8936 replaceLastQuery(selector, left, compound);
8937
8938 // update the ref since we moved things around for `query`
8939 selector.currentSubject = subject;
8940 selector.compoundCount++;
8941 return _descendant; // now populating the right side's descendant
8942 } else {
8943 // ancestor query
8944 // info for parent query
8945 var _ancestor2 = newQuery();
8946 var _descendant2 = newQuery();
8947 var adQChecks = [{
8948 type: Type.ANCESTOR,
8949 ancestor: _ancestor2,
8950 descendant: _descendant2
8951 }];
8952
8953 // the parent-child query takes the place of the query previously being populated
8954 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
8955 query.checks = adQChecks; // pc query takes over
8956
8957 selector.compoundCount++;
8958 return _descendant2; // we're now populating the child
8959 }
8960 }
8961 }, {
8962 name: 'subject',
8963 modifier: true,
8964 regex: tokens.subject,
8965 populate: function populate(selector, query) {
8966 if (selector.currentSubject != null && selector.currentSubject !== query) {
8967 warn('Redefinition of subject in selector `' + selector.toString() + '`');
8968 return false;
8969 }
8970 selector.currentSubject = query;
8971 var topQ = selector[selector.length - 1];
8972 var topChk = topQ.checks[0];
8973 var topType = topChk == null ? null : topChk.type;
8974 if (topType === Type.DIRECTED_EDGE) {
8975 // directed edge with subject on the target
8976
8977 // change to target node check
8978 topChk.type = Type.NODE_TARGET;
8979 } else if (topType === Type.UNDIRECTED_EDGE) {
8980 // undirected edge with subject on the second node
8981
8982 // change to neighbor check
8983 topChk.type = Type.NODE_NEIGHBOR;
8984 topChk.node = topChk.nodes[1]; // second node is subject
8985 topChk.neighbor = topChk.nodes[0];
8986
8987 // clean up unused fields for new type
8988 topChk.nodes = null;
8989 }
8990 }
8991 }];
8992 exprs.forEach(function (e) {
8993 return e.regexObj = new RegExp('^' + e.regex);
8994 });
8995
8996 /**
8997 * Of all the expressions, find the first match in the remaining text.
8998 * @param {string} remaining The remaining text to parse
8999 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
9000 */
9001 var consumeExpr = function consumeExpr(remaining) {
9002 var expr;
9003 var match;
9004 var name;
9005 for (var j = 0; j < exprs.length; j++) {
9006 var e = exprs[j];
9007 var n = e.name;
9008 var m = remaining.match(e.regexObj);
9009 if (m != null) {
9010 match = m;
9011 expr = e;
9012 name = n;
9013 var consumed = m[0];
9014 remaining = remaining.substring(consumed.length);
9015 break; // we've consumed one expr, so we can return now
9016 }
9017 }
9018
9019 return {
9020 expr: expr,
9021 match: match,
9022 name: name,
9023 remaining: remaining
9024 };
9025 };
9026
9027 /**
9028 * Consume all the leading whitespace
9029 * @param {string} remaining The text to consume
9030 * @returns The text with the leading whitespace removed
9031 */
9032 var consumeWhitespace = function consumeWhitespace(remaining) {
9033 var match = remaining.match(/^\s+/);
9034 if (match) {
9035 var consumed = match[0];
9036 remaining = remaining.substring(consumed.length);
9037 }
9038 return remaining;
9039 };
9040
9041 /**
9042 * Parse the string and store the parsed representation in the Selector.
9043 * @param {string} selector The selector string
9044 * @returns `true` if the selector was successfully parsed, `false` otherwise
9045 */
9046 var parse = function parse(selector) {
9047 var self = this;
9048 var remaining = self.inputText = selector;
9049 var currentQuery = self[0] = newQuery();
9050 self.length = 1;
9051 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
9052
9053 for (;;) {
9054 var exprInfo = consumeExpr(remaining);
9055 if (exprInfo.expr == null) {
9056 warn('The selector `' + selector + '`is invalid');
9057 return false;
9058 } else {
9059 var args = exprInfo.match.slice(1);
9060
9061 // let the token populate the selector object in currentQuery
9062 var ret = exprInfo.expr.populate(self, currentQuery, args);
9063 if (ret === false) {
9064 return false; // exit if population failed
9065 } else if (ret != null) {
9066 currentQuery = ret; // change the current query to be filled if the expr specifies
9067 }
9068 }
9069
9070 remaining = exprInfo.remaining;
9071
9072 // we're done when there's nothing left to parse
9073 if (remaining.match(/^\s*$/)) {
9074 break;
9075 }
9076 }
9077 var lastQ = self[self.length - 1];
9078 if (self.currentSubject != null) {
9079 lastQ.subject = self.currentSubject;
9080 }
9081 lastQ.edgeCount = self.edgeCount;
9082 lastQ.compoundCount = self.compoundCount;
9083 for (var i = 0; i < self.length; i++) {
9084 var q = self[i];
9085
9086 // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
9087 if (q.compoundCount > 0 && q.edgeCount > 0) {
9088 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
9089 return false;
9090 }
9091 if (q.edgeCount > 1) {
9092 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
9093 return false;
9094 } else if (q.edgeCount === 1) {
9095 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.');
9096 }
9097 }
9098 return true; // success
9099 };
9100
9101 /**
9102 * Get the selector represented as a string. This value uses default formatting,
9103 * so things like spacing may differ from the input text passed to the constructor.
9104 * @returns {string} The selector string
9105 */
9106 var toString = function toString() {
9107 if (this.toStringCache != null) {
9108 return this.toStringCache;
9109 }
9110 var clean = function clean(obj) {
9111 if (obj == null) {
9112 return '';
9113 } else {
9114 return obj;
9115 }
9116 };
9117 var cleanVal = function cleanVal(val) {
9118 if (string(val)) {
9119 return '"' + val + '"';
9120 } else {
9121 return clean(val);
9122 }
9123 };
9124 var space = function space(val) {
9125 return ' ' + val + ' ';
9126 };
9127 var checkToString = function checkToString(check, subject) {
9128 var type = check.type,
9129 value = check.value;
9130 switch (type) {
9131 case Type.GROUP:
9132 {
9133 var group = clean(value);
9134 return group.substring(0, group.length - 1);
9135 }
9136 case Type.DATA_COMPARE:
9137 {
9138 var field = check.field,
9139 operator = check.operator;
9140 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
9141 }
9142 case Type.DATA_BOOL:
9143 {
9144 var _operator = check.operator,
9145 _field = check.field;
9146 return '[' + clean(_operator) + _field + ']';
9147 }
9148 case Type.DATA_EXIST:
9149 {
9150 var _field2 = check.field;
9151 return '[' + _field2 + ']';
9152 }
9153 case Type.META_COMPARE:
9154 {
9155 var _operator2 = check.operator,
9156 _field3 = check.field;
9157 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
9158 }
9159 case Type.STATE:
9160 {
9161 return value;
9162 }
9163 case Type.ID:
9164 {
9165 return '#' + value;
9166 }
9167 case Type.CLASS:
9168 {
9169 return '.' + value;
9170 }
9171 case Type.PARENT:
9172 case Type.CHILD:
9173 {
9174 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
9175 }
9176 case Type.ANCESTOR:
9177 case Type.DESCENDANT:
9178 {
9179 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
9180 }
9181 case Type.COMPOUND_SPLIT:
9182 {
9183 var lhs = queryToString(check.left, subject);
9184 var sub = queryToString(check.subject, subject);
9185 var rhs = queryToString(check.right, subject);
9186 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
9187 }
9188 case Type.TRUE:
9189 {
9190 return '';
9191 }
9192 }
9193 };
9194 var queryToString = function queryToString(query, subject) {
9195 return query.checks.reduce(function (str, chk, i) {
9196 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
9197 }, '');
9198 };
9199 var str = '';
9200 for (var i = 0; i < this.length; i++) {
9201 var query = this[i];
9202 str += queryToString(query, query.subject);
9203 if (this.length > 1 && i < this.length - 1) {
9204 str += ', ';
9205 }
9206 }
9207 this.toStringCache = str;
9208 return str;
9209 };
9210 var parse$1 = {
9211 parse: parse,
9212 toString: toString
9213 };
9214
9215 var valCmp = function valCmp(fieldVal, operator, value) {
9216 var matches;
9217 var isFieldStr = string(fieldVal);
9218 var isFieldNum = number$1(fieldVal);
9219 var isValStr = string(value);
9220 var fieldStr, valStr;
9221 var caseInsensitive = false;
9222 var notExpr = false;
9223 var isIneqCmp = false;
9224 if (operator.indexOf('!') >= 0) {
9225 operator = operator.replace('!', '');
9226 notExpr = true;
9227 }
9228 if (operator.indexOf('@') >= 0) {
9229 operator = operator.replace('@', '');
9230 caseInsensitive = true;
9231 }
9232 if (isFieldStr || isValStr || caseInsensitive) {
9233 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
9234 valStr = '' + value;
9235 }
9236
9237 // if we're doing a case insensitive comparison, then we're using a STRING comparison
9238 // even if we're comparing numbers
9239 if (caseInsensitive) {
9240 fieldVal = fieldStr = fieldStr.toLowerCase();
9241 value = valStr = valStr.toLowerCase();
9242 }
9243 switch (operator) {
9244 case '*=':
9245 matches = fieldStr.indexOf(valStr) >= 0;
9246 break;
9247 case '$=':
9248 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
9249 break;
9250 case '^=':
9251 matches = fieldStr.indexOf(valStr) === 0;
9252 break;
9253 case '=':
9254 matches = fieldVal === value;
9255 break;
9256 case '>':
9257 isIneqCmp = true;
9258 matches = fieldVal > value;
9259 break;
9260 case '>=':
9261 isIneqCmp = true;
9262 matches = fieldVal >= value;
9263 break;
9264 case '<':
9265 isIneqCmp = true;
9266 matches = fieldVal < value;
9267 break;
9268 case '<=':
9269 isIneqCmp = true;
9270 matches = fieldVal <= value;
9271 break;
9272 default:
9273 matches = false;
9274 break;
9275 }
9276
9277 // apply the not op, but null vals for inequalities should always stay non-matching
9278 if (notExpr && (fieldVal != null || !isIneqCmp)) {
9279 matches = !matches;
9280 }
9281 return matches;
9282 };
9283 var boolCmp = function boolCmp(fieldVal, operator) {
9284 switch (operator) {
9285 case '?':
9286 return fieldVal ? true : false;
9287 case '!':
9288 return fieldVal ? false : true;
9289 case '^':
9290 return fieldVal === undefined;
9291 }
9292 };
9293 var existCmp = function existCmp(fieldVal) {
9294 return fieldVal !== undefined;
9295 };
9296 var data$1 = function data(ele, field) {
9297 return ele.data(field);
9298 };
9299 var meta = function meta(ele, field) {
9300 return ele[field]();
9301 };
9302
9303 /** A lookup of `match(check, ele)` functions by `Type` int */
9304 var match = [];
9305
9306 /**
9307 * Returns whether the query matches for the element
9308 * @param query The `{ type, value, ... }` query object
9309 * @param ele The element to compare against
9310 */
9311 var matches$1 = function matches(query, ele) {
9312 return query.checks.every(function (chk) {
9313 return match[chk.type](chk, ele);
9314 });
9315 };
9316 match[Type.GROUP] = function (check, ele) {
9317 var group = check.value;
9318 return group === '*' || group === ele.group();
9319 };
9320 match[Type.STATE] = function (check, ele) {
9321 var stateSelector = check.value;
9322 return stateSelectorMatches(stateSelector, ele);
9323 };
9324 match[Type.ID] = function (check, ele) {
9325 var id = check.value;
9326 return ele.id() === id;
9327 };
9328 match[Type.CLASS] = function (check, ele) {
9329 var cls = check.value;
9330 return ele.hasClass(cls);
9331 };
9332 match[Type.META_COMPARE] = function (check, ele) {
9333 var field = check.field,
9334 operator = check.operator,
9335 value = check.value;
9336 return valCmp(meta(ele, field), operator, value);
9337 };
9338 match[Type.DATA_COMPARE] = function (check, ele) {
9339 var field = check.field,
9340 operator = check.operator,
9341 value = check.value;
9342 return valCmp(data$1(ele, field), operator, value);
9343 };
9344 match[Type.DATA_BOOL] = function (check, ele) {
9345 var field = check.field,
9346 operator = check.operator;
9347 return boolCmp(data$1(ele, field), operator);
9348 };
9349 match[Type.DATA_EXIST] = function (check, ele) {
9350 var field = check.field;
9351 check.operator;
9352 return existCmp(data$1(ele, field));
9353 };
9354 match[Type.UNDIRECTED_EDGE] = function (check, ele) {
9355 var qA = check.nodes[0];
9356 var qB = check.nodes[1];
9357 var src = ele.source();
9358 var tgt = ele.target();
9359 return matches$1(qA, src) && matches$1(qB, tgt) || matches$1(qB, src) && matches$1(qA, tgt);
9360 };
9361 match[Type.NODE_NEIGHBOR] = function (check, ele) {
9362 return matches$1(check.node, ele) && ele.neighborhood().some(function (n) {
9363 return n.isNode() && matches$1(check.neighbor, n);
9364 });
9365 };
9366 match[Type.DIRECTED_EDGE] = function (check, ele) {
9367 return matches$1(check.source, ele.source()) && matches$1(check.target, ele.target());
9368 };
9369 match[Type.NODE_SOURCE] = function (check, ele) {
9370 return matches$1(check.source, ele) && ele.outgoers().some(function (n) {
9371 return n.isNode() && matches$1(check.target, n);
9372 });
9373 };
9374 match[Type.NODE_TARGET] = function (check, ele) {
9375 return matches$1(check.target, ele) && ele.incomers().some(function (n) {
9376 return n.isNode() && matches$1(check.source, n);
9377 });
9378 };
9379 match[Type.CHILD] = function (check, ele) {
9380 return matches$1(check.child, ele) && matches$1(check.parent, ele.parent());
9381 };
9382 match[Type.PARENT] = function (check, ele) {
9383 return matches$1(check.parent, ele) && ele.children().some(function (c) {
9384 return matches$1(check.child, c);
9385 });
9386 };
9387 match[Type.DESCENDANT] = function (check, ele) {
9388 return matches$1(check.descendant, ele) && ele.ancestors().some(function (a) {
9389 return matches$1(check.ancestor, a);
9390 });
9391 };
9392 match[Type.ANCESTOR] = function (check, ele) {
9393 return matches$1(check.ancestor, ele) && ele.descendants().some(function (d) {
9394 return matches$1(check.descendant, d);
9395 });
9396 };
9397 match[Type.COMPOUND_SPLIT] = function (check, ele) {
9398 return matches$1(check.subject, ele) && matches$1(check.left, ele) && matches$1(check.right, ele);
9399 };
9400 match[Type.TRUE] = function () {
9401 return true;
9402 };
9403 match[Type.COLLECTION] = function (check, ele) {
9404 var collection = check.value;
9405 return collection.has(ele);
9406 };
9407 match[Type.FILTER] = function (check, ele) {
9408 var filter = check.value;
9409 return filter(ele);
9410 };
9411
9412 // filter an existing collection
9413 var filter = function filter(collection) {
9414 var self = this;
9415
9416 // for 1 id #foo queries, just get the element
9417 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
9418 return collection.getElementById(self[0].checks[0].value).collection();
9419 }
9420 var selectorFunction = function selectorFunction(element) {
9421 for (var j = 0; j < self.length; j++) {
9422 var query = self[j];
9423 if (matches$1(query, element)) {
9424 return true;
9425 }
9426 }
9427 return false;
9428 };
9429 if (self.text() == null) {
9430 selectorFunction = function selectorFunction() {
9431 return true;
9432 };
9433 }
9434 return collection.filter(selectorFunction);
9435 }; // filter
9436
9437 // does selector match a single element?
9438 var matches = function matches(ele) {
9439 var self = this;
9440 for (var j = 0; j < self.length; j++) {
9441 var query = self[j];
9442 if (matches$1(query, ele)) {
9443 return true;
9444 }
9445 }
9446 return false;
9447 }; // matches
9448
9449 var matching = {
9450 matches: matches,
9451 filter: filter
9452 };
9453
9454 var Selector = function Selector(selector) {
9455 this.inputText = selector;
9456 this.currentSubject = null;
9457 this.compoundCount = 0;
9458 this.edgeCount = 0;
9459 this.length = 0;
9460 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
9461 this.addQuery({
9462 checks: [{
9463 type: Type.COLLECTION,
9464 value: selector.collection()
9465 }]
9466 });
9467 } else if (fn$6(selector)) {
9468 this.addQuery({
9469 checks: [{
9470 type: Type.FILTER,
9471 value: selector
9472 }]
9473 });
9474 } else if (string(selector)) {
9475 if (!this.parse(selector)) {
9476 this.invalid = true;
9477 }
9478 } else {
9479 error('A selector must be created from a string; found ');
9480 }
9481 };
9482 var selfn = Selector.prototype;
9483 [parse$1, matching].forEach(function (p) {
9484 return extend(selfn, p);
9485 });
9486 selfn.text = function () {
9487 return this.inputText;
9488 };
9489 selfn.size = function () {
9490 return this.length;
9491 };
9492 selfn.eq = function (i) {
9493 return this[i];
9494 };
9495 selfn.sameText = function (otherSel) {
9496 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
9497 };
9498 selfn.addQuery = function (q) {
9499 this[this.length++] = q;
9500 };
9501 selfn.selector = selfn.toString;
9502
9503 var elesfn$g = {
9504 allAre: function allAre(selector) {
9505 var selObj = new Selector(selector);
9506 return this.every(function (ele) {
9507 return selObj.matches(ele);
9508 });
9509 },
9510 is: function is(selector) {
9511 var selObj = new Selector(selector);
9512 return this.some(function (ele) {
9513 return selObj.matches(ele);
9514 });
9515 },
9516 some: function some(fn, thisArg) {
9517 for (var i = 0; i < this.length; i++) {
9518 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9519 if (ret) {
9520 return true;
9521 }
9522 }
9523 return false;
9524 },
9525 every: function every(fn, thisArg) {
9526 for (var i = 0; i < this.length; i++) {
9527 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
9528 if (!ret) {
9529 return false;
9530 }
9531 }
9532 return true;
9533 },
9534 same: function same(collection) {
9535 // cheap collection ref check
9536 if (this === collection) {
9537 return true;
9538 }
9539 collection = this.cy().collection(collection);
9540 var thisLength = this.length;
9541 var collectionLength = collection.length;
9542
9543 // cheap length check
9544 if (thisLength !== collectionLength) {
9545 return false;
9546 }
9547
9548 // cheap element ref check
9549 if (thisLength === 1) {
9550 return this[0] === collection[0];
9551 }
9552 return this.every(function (ele) {
9553 return collection.hasElementWithId(ele.id());
9554 });
9555 },
9556 anySame: function anySame(collection) {
9557 collection = this.cy().collection(collection);
9558 return this.some(function (ele) {
9559 return collection.hasElementWithId(ele.id());
9560 });
9561 },
9562 allAreNeighbors: function allAreNeighbors(collection) {
9563 collection = this.cy().collection(collection);
9564 var nhood = this.neighborhood();
9565 return collection.every(function (ele) {
9566 return nhood.hasElementWithId(ele.id());
9567 });
9568 },
9569 contains: function contains(collection) {
9570 collection = this.cy().collection(collection);
9571 var self = this;
9572 return collection.every(function (ele) {
9573 return self.hasElementWithId(ele.id());
9574 });
9575 }
9576 };
9577 elesfn$g.allAreNeighbours = elesfn$g.allAreNeighbors;
9578 elesfn$g.has = elesfn$g.contains;
9579 elesfn$g.equal = elesfn$g.equals = elesfn$g.same;
9580
9581 var cache = function cache(fn, name) {
9582 return function traversalCache(arg1, arg2, arg3, arg4) {
9583 var selectorOrEles = arg1;
9584 var eles = this;
9585 var key;
9586 if (selectorOrEles == null) {
9587 key = '';
9588 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
9589 key = selectorOrEles.id();
9590 }
9591 if (eles.length === 1 && key) {
9592 var _p = eles[0]._private;
9593 var tch = _p.traversalCache = _p.traversalCache || {};
9594 var ch = tch[name] = tch[name] || [];
9595 var hash = hashString(key);
9596 var cacheHit = ch[hash];
9597 if (cacheHit) {
9598 return cacheHit;
9599 } else {
9600 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
9601 }
9602 } else {
9603 return fn.call(eles, arg1, arg2, arg3, arg4);
9604 }
9605 };
9606 };
9607
9608 var elesfn$f = {
9609 parent: function parent(selector) {
9610 var parents = [];
9611
9612 // optimisation for single ele call
9613 if (this.length === 1) {
9614 var parent = this[0]._private.parent;
9615 if (parent) {
9616 return parent;
9617 }
9618 }
9619 for (var i = 0; i < this.length; i++) {
9620 var ele = this[i];
9621 var _parent = ele._private.parent;
9622 if (_parent) {
9623 parents.push(_parent);
9624 }
9625 }
9626 return this.spawn(parents, true).filter(selector);
9627 },
9628 parents: function parents(selector) {
9629 var parents = [];
9630 var eles = this.parent();
9631 while (eles.nonempty()) {
9632 for (var i = 0; i < eles.length; i++) {
9633 var ele = eles[i];
9634 parents.push(ele);
9635 }
9636 eles = eles.parent();
9637 }
9638 return this.spawn(parents, true).filter(selector);
9639 },
9640 commonAncestors: function commonAncestors(selector) {
9641 var ancestors;
9642 for (var i = 0; i < this.length; i++) {
9643 var ele = this[i];
9644 var parents = ele.parents();
9645 ancestors = ancestors || parents;
9646 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
9647 }
9648
9649 return ancestors.filter(selector);
9650 },
9651 orphans: function orphans(selector) {
9652 return this.stdFilter(function (ele) {
9653 return ele.isOrphan();
9654 }).filter(selector);
9655 },
9656 nonorphans: function nonorphans(selector) {
9657 return this.stdFilter(function (ele) {
9658 return ele.isChild();
9659 }).filter(selector);
9660 },
9661 children: cache(function (selector) {
9662 var children = [];
9663 for (var i = 0; i < this.length; i++) {
9664 var ele = this[i];
9665 var eleChildren = ele._private.children;
9666 for (var j = 0; j < eleChildren.length; j++) {
9667 children.push(eleChildren[j]);
9668 }
9669 }
9670 return this.spawn(children, true).filter(selector);
9671 }, 'children'),
9672 siblings: function siblings(selector) {
9673 return this.parent().children().not(this).filter(selector);
9674 },
9675 isParent: function isParent() {
9676 var ele = this[0];
9677 if (ele) {
9678 return ele.isNode() && ele._private.children.length !== 0;
9679 }
9680 },
9681 isChildless: function isChildless() {
9682 var ele = this[0];
9683 if (ele) {
9684 return ele.isNode() && ele._private.children.length === 0;
9685 }
9686 },
9687 isChild: function isChild() {
9688 var ele = this[0];
9689 if (ele) {
9690 return ele.isNode() && ele._private.parent != null;
9691 }
9692 },
9693 isOrphan: function isOrphan() {
9694 var ele = this[0];
9695 if (ele) {
9696 return ele.isNode() && ele._private.parent == null;
9697 }
9698 },
9699 descendants: function descendants(selector) {
9700 var elements = [];
9701 function add(eles) {
9702 for (var i = 0; i < eles.length; i++) {
9703 var ele = eles[i];
9704 elements.push(ele);
9705 if (ele.children().nonempty()) {
9706 add(ele.children());
9707 }
9708 }
9709 }
9710 add(this.children());
9711 return this.spawn(elements, true).filter(selector);
9712 }
9713 };
9714 function forEachCompound(eles, fn, includeSelf, recursiveStep) {
9715 var q = [];
9716 var did = new Set$1();
9717 var cy = eles.cy();
9718 var hasCompounds = cy.hasCompoundNodes();
9719 for (var i = 0; i < eles.length; i++) {
9720 var ele = eles[i];
9721 if (includeSelf) {
9722 q.push(ele);
9723 } else if (hasCompounds) {
9724 recursiveStep(q, did, ele);
9725 }
9726 }
9727 while (q.length > 0) {
9728 var _ele = q.shift();
9729 fn(_ele);
9730 did.add(_ele.id());
9731 if (hasCompounds) {
9732 recursiveStep(q, did, _ele);
9733 }
9734 }
9735 return eles;
9736 }
9737 function addChildren(q, did, ele) {
9738 if (ele.isParent()) {
9739 var children = ele._private.children;
9740 for (var i = 0; i < children.length; i++) {
9741 var child = children[i];
9742 if (!did.has(child.id())) {
9743 q.push(child);
9744 }
9745 }
9746 }
9747 }
9748
9749 // very efficient version of eles.add( eles.descendants() ).forEach()
9750 // for internal use
9751 elesfn$f.forEachDown = function (fn) {
9752 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9753 return forEachCompound(this, fn, includeSelf, addChildren);
9754 };
9755 function addParent(q, did, ele) {
9756 if (ele.isChild()) {
9757 var parent = ele._private.parent;
9758 if (!did.has(parent.id())) {
9759 q.push(parent);
9760 }
9761 }
9762 }
9763 elesfn$f.forEachUp = function (fn) {
9764 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9765 return forEachCompound(this, fn, includeSelf, addParent);
9766 };
9767 function addParentAndChildren(q, did, ele) {
9768 addParent(q, did, ele);
9769 addChildren(q, did, ele);
9770 }
9771 elesfn$f.forEachUpAndDown = function (fn) {
9772 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
9773 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
9774 };
9775
9776 // aliases
9777 elesfn$f.ancestors = elesfn$f.parents;
9778
9779 var fn$5, elesfn$e;
9780 fn$5 = elesfn$e = {
9781 data: define.data({
9782 field: 'data',
9783 bindingEvent: 'data',
9784 allowBinding: true,
9785 allowSetting: true,
9786 settingEvent: 'data',
9787 settingTriggersEvent: true,
9788 triggerFnName: 'trigger',
9789 allowGetting: true,
9790 immutableKeys: {
9791 'id': true,
9792 'source': true,
9793 'target': true,
9794 'parent': true
9795 },
9796 updateStyle: true
9797 }),
9798 removeData: define.removeData({
9799 field: 'data',
9800 event: 'data',
9801 triggerFnName: 'trigger',
9802 triggerEvent: true,
9803 immutableKeys: {
9804 'id': true,
9805 'source': true,
9806 'target': true,
9807 'parent': true
9808 },
9809 updateStyle: true
9810 }),
9811 scratch: define.data({
9812 field: 'scratch',
9813 bindingEvent: 'scratch',
9814 allowBinding: true,
9815 allowSetting: true,
9816 settingEvent: 'scratch',
9817 settingTriggersEvent: true,
9818 triggerFnName: 'trigger',
9819 allowGetting: true,
9820 updateStyle: true
9821 }),
9822 removeScratch: define.removeData({
9823 field: 'scratch',
9824 event: 'scratch',
9825 triggerFnName: 'trigger',
9826 triggerEvent: true,
9827 updateStyle: true
9828 }),
9829 rscratch: define.data({
9830 field: 'rscratch',
9831 allowBinding: false,
9832 allowSetting: true,
9833 settingTriggersEvent: false,
9834 allowGetting: true
9835 }),
9836 removeRscratch: define.removeData({
9837 field: 'rscratch',
9838 triggerEvent: false
9839 }),
9840 id: function id() {
9841 var ele = this[0];
9842 if (ele) {
9843 return ele._private.data.id;
9844 }
9845 }
9846 };
9847
9848 // aliases
9849 fn$5.attr = fn$5.data;
9850 fn$5.removeAttr = fn$5.removeData;
9851 var data = elesfn$e;
9852
9853 var elesfn$d = {};
9854 function defineDegreeFunction(callback) {
9855 return function (includeLoops) {
9856 var self = this;
9857 if (includeLoops === undefined) {
9858 includeLoops = true;
9859 }
9860 if (self.length === 0) {
9861 return;
9862 }
9863 if (self.isNode() && !self.removed()) {
9864 var degree = 0;
9865 var node = self[0];
9866 var connectedEdges = node._private.edges;
9867 for (var i = 0; i < connectedEdges.length; i++) {
9868 var edge = connectedEdges[i];
9869 if (!includeLoops && edge.isLoop()) {
9870 continue;
9871 }
9872 degree += callback(node, edge);
9873 }
9874 return degree;
9875 } else {
9876 return;
9877 }
9878 };
9879 }
9880 extend(elesfn$d, {
9881 degree: defineDegreeFunction(function (node, edge) {
9882 if (edge.source().same(edge.target())) {
9883 return 2;
9884 } else {
9885 return 1;
9886 }
9887 }),
9888 indegree: defineDegreeFunction(function (node, edge) {
9889 if (edge.target().same(node)) {
9890 return 1;
9891 } else {
9892 return 0;
9893 }
9894 }),
9895 outdegree: defineDegreeFunction(function (node, edge) {
9896 if (edge.source().same(node)) {
9897 return 1;
9898 } else {
9899 return 0;
9900 }
9901 })
9902 });
9903 function defineDegreeBoundsFunction(degreeFn, callback) {
9904 return function (includeLoops) {
9905 var ret;
9906 var nodes = this.nodes();
9907 for (var i = 0; i < nodes.length; i++) {
9908 var ele = nodes[i];
9909 var degree = ele[degreeFn](includeLoops);
9910 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
9911 ret = degree;
9912 }
9913 }
9914 return ret;
9915 };
9916 }
9917 extend(elesfn$d, {
9918 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
9919 return degree < min;
9920 }),
9921 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
9922 return degree > max;
9923 }),
9924 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
9925 return degree < min;
9926 }),
9927 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
9928 return degree > max;
9929 }),
9930 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
9931 return degree < min;
9932 }),
9933 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
9934 return degree > max;
9935 })
9936 });
9937 extend(elesfn$d, {
9938 totalDegree: function totalDegree(includeLoops) {
9939 var total = 0;
9940 var nodes = this.nodes();
9941 for (var i = 0; i < nodes.length; i++) {
9942 total += nodes[i].degree(includeLoops);
9943 }
9944 return total;
9945 }
9946 });
9947
9948 var fn$4, elesfn$c;
9949 var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
9950 for (var i = 0; i < eles.length; i++) {
9951 var ele = eles[i];
9952 if (!ele.locked()) {
9953 var oldPos = ele._private.position;
9954 var delta = {
9955 x: newPos.x != null ? newPos.x - oldPos.x : 0,
9956 y: newPos.y != null ? newPos.y - oldPos.y : 0
9957 };
9958 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
9959 ele.children().shift(delta, silent);
9960 }
9961 ele.dirtyBoundingBoxCache();
9962 }
9963 }
9964 };
9965 var positionDef = {
9966 field: 'position',
9967 bindingEvent: 'position',
9968 allowBinding: true,
9969 allowSetting: true,
9970 settingEvent: 'position',
9971 settingTriggersEvent: true,
9972 triggerFnName: 'emitAndNotify',
9973 allowGetting: true,
9974 validKeys: ['x', 'y'],
9975 beforeGet: function beforeGet(ele) {
9976 ele.updateCompoundBounds();
9977 },
9978 beforeSet: function beforeSet(eles, newPos) {
9979 beforePositionSet(eles, newPos, false);
9980 },
9981 onSet: function onSet(eles) {
9982 eles.dirtyCompoundBoundsCache();
9983 },
9984 canSet: function canSet(ele) {
9985 return !ele.locked();
9986 }
9987 };
9988 fn$4 = elesfn$c = {
9989 position: define.data(positionDef),
9990 // position but no notification to renderer
9991 silentPosition: define.data(extend({}, positionDef, {
9992 allowBinding: false,
9993 allowSetting: true,
9994 settingTriggersEvent: false,
9995 allowGetting: false,
9996 beforeSet: function beforeSet(eles, newPos) {
9997 beforePositionSet(eles, newPos, true);
9998 },
9999 onSet: function onSet(eles) {
10000 eles.dirtyCompoundBoundsCache();
10001 }
10002 })),
10003 positions: function positions(pos, silent) {
10004 if (plainObject(pos)) {
10005 if (silent) {
10006 this.silentPosition(pos);
10007 } else {
10008 this.position(pos);
10009 }
10010 } else if (fn$6(pos)) {
10011 var _fn = pos;
10012 var cy = this.cy();
10013 cy.startBatch();
10014 for (var i = 0; i < this.length; i++) {
10015 var ele = this[i];
10016 var _pos = void 0;
10017 if (_pos = _fn(ele, i)) {
10018 if (silent) {
10019 ele.silentPosition(_pos);
10020 } else {
10021 ele.position(_pos);
10022 }
10023 }
10024 }
10025 cy.endBatch();
10026 }
10027 return this; // chaining
10028 },
10029
10030 silentPositions: function silentPositions(pos) {
10031 return this.positions(pos, true);
10032 },
10033 shift: function shift(dim, val, silent) {
10034 var delta;
10035 if (plainObject(dim)) {
10036 delta = {
10037 x: number$1(dim.x) ? dim.x : 0,
10038 y: number$1(dim.y) ? dim.y : 0
10039 };
10040 silent = val;
10041 } else if (string(dim) && number$1(val)) {
10042 delta = {
10043 x: 0,
10044 y: 0
10045 };
10046 delta[dim] = val;
10047 }
10048 if (delta != null) {
10049 var cy = this.cy();
10050 cy.startBatch();
10051 for (var i = 0; i < this.length; i++) {
10052 var ele = this[i];
10053
10054 // exclude any node that is a descendant of the calling collection
10055 if (cy.hasCompoundNodes() && ele.isChild() && ele.ancestors().anySame(this)) {
10056 continue;
10057 }
10058 var pos = ele.position();
10059 var newPos = {
10060 x: pos.x + delta.x,
10061 y: pos.y + delta.y
10062 };
10063 if (silent) {
10064 ele.silentPosition(newPos);
10065 } else {
10066 ele.position(newPos);
10067 }
10068 }
10069 cy.endBatch();
10070 }
10071 return this;
10072 },
10073 silentShift: function silentShift(dim, val) {
10074 if (plainObject(dim)) {
10075 this.shift(dim, true);
10076 } else if (string(dim) && number$1(val)) {
10077 this.shift(dim, val, true);
10078 }
10079 return this;
10080 },
10081 // get/set the rendered (i.e. on screen) positon of the element
10082 renderedPosition: function renderedPosition(dim, val) {
10083 var ele = this[0];
10084 var cy = this.cy();
10085 var zoom = cy.zoom();
10086 var pan = cy.pan();
10087 var rpos = plainObject(dim) ? dim : undefined;
10088 var setting = rpos !== undefined || val !== undefined && string(dim);
10089 if (ele && ele.isNode()) {
10090 // must have an element and must be a node to return position
10091 if (setting) {
10092 for (var i = 0; i < this.length; i++) {
10093 var _ele = this[i];
10094 if (val !== undefined) {
10095 // set one dimension
10096 _ele.position(dim, (val - pan[dim]) / zoom);
10097 } else if (rpos !== undefined) {
10098 // set whole position
10099 _ele.position(renderedToModelPosition(rpos, zoom, pan));
10100 }
10101 }
10102 } else {
10103 // getting
10104 var pos = ele.position();
10105 rpos = modelToRenderedPosition(pos, zoom, pan);
10106 if (dim === undefined) {
10107 // then return the whole rendered position
10108 return rpos;
10109 } else {
10110 // then return the specified dimension
10111 return rpos[dim];
10112 }
10113 }
10114 } else if (!setting) {
10115 return undefined; // for empty collection case
10116 }
10117
10118 return this; // chaining
10119 },
10120
10121 // get/set the position relative to the parent
10122 relativePosition: function relativePosition(dim, val) {
10123 var ele = this[0];
10124 var cy = this.cy();
10125 var ppos = plainObject(dim) ? dim : undefined;
10126 var setting = ppos !== undefined || val !== undefined && string(dim);
10127 var hasCompoundNodes = cy.hasCompoundNodes();
10128 if (ele && ele.isNode()) {
10129 // must have an element and must be a node to return position
10130 if (setting) {
10131 for (var i = 0; i < this.length; i++) {
10132 var _ele2 = this[i];
10133 var parent = hasCompoundNodes ? _ele2.parent() : null;
10134 var hasParent = parent && parent.length > 0;
10135 var relativeToParent = hasParent;
10136 if (hasParent) {
10137 parent = parent[0];
10138 }
10139 var origin = relativeToParent ? parent.position() : {
10140 x: 0,
10141 y: 0
10142 };
10143 if (val !== undefined) {
10144 // set one dimension
10145 _ele2.position(dim, val + origin[dim]);
10146 } else if (ppos !== undefined) {
10147 // set whole position
10148 _ele2.position({
10149 x: ppos.x + origin.x,
10150 y: ppos.y + origin.y
10151 });
10152 }
10153 }
10154 } else {
10155 // getting
10156 var pos = ele.position();
10157 var _parent = hasCompoundNodes ? ele.parent() : null;
10158 var _hasParent = _parent && _parent.length > 0;
10159 var _relativeToParent = _hasParent;
10160 if (_hasParent) {
10161 _parent = _parent[0];
10162 }
10163 var _origin = _relativeToParent ? _parent.position() : {
10164 x: 0,
10165 y: 0
10166 };
10167 ppos = {
10168 x: pos.x - _origin.x,
10169 y: pos.y - _origin.y
10170 };
10171 if (dim === undefined) {
10172 // then return the whole rendered position
10173 return ppos;
10174 } else {
10175 // then return the specified dimension
10176 return ppos[dim];
10177 }
10178 }
10179 } else if (!setting) {
10180 return undefined; // for empty collection case
10181 }
10182
10183 return this; // chaining
10184 }
10185 };
10186
10187 // aliases
10188 fn$4.modelPosition = fn$4.point = fn$4.position;
10189 fn$4.modelPositions = fn$4.points = fn$4.positions;
10190 fn$4.renderedPoint = fn$4.renderedPosition;
10191 fn$4.relativePoint = fn$4.relativePosition;
10192 var position = elesfn$c;
10193
10194 var fn$3, elesfn$b;
10195 fn$3 = elesfn$b = {};
10196 elesfn$b.renderedBoundingBox = function (options) {
10197 var bb = this.boundingBox(options);
10198 var cy = this.cy();
10199 var zoom = cy.zoom();
10200 var pan = cy.pan();
10201 var x1 = bb.x1 * zoom + pan.x;
10202 var x2 = bb.x2 * zoom + pan.x;
10203 var y1 = bb.y1 * zoom + pan.y;
10204 var y2 = bb.y2 * zoom + pan.y;
10205 return {
10206 x1: x1,
10207 x2: x2,
10208 y1: y1,
10209 y2: y2,
10210 w: x2 - x1,
10211 h: y2 - y1
10212 };
10213 };
10214 elesfn$b.dirtyCompoundBoundsCache = function () {
10215 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
10216 var cy = this.cy();
10217 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
10218 return this;
10219 }
10220 this.forEachUp(function (ele) {
10221 if (ele.isParent()) {
10222 var _p = ele._private;
10223 _p.compoundBoundsClean = false;
10224 _p.bbCache = null;
10225 if (!silent) {
10226 ele.emitAndNotify('bounds');
10227 }
10228 }
10229 });
10230 return this;
10231 };
10232 elesfn$b.updateCompoundBounds = function () {
10233 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
10234 var cy = this.cy();
10235
10236 // not possible to do on non-compound graphs or with the style disabled
10237 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
10238 return this;
10239 }
10240
10241 // save cycles when batching -- but bounds will be stale (or not exist yet)
10242 if (!force && cy.batching()) {
10243 return this;
10244 }
10245 function update(parent) {
10246 if (!parent.isParent()) {
10247 return;
10248 }
10249 var _p = parent._private;
10250 var children = parent.children();
10251 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
10252 var min = {
10253 width: {
10254 val: parent.pstyle('min-width').pfValue,
10255 left: parent.pstyle('min-width-bias-left'),
10256 right: parent.pstyle('min-width-bias-right')
10257 },
10258 height: {
10259 val: parent.pstyle('min-height').pfValue,
10260 top: parent.pstyle('min-height-bias-top'),
10261 bottom: parent.pstyle('min-height-bias-bottom')
10262 }
10263 };
10264 var bb = children.boundingBox({
10265 includeLabels: includeLabels,
10266 includeOverlays: false,
10267 // updating the compound bounds happens outside of the regular
10268 // cache cycle (i.e. before fired events)
10269 useCache: false
10270 });
10271 var pos = _p.position;
10272
10273 // if children take up zero area then keep position and fall back on stylesheet w/h
10274 if (bb.w === 0 || bb.h === 0) {
10275 bb = {
10276 w: parent.pstyle('width').pfValue,
10277 h: parent.pstyle('height').pfValue
10278 };
10279 bb.x1 = pos.x - bb.w / 2;
10280 bb.x2 = pos.x + bb.w / 2;
10281 bb.y1 = pos.y - bb.h / 2;
10282 bb.y2 = pos.y + bb.h / 2;
10283 }
10284 function computeBiasValues(propDiff, propBias, propBiasComplement) {
10285 var biasDiff = 0;
10286 var biasComplementDiff = 0;
10287 var biasTotal = propBias + propBiasComplement;
10288 if (propDiff > 0 && biasTotal > 0) {
10289 biasDiff = propBias / biasTotal * propDiff;
10290 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
10291 }
10292 return {
10293 biasDiff: biasDiff,
10294 biasComplementDiff: biasComplementDiff
10295 };
10296 }
10297 function computePaddingValues(width, height, paddingObject, relativeTo) {
10298 // Assuming percentage is number from 0 to 1
10299 if (paddingObject.units === '%') {
10300 switch (relativeTo) {
10301 case 'width':
10302 return width > 0 ? paddingObject.pfValue * width : 0;
10303 case 'height':
10304 return height > 0 ? paddingObject.pfValue * height : 0;
10305 case 'average':
10306 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
10307 case 'min':
10308 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
10309 case 'max':
10310 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
10311 default:
10312 return 0;
10313 }
10314 } else if (paddingObject.units === 'px') {
10315 return paddingObject.pfValue;
10316 } else {
10317 return 0;
10318 }
10319 }
10320 var leftVal = min.width.left.value;
10321 if (min.width.left.units === 'px' && min.width.val > 0) {
10322 leftVal = leftVal * 100 / min.width.val;
10323 }
10324 var rightVal = min.width.right.value;
10325 if (min.width.right.units === 'px' && min.width.val > 0) {
10326 rightVal = rightVal * 100 / min.width.val;
10327 }
10328 var topVal = min.height.top.value;
10329 if (min.height.top.units === 'px' && min.height.val > 0) {
10330 topVal = topVal * 100 / min.height.val;
10331 }
10332 var bottomVal = min.height.bottom.value;
10333 if (min.height.bottom.units === 'px' && min.height.val > 0) {
10334 bottomVal = bottomVal * 100 / min.height.val;
10335 }
10336 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
10337 var diffLeft = widthBiasDiffs.biasDiff;
10338 var diffRight = widthBiasDiffs.biasComplementDiff;
10339 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
10340 var diffTop = heightBiasDiffs.biasDiff;
10341 var diffBottom = heightBiasDiffs.biasComplementDiff;
10342 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
10343 _p.autoWidth = Math.max(bb.w, min.width.val);
10344 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
10345 _p.autoHeight = Math.max(bb.h, min.height.val);
10346 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
10347 }
10348 for (var i = 0; i < this.length; i++) {
10349 var ele = this[i];
10350 var _p = ele._private;
10351 if (!_p.compoundBoundsClean || force) {
10352 update(ele);
10353 if (!cy.batching()) {
10354 _p.compoundBoundsClean = true;
10355 }
10356 }
10357 }
10358 return this;
10359 };
10360 var noninf = function noninf(x) {
10361 if (x === Infinity || x === -Infinity) {
10362 return 0;
10363 }
10364 return x;
10365 };
10366 var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
10367 // don't update with zero area boxes
10368 if (x2 - x1 === 0 || y2 - y1 === 0) {
10369 return;
10370 }
10371
10372 // don't update with null dim
10373 if (x1 == null || y1 == null || x2 == null || y2 == null) {
10374 return;
10375 }
10376 b.x1 = x1 < b.x1 ? x1 : b.x1;
10377 b.x2 = x2 > b.x2 ? x2 : b.x2;
10378 b.y1 = y1 < b.y1 ? y1 : b.y1;
10379 b.y2 = y2 > b.y2 ? y2 : b.y2;
10380 b.w = b.x2 - b.x1;
10381 b.h = b.y2 - b.y1;
10382 };
10383 var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
10384 if (b2 == null) {
10385 return b;
10386 }
10387 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
10388 };
10389 var prefixedProperty = function prefixedProperty(obj, field, prefix) {
10390 return getPrefixedProperty(obj, field, prefix);
10391 };
10392 var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
10393 if (ele.cy().headless()) {
10394 return;
10395 }
10396 var _p = ele._private;
10397 var rstyle = _p.rstyle;
10398 var halfArW = rstyle.arrowWidth / 2;
10399 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
10400 var x;
10401 var y;
10402 if (arrowType !== 'none') {
10403 if (prefix === 'source') {
10404 x = rstyle.srcX;
10405 y = rstyle.srcY;
10406 } else if (prefix === 'target') {
10407 x = rstyle.tgtX;
10408 y = rstyle.tgtY;
10409 } else {
10410 x = rstyle.midX;
10411 y = rstyle.midY;
10412 }
10413
10414 // always store the individual arrow bounds
10415 var bbs = _p.arrowBounds = _p.arrowBounds || {};
10416 var bb = bbs[prefix] = bbs[prefix] || {};
10417 bb.x1 = x - halfArW;
10418 bb.y1 = y - halfArW;
10419 bb.x2 = x + halfArW;
10420 bb.y2 = y + halfArW;
10421 bb.w = bb.x2 - bb.x1;
10422 bb.h = bb.y2 - bb.y1;
10423 expandBoundingBox(bb, 1);
10424 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
10425 }
10426 };
10427 var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
10428 if (ele.cy().headless()) {
10429 return;
10430 }
10431 var prefixDash;
10432 if (prefix) {
10433 prefixDash = prefix + '-';
10434 } else {
10435 prefixDash = '';
10436 }
10437 var _p = ele._private;
10438 var rstyle = _p.rstyle;
10439 var label = ele.pstyle(prefixDash + 'label').strValue;
10440 if (label) {
10441 var halign = ele.pstyle('text-halign');
10442 var valign = ele.pstyle('text-valign');
10443 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
10444 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
10445 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
10446 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
10447 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
10448 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
10449 var isEdge = ele.isEdge();
10450 var rotation = ele.pstyle(prefixDash + 'text-rotation');
10451 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
10452 var borderWidth = ele.pstyle('text-border-width').pfValue;
10453 var halfBorderWidth = borderWidth / 2;
10454 var padding = ele.pstyle('text-background-padding').pfValue;
10455 var marginOfError = 2; // expand to work around browser dimension inaccuracies
10456
10457 var lh = labelHeight;
10458 var lw = labelWidth;
10459 var lw_2 = lw / 2;
10460 var lh_2 = lh / 2;
10461 var lx1, lx2, ly1, ly2;
10462 if (isEdge) {
10463 lx1 = labelX - lw_2;
10464 lx2 = labelX + lw_2;
10465 ly1 = labelY - lh_2;
10466 ly2 = labelY + lh_2;
10467 } else {
10468 switch (halign.value) {
10469 case 'left':
10470 lx1 = labelX - lw;
10471 lx2 = labelX;
10472 break;
10473 case 'center':
10474 lx1 = labelX - lw_2;
10475 lx2 = labelX + lw_2;
10476 break;
10477 case 'right':
10478 lx1 = labelX;
10479 lx2 = labelX + lw;
10480 break;
10481 }
10482 switch (valign.value) {
10483 case 'top':
10484 ly1 = labelY - lh;
10485 ly2 = labelY;
10486 break;
10487 case 'center':
10488 ly1 = labelY - lh_2;
10489 ly2 = labelY + lh_2;
10490 break;
10491 case 'bottom':
10492 ly1 = labelY;
10493 ly2 = labelY + lh;
10494 break;
10495 }
10496 }
10497
10498 // shift by margin and expand by outline and border
10499 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10500 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10501 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
10502 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
10503
10504 // always store the unrotated label bounds separately
10505 var bbPrefix = prefix || 'main';
10506 var bbs = _p.labelBounds;
10507 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
10508 bb.x1 = lx1;
10509 bb.y1 = ly1;
10510 bb.x2 = lx2;
10511 bb.y2 = ly2;
10512 bb.w = lx2 - lx1;
10513 bb.h = ly2 - ly1;
10514 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
10515 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
10516 if (isAutorotate || isPfValue) {
10517 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
10518 var cos = Math.cos(theta);
10519 var sin = Math.sin(theta);
10520
10521 // rotation point (default value for center-center)
10522 var xo = (lx1 + lx2) / 2;
10523 var yo = (ly1 + ly2) / 2;
10524 if (!isEdge) {
10525 switch (halign.value) {
10526 case 'left':
10527 xo = lx2;
10528 break;
10529 case 'right':
10530 xo = lx1;
10531 break;
10532 }
10533 switch (valign.value) {
10534 case 'top':
10535 yo = ly2;
10536 break;
10537 case 'bottom':
10538 yo = ly1;
10539 break;
10540 }
10541 }
10542 var rotate = function rotate(x, y) {
10543 x = x - xo;
10544 y = y - yo;
10545 return {
10546 x: x * cos - y * sin + xo,
10547 y: x * sin + y * cos + yo
10548 };
10549 };
10550 var px1y1 = rotate(lx1, ly1);
10551 var px1y2 = rotate(lx1, ly2);
10552 var px2y1 = rotate(lx2, ly1);
10553 var px2y2 = rotate(lx2, ly2);
10554 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10555 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
10556 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10557 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
10558 }
10559 var bbPrefixRot = bbPrefix + 'Rot';
10560 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
10561 bbRot.x1 = lx1;
10562 bbRot.y1 = ly1;
10563 bbRot.x2 = lx2;
10564 bbRot.y2 = ly2;
10565 bbRot.w = lx2 - lx1;
10566 bbRot.h = ly2 - ly1;
10567 updateBounds(bounds, lx1, ly1, lx2, ly2);
10568 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
10569 }
10570 return bounds;
10571 };
10572 var updateBoundsFromOutline = function updateBoundsFromOutline(bounds, ele) {
10573 if (ele.cy().headless()) {
10574 return;
10575 }
10576 var outlineOpacity = ele.pstyle('outline-opacity').value;
10577 var outlineWidth = ele.pstyle('outline-width').value;
10578 if (outlineOpacity > 0 && outlineWidth > 0) {
10579 var outlineOffset = ele.pstyle('outline-offset').value;
10580 var nodeShape = ele.pstyle('shape').value;
10581 var outlineSize = outlineWidth + outlineOffset;
10582 var scaleX = (bounds.w + outlineSize * 2) / bounds.w;
10583 var scaleY = (bounds.h + outlineSize * 2) / bounds.h;
10584 var xOffset = 0;
10585 var yOffset = 0;
10586 if (["diamond", "pentagon", "round-triangle"].includes(nodeShape)) {
10587 scaleX = (bounds.w + outlineSize * 2.4) / bounds.w;
10588 yOffset = -outlineSize / 3.6;
10589 } else if (["concave-hexagon", "rhomboid", "right-rhomboid"].includes(nodeShape)) {
10590 scaleX = (bounds.w + outlineSize * 2.4) / bounds.w;
10591 } else if (nodeShape === "star") {
10592 scaleX = (bounds.w + outlineSize * 2.8) / bounds.w;
10593 scaleY = (bounds.h + outlineSize * 2.6) / bounds.h;
10594 yOffset = -outlineSize / 3.8;
10595 } else if (nodeShape === "triangle") {
10596 scaleX = (bounds.w + outlineSize * 2.8) / bounds.w;
10597 scaleY = (bounds.h + outlineSize * 2.4) / bounds.h;
10598 yOffset = -outlineSize / 1.4;
10599 } else if (nodeShape === "vee") {
10600 scaleX = (bounds.w + outlineSize * 4.4) / bounds.w;
10601 scaleY = (bounds.h + outlineSize * 3.8) / bounds.h;
10602 yOffset = -outlineSize * .5;
10603 }
10604 var hDelta = bounds.h * scaleY - bounds.h;
10605 var wDelta = bounds.w * scaleX - bounds.w;
10606 expandBoundingBoxSides(bounds, [Math.ceil(hDelta / 2), Math.ceil(wDelta / 2)]);
10607 if (xOffset != 0 || yOffset !== 0) {
10608 var oBounds = shiftBoundingBox(bounds, xOffset, yOffset);
10609 updateBoundingBox(bounds, oBounds);
10610 }
10611 }
10612 };
10613
10614 // get the bounding box of the elements (in raw model position)
10615 var boundingBoxImpl = function boundingBoxImpl(ele, options) {
10616 var cy = ele._private.cy;
10617 var styleEnabled = cy.styleEnabled();
10618 var headless = cy.headless();
10619 var bounds = makeBoundingBox();
10620 var _p = ele._private;
10621 var isNode = ele.isNode();
10622 var isEdge = ele.isEdge();
10623 var ex1, ex2, ey1, ey2; // extrema of body / lines
10624 var x, y; // node pos
10625 var rstyle = _p.rstyle;
10626 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0];
10627
10628 // must use `display` prop only, as reading `compound.width()` causes recursion
10629 // (other factors like width values will be considered later in this function anyway)
10630 var isDisplayed = function isDisplayed(ele) {
10631 return ele.pstyle('display').value !== 'none';
10632 };
10633 var displayed = !styleEnabled || isDisplayed(ele)
10634
10635 // must take into account connected nodes b/c of implicit edge hiding on display:none node
10636 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
10637 if (displayed) {
10638 // displayed suffices, since we will find zero area eles anyway
10639 var overlayOpacity = 0;
10640 var overlayPadding = 0;
10641 if (styleEnabled && options.includeOverlays) {
10642 overlayOpacity = ele.pstyle('overlay-opacity').value;
10643 if (overlayOpacity !== 0) {
10644 overlayPadding = ele.pstyle('overlay-padding').value;
10645 }
10646 }
10647 var underlayOpacity = 0;
10648 var underlayPadding = 0;
10649 if (styleEnabled && options.includeUnderlays) {
10650 underlayOpacity = ele.pstyle('underlay-opacity').value;
10651 if (underlayOpacity !== 0) {
10652 underlayPadding = ele.pstyle('underlay-padding').value;
10653 }
10654 }
10655 var padding = Math.max(overlayPadding, underlayPadding);
10656 var w = 0;
10657 var wHalf = 0;
10658 if (styleEnabled) {
10659 w = ele.pstyle('width').pfValue;
10660 wHalf = w / 2;
10661 }
10662 if (isNode && options.includeNodes) {
10663 var pos = ele.position();
10664 x = pos.x;
10665 y = pos.y;
10666 var _w = ele.outerWidth();
10667 var halfW = _w / 2;
10668 var h = ele.outerHeight();
10669 var halfH = h / 2;
10670
10671 // handle node dimensions
10672 /////////////////////////
10673
10674 ex1 = x - halfW;
10675 ex2 = x + halfW;
10676 ey1 = y - halfH;
10677 ey2 = y + halfH;
10678 updateBounds(bounds, ex1, ey1, ex2, ey2);
10679 if (styleEnabled && options.includeOutlines) {
10680 updateBoundsFromOutline(bounds, ele);
10681 }
10682 } else if (isEdge && options.includeEdges) {
10683 if (styleEnabled && !headless) {
10684 var curveStyle = ele.pstyle('curve-style').strValue;
10685
10686 // handle edge dimensions (rough box estimate)
10687 //////////////////////////////////////////////
10688
10689 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10690 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
10691 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10692 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY);
10693
10694 // take into account edge width
10695 ex1 -= wHalf;
10696 ex2 += wHalf;
10697 ey1 -= wHalf;
10698 ey2 += wHalf;
10699 updateBounds(bounds, ex1, ey1, ex2, ey2);
10700
10701 // precise edges
10702 ////////////////
10703
10704 if (curveStyle === 'haystack') {
10705 var hpts = rstyle.haystackPts;
10706 if (hpts && hpts.length === 2) {
10707 ex1 = hpts[0].x;
10708 ey1 = hpts[0].y;
10709 ex2 = hpts[1].x;
10710 ey2 = hpts[1].y;
10711 if (ex1 > ex2) {
10712 var temp = ex1;
10713 ex1 = ex2;
10714 ex2 = temp;
10715 }
10716 if (ey1 > ey2) {
10717 var _temp = ey1;
10718 ey1 = ey2;
10719 ey2 = _temp;
10720 }
10721 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
10722 }
10723 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle.endsWith('segments') || curveStyle.endsWith('taxi')) {
10724 var pts;
10725 switch (curveStyle) {
10726 case 'bezier':
10727 case 'unbundled-bezier':
10728 pts = rstyle.bezierPts;
10729 break;
10730 case 'segments':
10731 case 'taxi':
10732 case 'round-segments':
10733 case 'round-taxi':
10734 pts = rstyle.linePts;
10735 break;
10736 }
10737 if (pts != null) {
10738 for (var j = 0; j < pts.length; j++) {
10739 var pt = pts[j];
10740 ex1 = pt.x - wHalf;
10741 ex2 = pt.x + wHalf;
10742 ey1 = pt.y - wHalf;
10743 ey2 = pt.y + wHalf;
10744 updateBounds(bounds, ex1, ey1, ex2, ey2);
10745 }
10746 }
10747 } // bezier-like or segment-like edge
10748 } else {
10749 // headless or style disabled
10750
10751 // fallback on source and target positions
10752 //////////////////////////////////////////
10753
10754 var n1 = ele.source();
10755 var n1pos = n1.position();
10756 var n2 = ele.target();
10757 var n2pos = n2.position();
10758 ex1 = n1pos.x;
10759 ex2 = n2pos.x;
10760 ey1 = n1pos.y;
10761 ey2 = n2pos.y;
10762 if (ex1 > ex2) {
10763 var _temp2 = ex1;
10764 ex1 = ex2;
10765 ex2 = _temp2;
10766 }
10767 if (ey1 > ey2) {
10768 var _temp3 = ey1;
10769 ey1 = ey2;
10770 ey2 = _temp3;
10771 }
10772
10773 // take into account edge width
10774 ex1 -= wHalf;
10775 ex2 += wHalf;
10776 ey1 -= wHalf;
10777 ey2 += wHalf;
10778 updateBounds(bounds, ex1, ey1, ex2, ey2);
10779 } // headless or style disabled
10780 } // edges
10781
10782 // handle edge arrow size
10783 /////////////////////////
10784
10785 if (styleEnabled && options.includeEdges && isEdge) {
10786 updateBoundsFromArrow(bounds, ele, 'mid-source');
10787 updateBoundsFromArrow(bounds, ele, 'mid-target');
10788 updateBoundsFromArrow(bounds, ele, 'source');
10789 updateBoundsFromArrow(bounds, ele, 'target');
10790 }
10791
10792 // ghost
10793 ////////
10794
10795 if (styleEnabled) {
10796 var ghost = ele.pstyle('ghost').value === 'yes';
10797 if (ghost) {
10798 var gx = ele.pstyle('ghost-offset-x').pfValue;
10799 var gy = ele.pstyle('ghost-offset-y').pfValue;
10800 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
10801 }
10802 }
10803
10804 // always store the body bounds separately from the labels
10805 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
10806 assignBoundingBox(bbBody, bounds);
10807 expandBoundingBoxSides(bbBody, manualExpansion);
10808 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
10809
10810 // overlay
10811 //////////
10812
10813 if (styleEnabled) {
10814 ex1 = bounds.x1;
10815 ex2 = bounds.x2;
10816 ey1 = bounds.y1;
10817 ey2 = bounds.y2;
10818 updateBounds(bounds, ex1 - padding, ey1 - padding, ex2 + padding, ey2 + padding);
10819 }
10820
10821 // always store the body bounds separately from the labels
10822 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
10823 assignBoundingBox(bbOverlay, bounds);
10824 expandBoundingBoxSides(bbOverlay, manualExpansion);
10825 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
10826
10827 // handle label dimensions
10828 //////////////////////////
10829
10830 var bbLabels = _p.labelBounds = _p.labelBounds || {};
10831 if (bbLabels.all != null) {
10832 clearBoundingBox(bbLabels.all);
10833 } else {
10834 bbLabels.all = makeBoundingBox();
10835 }
10836 if (styleEnabled && options.includeLabels) {
10837 if (options.includeMainLabels) {
10838 updateBoundsFromLabel(bounds, ele, null);
10839 }
10840 if (isEdge) {
10841 if (options.includeSourceLabels) {
10842 updateBoundsFromLabel(bounds, ele, 'source');
10843 }
10844 if (options.includeTargetLabels) {
10845 updateBoundsFromLabel(bounds, ele, 'target');
10846 }
10847 }
10848 } // style enabled for labels
10849 } // if displayed
10850
10851 bounds.x1 = noninf(bounds.x1);
10852 bounds.y1 = noninf(bounds.y1);
10853 bounds.x2 = noninf(bounds.x2);
10854 bounds.y2 = noninf(bounds.y2);
10855 bounds.w = noninf(bounds.x2 - bounds.x1);
10856 bounds.h = noninf(bounds.y2 - bounds.y1);
10857 if (bounds.w > 0 && bounds.h > 0 && displayed) {
10858 expandBoundingBoxSides(bounds, manualExpansion);
10859
10860 // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
10861 expandBoundingBox(bounds, 1);
10862 }
10863 return bounds;
10864 };
10865 var getKey = function getKey(opts) {
10866 var i = 0;
10867 var tf = function tf(val) {
10868 return (val ? 1 : 0) << i++;
10869 };
10870 var key = 0;
10871 key += tf(opts.incudeNodes);
10872 key += tf(opts.includeEdges);
10873 key += tf(opts.includeLabels);
10874 key += tf(opts.includeMainLabels);
10875 key += tf(opts.includeSourceLabels);
10876 key += tf(opts.includeTargetLabels);
10877 key += tf(opts.includeOverlays);
10878 key += tf(opts.includeOutlines);
10879 return key;
10880 };
10881 var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
10882 if (ele.isEdge()) {
10883 var p1 = ele.source().position();
10884 var p2 = ele.target().position();
10885 var r = function r(x) {
10886 return Math.round(x);
10887 };
10888 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
10889 } else {
10890 return 0;
10891 }
10892 };
10893 var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
10894 var _p = ele._private;
10895 var bb;
10896 var isEdge = ele.isEdge();
10897 var key = opts == null ? defBbOptsKey : getKey(opts);
10898 var usingDefOpts = key === defBbOptsKey;
10899 var currPosKey = getBoundingBoxPosKey(ele);
10900 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10901 var useCache = opts.useCache && isPosKeySame;
10902 var isDirty = function isDirty(ele) {
10903 return ele._private.bbCache == null || ele._private.styleDirty;
10904 };
10905 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
10906 if (needRecalc) {
10907 if (!isPosKeySame) {
10908 ele.recalculateRenderedStyle(useCache);
10909 }
10910 bb = boundingBoxImpl(ele, defBbOpts);
10911 _p.bbCache = bb;
10912 _p.bbCachePosKey = currPosKey;
10913 } else {
10914 bb = _p.bbCache;
10915 }
10916
10917 // not using def opts => need to build up bb from combination of sub bbs
10918 if (!usingDefOpts) {
10919 var isNode = ele.isNode();
10920 bb = makeBoundingBox();
10921 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
10922 if (opts.includeOverlays) {
10923 updateBoundsFromBox(bb, _p.overlayBounds);
10924 } else {
10925 updateBoundsFromBox(bb, _p.bodyBounds);
10926 }
10927 }
10928 if (opts.includeLabels) {
10929 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
10930 updateBoundsFromBox(bb, _p.labelBounds.all);
10931 } else {
10932 if (opts.includeMainLabels) {
10933 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
10934 }
10935 if (opts.includeSourceLabels) {
10936 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
10937 }
10938 if (opts.includeTargetLabels) {
10939 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
10940 }
10941 }
10942 }
10943 bb.w = bb.x2 - bb.x1;
10944 bb.h = bb.y2 - bb.y1;
10945 }
10946 return bb;
10947 };
10948 var defBbOpts = {
10949 includeNodes: true,
10950 includeEdges: true,
10951 includeLabels: true,
10952 includeMainLabels: true,
10953 includeSourceLabels: true,
10954 includeTargetLabels: true,
10955 includeOverlays: true,
10956 includeUnderlays: true,
10957 includeOutlines: true,
10958 useCache: true
10959 };
10960 var defBbOptsKey = getKey(defBbOpts);
10961 var filledBbOpts = defaults$g(defBbOpts);
10962 elesfn$b.boundingBox = function (options) {
10963 var bounds;
10964
10965 // the main usecase is ele.boundingBox() for a single element with no/def options
10966 // specified s.t. the cache is used, so check for this case to make it faster by
10967 // avoiding the overhead of the rest of the function
10968 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
10969 if (options === undefined) {
10970 options = defBbOpts;
10971 } else {
10972 options = filledBbOpts(options);
10973 }
10974 bounds = cachedBoundingBoxImpl(this[0], options);
10975 } else {
10976 bounds = makeBoundingBox();
10977 options = options || defBbOpts;
10978 var opts = filledBbOpts(options);
10979 var eles = this;
10980 var cy = eles.cy();
10981 var styleEnabled = cy.styleEnabled();
10982 if (styleEnabled) {
10983 for (var i = 0; i < eles.length; i++) {
10984 var ele = eles[i];
10985 var _p = ele._private;
10986 var currPosKey = getBoundingBoxPosKey(ele);
10987 var isPosKeySame = _p.bbCachePosKey === currPosKey;
10988 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
10989 ele.recalculateRenderedStyle(useCache);
10990 }
10991 }
10992 this.updateCompoundBounds(!options.useCache);
10993 for (var _i = 0; _i < eles.length; _i++) {
10994 var _ele = eles[_i];
10995 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
10996 }
10997 }
10998 bounds.x1 = noninf(bounds.x1);
10999 bounds.y1 = noninf(bounds.y1);
11000 bounds.x2 = noninf(bounds.x2);
11001 bounds.y2 = noninf(bounds.y2);
11002 bounds.w = noninf(bounds.x2 - bounds.x1);
11003 bounds.h = noninf(bounds.y2 - bounds.y1);
11004 return bounds;
11005 };
11006 elesfn$b.dirtyBoundingBoxCache = function () {
11007 for (var i = 0; i < this.length; i++) {
11008 var _p = this[i]._private;
11009 _p.bbCache = null;
11010 _p.bbCachePosKey = null;
11011 _p.bodyBounds = null;
11012 _p.overlayBounds = null;
11013 _p.labelBounds.all = null;
11014 _p.labelBounds.source = null;
11015 _p.labelBounds.target = null;
11016 _p.labelBounds.main = null;
11017 _p.labelBounds.sourceRot = null;
11018 _p.labelBounds.targetRot = null;
11019 _p.labelBounds.mainRot = null;
11020 _p.arrowBounds.source = null;
11021 _p.arrowBounds.target = null;
11022 _p.arrowBounds['mid-source'] = null;
11023 _p.arrowBounds['mid-target'] = null;
11024 }
11025 this.emitAndNotify('bounds');
11026 return this;
11027 };
11028
11029 // private helper to get bounding box for custom node positions
11030 // - good for perf in certain cases but currently requires dirtying the rendered style
11031 // - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
11032 // - try to use for only things like discrete layouts where the node position would change anyway
11033 elesfn$b.boundingBoxAt = function (fn) {
11034 var nodes = this.nodes();
11035 var cy = this.cy();
11036 var hasCompoundNodes = cy.hasCompoundNodes();
11037 var parents = cy.collection();
11038 if (hasCompoundNodes) {
11039 parents = nodes.filter(function (node) {
11040 return node.isParent();
11041 });
11042 nodes = nodes.not(parents);
11043 }
11044 if (plainObject(fn)) {
11045 var obj = fn;
11046 fn = function fn() {
11047 return obj;
11048 };
11049 }
11050 var storeOldPos = function storeOldPos(node, i) {
11051 return node._private.bbAtOldPos = fn(node, i);
11052 };
11053 var getOldPos = function getOldPos(node) {
11054 return node._private.bbAtOldPos;
11055 };
11056 cy.startBatch();
11057 nodes.forEach(storeOldPos).silentPositions(fn);
11058 if (hasCompoundNodes) {
11059 parents.dirtyCompoundBoundsCache();
11060 parents.dirtyBoundingBoxCache();
11061 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
11062 }
11063
11064 var bb = copyBoundingBox(this.boundingBox({
11065 useCache: false
11066 }));
11067 nodes.silentPositions(getOldPos);
11068 if (hasCompoundNodes) {
11069 parents.dirtyCompoundBoundsCache();
11070 parents.dirtyBoundingBoxCache();
11071 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
11072 }
11073
11074 cy.endBatch();
11075 return bb;
11076 };
11077 fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
11078 fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
11079 var bounds = elesfn$b;
11080
11081 var fn$2, elesfn$a;
11082 fn$2 = elesfn$a = {};
11083 var defineDimFns = function defineDimFns(opts) {
11084 opts.uppercaseName = capitalize(opts.name);
11085 opts.autoName = 'auto' + opts.uppercaseName;
11086 opts.labelName = 'label' + opts.uppercaseName;
11087 opts.outerName = 'outer' + opts.uppercaseName;
11088 opts.uppercaseOuterName = capitalize(opts.outerName);
11089 fn$2[opts.name] = function dimImpl() {
11090 var ele = this[0];
11091 var _p = ele._private;
11092 var cy = _p.cy;
11093 var styleEnabled = cy._private.styleEnabled;
11094 if (ele) {
11095 if (styleEnabled) {
11096 if (ele.isParent()) {
11097 ele.updateCompoundBounds();
11098 return _p[opts.autoName] || 0;
11099 }
11100 var d = ele.pstyle(opts.name);
11101 switch (d.strValue) {
11102 case 'label':
11103 ele.recalculateRenderedStyle();
11104 return _p.rstyle[opts.labelName] || 0;
11105 default:
11106 return d.pfValue;
11107 }
11108 } else {
11109 return 1;
11110 }
11111 }
11112 };
11113 fn$2['outer' + opts.uppercaseName] = function outerDimImpl() {
11114 var ele = this[0];
11115 var _p = ele._private;
11116 var cy = _p.cy;
11117 var styleEnabled = cy._private.styleEnabled;
11118 if (ele) {
11119 if (styleEnabled) {
11120 var dim = ele[opts.name]();
11121 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
11122 var padding = 2 * ele.padding();
11123 return dim + border + padding;
11124 } else {
11125 return 1;
11126 }
11127 }
11128 };
11129 fn$2['rendered' + opts.uppercaseName] = function renderedDimImpl() {
11130 var ele = this[0];
11131 if (ele) {
11132 var d = ele[opts.name]();
11133 return d * this.cy().zoom();
11134 }
11135 };
11136 fn$2['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
11137 var ele = this[0];
11138 if (ele) {
11139 var od = ele[opts.outerName]();
11140 return od * this.cy().zoom();
11141 }
11142 };
11143 };
11144 defineDimFns({
11145 name: 'width'
11146 });
11147 defineDimFns({
11148 name: 'height'
11149 });
11150 elesfn$a.padding = function () {
11151 var ele = this[0];
11152 var _p = ele._private;
11153 if (ele.isParent()) {
11154 ele.updateCompoundBounds();
11155 if (_p.autoPadding !== undefined) {
11156 return _p.autoPadding;
11157 } else {
11158 return ele.pstyle('padding').pfValue;
11159 }
11160 } else {
11161 return ele.pstyle('padding').pfValue;
11162 }
11163 };
11164 elesfn$a.paddedHeight = function () {
11165 var ele = this[0];
11166 return ele.height() + 2 * ele.padding();
11167 };
11168 elesfn$a.paddedWidth = function () {
11169 var ele = this[0];
11170 return ele.width() + 2 * ele.padding();
11171 };
11172 var widthHeight = elesfn$a;
11173
11174 var ifEdge = function ifEdge(ele, getValue) {
11175 if (ele.isEdge()) {
11176 return getValue(ele);
11177 }
11178 };
11179 var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
11180 if (ele.isEdge()) {
11181 var cy = ele.cy();
11182 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
11183 }
11184 };
11185 var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
11186 if (ele.isEdge()) {
11187 var cy = ele.cy();
11188 var pan = cy.pan();
11189 var zoom = cy.zoom();
11190 return getPoints(ele).map(function (p) {
11191 return modelToRenderedPosition(p, zoom, pan);
11192 });
11193 }
11194 };
11195 var controlPoints = function controlPoints(ele) {
11196 return ele.renderer().getControlPoints(ele);
11197 };
11198 var segmentPoints = function segmentPoints(ele) {
11199 return ele.renderer().getSegmentPoints(ele);
11200 };
11201 var sourceEndpoint = function sourceEndpoint(ele) {
11202 return ele.renderer().getSourceEndpoint(ele);
11203 };
11204 var targetEndpoint = function targetEndpoint(ele) {
11205 return ele.renderer().getTargetEndpoint(ele);
11206 };
11207 var midpoint = function midpoint(ele) {
11208 return ele.renderer().getEdgeMidpoint(ele);
11209 };
11210 var pts = {
11211 controlPoints: {
11212 get: controlPoints,
11213 mult: true
11214 },
11215 segmentPoints: {
11216 get: segmentPoints,
11217 mult: true
11218 },
11219 sourceEndpoint: {
11220 get: sourceEndpoint
11221 },
11222 targetEndpoint: {
11223 get: targetEndpoint
11224 },
11225 midpoint: {
11226 get: midpoint
11227 }
11228 };
11229 var renderedName = function renderedName(name) {
11230 return 'rendered' + name[0].toUpperCase() + name.substr(1);
11231 };
11232 var edgePoints = Object.keys(pts).reduce(function (obj, name) {
11233 var spec = pts[name];
11234 var rName = renderedName(name);
11235 obj[name] = function () {
11236 return ifEdge(this, spec.get);
11237 };
11238 if (spec.mult) {
11239 obj[rName] = function () {
11240 return ifEdgeRenderedPositions(this, spec.get);
11241 };
11242 } else {
11243 obj[rName] = function () {
11244 return ifEdgeRenderedPosition(this, spec.get);
11245 };
11246 }
11247 return obj;
11248 }, {});
11249
11250 var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
11251
11252 /*!
11253 Event object based on jQuery events, MIT license
11254
11255 https://jquery.org/license/
11256 https://tldrlegal.com/license/mit-license
11257 https://github.com/jquery/jquery/blob/master/src/event.js
11258 */
11259
11260 var Event = function Event(src, props) {
11261 this.recycle(src, props);
11262 };
11263 function returnFalse() {
11264 return false;
11265 }
11266 function returnTrue() {
11267 return true;
11268 }
11269
11270 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
11271 Event.prototype = {
11272 instanceString: function instanceString() {
11273 return 'event';
11274 },
11275 recycle: function recycle(src, props) {
11276 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
11277 if (src != null && src.preventDefault) {
11278 // Browser Event object
11279 this.type = src.type;
11280
11281 // Events bubbling up the document may have been marked as prevented
11282 // by a handler lower down the tree; reflect the correct value.
11283 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
11284 } else if (src != null && src.type) {
11285 // Plain object containing all event details
11286 props = src;
11287 } else {
11288 // Event string
11289 this.type = src;
11290 }
11291
11292 // Put explicitly provided properties onto the event object
11293 if (props != null) {
11294 // more efficient to manually copy fields we use
11295 this.originalEvent = props.originalEvent;
11296 this.type = props.type != null ? props.type : this.type;
11297 this.cy = props.cy;
11298 this.target = props.target;
11299 this.position = props.position;
11300 this.renderedPosition = props.renderedPosition;
11301 this.namespace = props.namespace;
11302 this.layout = props.layout;
11303 }
11304 if (this.cy != null && this.position != null && this.renderedPosition == null) {
11305 // create a rendered position based on the passed position
11306 var pos = this.position;
11307 var zoom = this.cy.zoom();
11308 var pan = this.cy.pan();
11309 this.renderedPosition = {
11310 x: pos.x * zoom + pan.x,
11311 y: pos.y * zoom + pan.y
11312 };
11313 }
11314
11315 // Create a timestamp if incoming event doesn't have one
11316 this.timeStamp = src && src.timeStamp || Date.now();
11317 },
11318 preventDefault: function preventDefault() {
11319 this.isDefaultPrevented = returnTrue;
11320 var e = this.originalEvent;
11321 if (!e) {
11322 return;
11323 }
11324
11325 // if preventDefault exists run it on the original event
11326 if (e.preventDefault) {
11327 e.preventDefault();
11328 }
11329 },
11330 stopPropagation: function stopPropagation() {
11331 this.isPropagationStopped = returnTrue;
11332 var e = this.originalEvent;
11333 if (!e) {
11334 return;
11335 }
11336
11337 // if stopPropagation exists run it on the original event
11338 if (e.stopPropagation) {
11339 e.stopPropagation();
11340 }
11341 },
11342 stopImmediatePropagation: function stopImmediatePropagation() {
11343 this.isImmediatePropagationStopped = returnTrue;
11344 this.stopPropagation();
11345 },
11346 isDefaultPrevented: returnFalse,
11347 isPropagationStopped: returnFalse,
11348 isImmediatePropagationStopped: returnFalse
11349 };
11350
11351 var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
11352 var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
11353
11354 var defaults$8 = {
11355 qualifierCompare: function qualifierCompare(q1, q2) {
11356 return q1 === q2;
11357 },
11358 eventMatches: function eventMatches( /*context, listener, eventObj*/
11359 ) {
11360 return true;
11361 },
11362 addEventFields: function addEventFields( /*context, evt*/
11363 ) {},
11364 callbackContext: function callbackContext(context /*, listener, eventObj*/) {
11365 return context;
11366 },
11367 beforeEmit: function beforeEmit( /* context, listener, eventObj */
11368 ) {},
11369 afterEmit: function afterEmit( /* context, listener, eventObj */
11370 ) {},
11371 bubble: function bubble( /*context*/
11372 ) {
11373 return false;
11374 },
11375 parent: function parent( /*context*/
11376 ) {
11377 return null;
11378 },
11379 context: null
11380 };
11381 var defaultsKeys = Object.keys(defaults$8);
11382 var emptyOpts = {};
11383 function Emitter() {
11384 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
11385 var context = arguments.length > 1 ? arguments[1] : undefined;
11386 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
11387 for (var i = 0; i < defaultsKeys.length; i++) {
11388 var key = defaultsKeys[i];
11389 this[key] = opts[key] || defaults$8[key];
11390 }
11391 this.context = context || this.context;
11392 this.listeners = [];
11393 this.emitting = 0;
11394 }
11395 var p = Emitter.prototype;
11396 var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
11397 if (fn$6(qualifier)) {
11398 callback = qualifier;
11399 qualifier = null;
11400 }
11401 if (confOverrides) {
11402 if (conf == null) {
11403 conf = confOverrides;
11404 } else {
11405 conf = extend({}, conf, confOverrides);
11406 }
11407 }
11408 var eventList = array(events) ? events : events.split(/\s+/);
11409 for (var i = 0; i < eventList.length; i++) {
11410 var evt = eventList[i];
11411 if (emptyString(evt)) {
11412 continue;
11413 }
11414 var match = evt.match(eventRegex); // type[.namespace]
11415
11416 if (match) {
11417 var type = match[1];
11418 var namespace = match[2] ? match[2] : null;
11419 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
11420 if (ret === false) {
11421 break;
11422 } // allow exiting early
11423 }
11424 }
11425 };
11426
11427 var makeEventObj = function makeEventObj(self, obj) {
11428 self.addEventFields(self.context, obj);
11429 return new Event(obj.type, obj);
11430 };
11431 var forEachEventObj = function forEachEventObj(self, handler, events) {
11432 if (event(events)) {
11433 handler(self, events);
11434 return;
11435 } else if (plainObject(events)) {
11436 handler(self, makeEventObj(self, events));
11437 return;
11438 }
11439 var eventList = array(events) ? events : events.split(/\s+/);
11440 for (var i = 0; i < eventList.length; i++) {
11441 var evt = eventList[i];
11442 if (emptyString(evt)) {
11443 continue;
11444 }
11445 var match = evt.match(eventRegex); // type[.namespace]
11446
11447 if (match) {
11448 var type = match[1];
11449 var namespace = match[2] ? match[2] : null;
11450 var eventObj = makeEventObj(self, {
11451 type: type,
11452 namespace: namespace,
11453 target: self.context
11454 });
11455 handler(self, eventObj);
11456 }
11457 }
11458 };
11459 p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
11460 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
11461 if (fn$6(callback)) {
11462 self.listeners.push({
11463 event: event,
11464 // full event string
11465 callback: callback,
11466 // callback to run
11467 type: type,
11468 // the event type (e.g. 'click')
11469 namespace: namespace,
11470 // the event namespace (e.g. ".foo")
11471 qualifier: qualifier,
11472 // a restriction on whether to match this emitter
11473 conf: conf // additional configuration
11474 });
11475 }
11476 }, events, qualifier, callback, conf, confOverrides);
11477 return this;
11478 };
11479 p.one = function (events, qualifier, callback, conf) {
11480 return this.on(events, qualifier, callback, conf, {
11481 one: true
11482 });
11483 };
11484 p.removeListener = p.off = function (events, qualifier, callback, conf) {
11485 var _this = this;
11486 if (this.emitting !== 0) {
11487 this.listeners = copyArray$1(this.listeners);
11488 }
11489 var listeners = this.listeners;
11490 var _loop = function _loop(i) {
11491 var listener = listeners[i];
11492 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback /*, conf*/) {
11493 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
11494 listeners.splice(i, 1);
11495 return false;
11496 }
11497 }, events, qualifier, callback, conf);
11498 };
11499 for (var i = listeners.length - 1; i >= 0; i--) {
11500 _loop(i);
11501 }
11502 return this;
11503 };
11504 p.removeAllListeners = function () {
11505 return this.removeListener('*');
11506 };
11507 p.emit = p.trigger = function (events, extraParams, manualCallback) {
11508 var listeners = this.listeners;
11509 var numListenersBeforeEmit = listeners.length;
11510 this.emitting++;
11511 if (!array(extraParams)) {
11512 extraParams = [extraParams];
11513 }
11514 forEachEventObj(this, function (self, eventObj) {
11515 if (manualCallback != null) {
11516 listeners = [{
11517 event: eventObj.event,
11518 type: eventObj.type,
11519 namespace: eventObj.namespace,
11520 callback: manualCallback
11521 }];
11522 numListenersBeforeEmit = listeners.length;
11523 }
11524 var _loop2 = function _loop2(i) {
11525 var listener = listeners[i];
11526 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
11527 var args = [eventObj];
11528 if (extraParams != null) {
11529 push(args, extraParams);
11530 }
11531 self.beforeEmit(self.context, listener, eventObj);
11532 if (listener.conf && listener.conf.one) {
11533 self.listeners = self.listeners.filter(function (l) {
11534 return l !== listener;
11535 });
11536 }
11537 var context = self.callbackContext(self.context, listener, eventObj);
11538 var ret = listener.callback.apply(context, args);
11539 self.afterEmit(self.context, listener, eventObj);
11540 if (ret === false) {
11541 eventObj.stopPropagation();
11542 eventObj.preventDefault();
11543 }
11544 } // if listener matches
11545 };
11546 for (var i = 0; i < numListenersBeforeEmit; i++) {
11547 _loop2(i);
11548 } // for listener
11549
11550 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
11551 self.parent(self.context).emit(eventObj, extraParams);
11552 }
11553 }, events);
11554 this.emitting--;
11555 return this;
11556 };
11557
11558 var emitterOptions$1 = {
11559 qualifierCompare: function qualifierCompare(selector1, selector2) {
11560 if (selector1 == null || selector2 == null) {
11561 return selector1 == null && selector2 == null;
11562 } else {
11563 return selector1.sameText(selector2);
11564 }
11565 },
11566 eventMatches: function eventMatches(ele, listener, eventObj) {
11567 var selector = listener.qualifier;
11568 if (selector != null) {
11569 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
11570 }
11571 return true;
11572 },
11573 addEventFields: function addEventFields(ele, evt) {
11574 evt.cy = ele.cy();
11575 evt.target = ele;
11576 },
11577 callbackContext: function callbackContext(ele, listener, eventObj) {
11578 return listener.qualifier != null ? eventObj.target : ele;
11579 },
11580 beforeEmit: function beforeEmit(context, listener /*, eventObj*/) {
11581 if (listener.conf && listener.conf.once) {
11582 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
11583 }
11584 },
11585 bubble: function bubble() {
11586 return true;
11587 },
11588 parent: function parent(ele) {
11589 return ele.isChild() ? ele.parent() : ele.cy();
11590 }
11591 };
11592 var argSelector$1 = function argSelector(arg) {
11593 if (string(arg)) {
11594 return new Selector(arg);
11595 } else {
11596 return arg;
11597 }
11598 };
11599 var elesfn$9 = {
11600 createEmitter: function createEmitter() {
11601 for (var i = 0; i < this.length; i++) {
11602 var ele = this[i];
11603 var _p = ele._private;
11604 if (!_p.emitter) {
11605 _p.emitter = new Emitter(emitterOptions$1, ele);
11606 }
11607 }
11608 return this;
11609 },
11610 emitter: function emitter() {
11611 return this._private.emitter;
11612 },
11613 on: function on(events, selector, callback) {
11614 var argSel = argSelector$1(selector);
11615 for (var i = 0; i < this.length; i++) {
11616 var ele = this[i];
11617 ele.emitter().on(events, argSel, callback);
11618 }
11619 return this;
11620 },
11621 removeListener: function removeListener(events, selector, callback) {
11622 var argSel = argSelector$1(selector);
11623 for (var i = 0; i < this.length; i++) {
11624 var ele = this[i];
11625 ele.emitter().removeListener(events, argSel, callback);
11626 }
11627 return this;
11628 },
11629 removeAllListeners: function removeAllListeners() {
11630 for (var i = 0; i < this.length; i++) {
11631 var ele = this[i];
11632 ele.emitter().removeAllListeners();
11633 }
11634 return this;
11635 },
11636 one: function one(events, selector, callback) {
11637 var argSel = argSelector$1(selector);
11638 for (var i = 0; i < this.length; i++) {
11639 var ele = this[i];
11640 ele.emitter().one(events, argSel, callback);
11641 }
11642 return this;
11643 },
11644 once: function once(events, selector, callback) {
11645 var argSel = argSelector$1(selector);
11646 for (var i = 0; i < this.length; i++) {
11647 var ele = this[i];
11648 ele.emitter().on(events, argSel, callback, {
11649 once: true,
11650 onceCollection: this
11651 });
11652 }
11653 },
11654 emit: function emit(events, extraParams) {
11655 for (var i = 0; i < this.length; i++) {
11656 var ele = this[i];
11657 ele.emitter().emit(events, extraParams);
11658 }
11659 return this;
11660 },
11661 emitAndNotify: function emitAndNotify(event, extraParams) {
11662 // for internal use only
11663 if (this.length === 0) {
11664 return;
11665 } // empty collections don't need to notify anything
11666
11667 // notify renderer
11668 this.cy().notify(event, this);
11669 this.emit(event, extraParams);
11670 return this;
11671 }
11672 };
11673 define.eventAliasesOn(elesfn$9);
11674
11675 var elesfn$8 = {
11676 nodes: function nodes(selector) {
11677 return this.filter(function (ele) {
11678 return ele.isNode();
11679 }).filter(selector);
11680 },
11681 edges: function edges(selector) {
11682 return this.filter(function (ele) {
11683 return ele.isEdge();
11684 }).filter(selector);
11685 },
11686 // internal helper to get nodes and edges as separate collections with single iteration over elements
11687 byGroup: function byGroup() {
11688 var nodes = this.spawn();
11689 var edges = this.spawn();
11690 for (var i = 0; i < this.length; i++) {
11691 var ele = this[i];
11692 if (ele.isNode()) {
11693 nodes.push(ele);
11694 } else {
11695 edges.push(ele);
11696 }
11697 }
11698 return {
11699 nodes: nodes,
11700 edges: edges
11701 };
11702 },
11703 filter: function filter(_filter, thisArg) {
11704 if (_filter === undefined) {
11705 // check this first b/c it's the most common/performant case
11706 return this;
11707 } else if (string(_filter) || elementOrCollection(_filter)) {
11708 return new Selector(_filter).filter(this);
11709 } else if (fn$6(_filter)) {
11710 var filterEles = this.spawn();
11711 var eles = this;
11712 for (var i = 0; i < eles.length; i++) {
11713 var ele = eles[i];
11714 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
11715 if (include) {
11716 filterEles.push(ele);
11717 }
11718 }
11719 return filterEles;
11720 }
11721 return this.spawn(); // if not handled by above, give 'em an empty collection
11722 },
11723
11724 not: function not(toRemove) {
11725 if (!toRemove) {
11726 return this;
11727 } else {
11728 if (string(toRemove)) {
11729 toRemove = this.filter(toRemove);
11730 }
11731 var elements = this.spawn();
11732 for (var i = 0; i < this.length; i++) {
11733 var element = this[i];
11734 var remove = toRemove.has(element);
11735 if (!remove) {
11736 elements.push(element);
11737 }
11738 }
11739 return elements;
11740 }
11741 },
11742 absoluteComplement: function absoluteComplement() {
11743 var cy = this.cy();
11744 return cy.mutableElements().not(this);
11745 },
11746 intersect: function intersect(other) {
11747 // if a selector is specified, then filter by it instead
11748 if (string(other)) {
11749 var selector = other;
11750 return this.filter(selector);
11751 }
11752 var elements = this.spawn();
11753 var col1 = this;
11754 var col2 = other;
11755 var col1Smaller = this.length < other.length;
11756 var colS = col1Smaller ? col1 : col2;
11757 var colL = col1Smaller ? col2 : col1;
11758 for (var i = 0; i < colS.length; i++) {
11759 var ele = colS[i];
11760 if (colL.has(ele)) {
11761 elements.push(ele);
11762 }
11763 }
11764 return elements;
11765 },
11766 xor: function xor(other) {
11767 var cy = this._private.cy;
11768 if (string(other)) {
11769 other = cy.$(other);
11770 }
11771 var elements = this.spawn();
11772 var col1 = this;
11773 var col2 = other;
11774 var add = function add(col, other) {
11775 for (var i = 0; i < col.length; i++) {
11776 var ele = col[i];
11777 var id = ele._private.data.id;
11778 var inOther = other.hasElementWithId(id);
11779 if (!inOther) {
11780 elements.push(ele);
11781 }
11782 }
11783 };
11784 add(col1, col2);
11785 add(col2, col1);
11786 return elements;
11787 },
11788 diff: function diff(other) {
11789 var cy = this._private.cy;
11790 if (string(other)) {
11791 other = cy.$(other);
11792 }
11793 var left = this.spawn();
11794 var right = this.spawn();
11795 var both = this.spawn();
11796 var col1 = this;
11797 var col2 = other;
11798 var add = function add(col, other, retEles) {
11799 for (var i = 0; i < col.length; i++) {
11800 var ele = col[i];
11801 var id = ele._private.data.id;
11802 var inOther = other.hasElementWithId(id);
11803 if (inOther) {
11804 both.merge(ele);
11805 } else {
11806 retEles.push(ele);
11807 }
11808 }
11809 };
11810 add(col1, col2, left);
11811 add(col2, col1, right);
11812 return {
11813 left: left,
11814 right: right,
11815 both: both
11816 };
11817 },
11818 add: function add(toAdd) {
11819 var cy = this._private.cy;
11820 if (!toAdd) {
11821 return this;
11822 }
11823 if (string(toAdd)) {
11824 var selector = toAdd;
11825 toAdd = cy.mutableElements().filter(selector);
11826 }
11827 var elements = this.spawnSelf();
11828 for (var i = 0; i < toAdd.length; i++) {
11829 var ele = toAdd[i];
11830 var add = !this.has(ele);
11831 if (add) {
11832 elements.push(ele);
11833 }
11834 }
11835 return elements;
11836 },
11837 // in place merge on calling collection
11838 merge: function merge(toAdd) {
11839 var _p = this._private;
11840 var cy = _p.cy;
11841 if (!toAdd) {
11842 return this;
11843 }
11844 if (toAdd && string(toAdd)) {
11845 var selector = toAdd;
11846 toAdd = cy.mutableElements().filter(selector);
11847 }
11848 var map = _p.map;
11849 for (var i = 0; i < toAdd.length; i++) {
11850 var toAddEle = toAdd[i];
11851 var id = toAddEle._private.data.id;
11852 var add = !map.has(id);
11853 if (add) {
11854 var index = this.length++;
11855 this[index] = toAddEle;
11856 map.set(id, {
11857 ele: toAddEle,
11858 index: index
11859 });
11860 }
11861 }
11862 return this; // chaining
11863 },
11864
11865 unmergeAt: function unmergeAt(i) {
11866 var ele = this[i];
11867 var id = ele.id();
11868 var _p = this._private;
11869 var map = _p.map;
11870
11871 // remove ele
11872 this[i] = undefined;
11873 map["delete"](id);
11874 var unmergedLastEle = i === this.length - 1;
11875
11876 // replace empty spot with last ele in collection
11877 if (this.length > 1 && !unmergedLastEle) {
11878 var lastEleI = this.length - 1;
11879 var lastEle = this[lastEleI];
11880 var lastEleId = lastEle._private.data.id;
11881 this[lastEleI] = undefined;
11882 this[i] = lastEle;
11883 map.set(lastEleId, {
11884 ele: lastEle,
11885 index: i
11886 });
11887 }
11888
11889 // the collection is now 1 ele smaller
11890 this.length--;
11891 return this;
11892 },
11893 // remove single ele in place in calling collection
11894 unmergeOne: function unmergeOne(ele) {
11895 ele = ele[0];
11896 var _p = this._private;
11897 var id = ele._private.data.id;
11898 var map = _p.map;
11899 var entry = map.get(id);
11900 if (!entry) {
11901 return this; // no need to remove
11902 }
11903
11904 var i = entry.index;
11905 this.unmergeAt(i);
11906 return this;
11907 },
11908 // remove eles in place on calling collection
11909 unmerge: function unmerge(toRemove) {
11910 var cy = this._private.cy;
11911 if (!toRemove) {
11912 return this;
11913 }
11914 if (toRemove && string(toRemove)) {
11915 var selector = toRemove;
11916 toRemove = cy.mutableElements().filter(selector);
11917 }
11918 for (var i = 0; i < toRemove.length; i++) {
11919 this.unmergeOne(toRemove[i]);
11920 }
11921 return this; // chaining
11922 },
11923
11924 unmergeBy: function unmergeBy(toRmFn) {
11925 for (var i = this.length - 1; i >= 0; i--) {
11926 var ele = this[i];
11927 if (toRmFn(ele)) {
11928 this.unmergeAt(i);
11929 }
11930 }
11931 return this;
11932 },
11933 map: function map(mapFn, thisArg) {
11934 var arr = [];
11935 var eles = this;
11936 for (var i = 0; i < eles.length; i++) {
11937 var ele = eles[i];
11938 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11939 arr.push(ret);
11940 }
11941 return arr;
11942 },
11943 reduce: function reduce(fn, initialValue) {
11944 var val = initialValue;
11945 var eles = this;
11946 for (var i = 0; i < eles.length; i++) {
11947 val = fn(val, eles[i], i, eles);
11948 }
11949 return val;
11950 },
11951 max: function max(valFn, thisArg) {
11952 var max = -Infinity;
11953 var maxEle;
11954 var eles = this;
11955 for (var i = 0; i < eles.length; i++) {
11956 var ele = eles[i];
11957 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11958 if (val > max) {
11959 max = val;
11960 maxEle = ele;
11961 }
11962 }
11963 return {
11964 value: max,
11965 ele: maxEle
11966 };
11967 },
11968 min: function min(valFn, thisArg) {
11969 var min = Infinity;
11970 var minEle;
11971 var eles = this;
11972 for (var i = 0; i < eles.length; i++) {
11973 var ele = eles[i];
11974 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11975 if (val < min) {
11976 min = val;
11977 minEle = ele;
11978 }
11979 }
11980 return {
11981 value: min,
11982 ele: minEle
11983 };
11984 }
11985 };
11986
11987 // aliases
11988 var fn$1 = elesfn$8;
11989 fn$1['u'] = fn$1['|'] = fn$1['+'] = fn$1.union = fn$1.or = fn$1.add;
11990 fn$1['\\'] = fn$1['!'] = fn$1['-'] = fn$1.difference = fn$1.relativeComplement = fn$1.subtract = fn$1.not;
11991 fn$1['n'] = fn$1['&'] = fn$1['.'] = fn$1.and = fn$1.intersection = fn$1.intersect;
11992 fn$1['^'] = fn$1['(+)'] = fn$1['(-)'] = fn$1.symmetricDifference = fn$1.symdiff = fn$1.xor;
11993 fn$1.fnFilter = fn$1.filterFn = fn$1.stdFilter = fn$1.filter;
11994 fn$1.complement = fn$1.abscomp = fn$1.absoluteComplement;
11995
11996 var elesfn$7 = {
11997 isNode: function isNode() {
11998 return this.group() === 'nodes';
11999 },
12000 isEdge: function isEdge() {
12001 return this.group() === 'edges';
12002 },
12003 isLoop: function isLoop() {
12004 return this.isEdge() && this.source()[0] === this.target()[0];
12005 },
12006 isSimple: function isSimple() {
12007 return this.isEdge() && this.source()[0] !== this.target()[0];
12008 },
12009 group: function group() {
12010 var ele = this[0];
12011 if (ele) {
12012 return ele._private.group;
12013 }
12014 }
12015 };
12016
12017 /**
12018 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
12019 * and z-index (low to high). These styles affect how this applies:
12020 *
12021 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
12022 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
12023 * root to leaves of the compound graph. The last drawn is `top`.
12024 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
12025 * `manual` ignores this convention and draws based on the `z-index` value setting.
12026 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
12027 * `z-index` will be drawn on top of an element with a lower `z-index`.
12028 */
12029 var zIndexSort = function zIndexSort(a, b) {
12030 var cy = a.cy();
12031 var hasCompoundNodes = cy.hasCompoundNodes();
12032 function getDepth(ele) {
12033 var style = ele.pstyle('z-compound-depth');
12034 if (style.value === 'auto') {
12035 return hasCompoundNodes ? ele.zDepth() : 0;
12036 } else if (style.value === 'bottom') {
12037 return -1;
12038 } else if (style.value === 'top') {
12039 return MAX_INT$1;
12040 }
12041 // 'orphan'
12042 return 0;
12043 }
12044 var depthDiff = getDepth(a) - getDepth(b);
12045 if (depthDiff !== 0) {
12046 return depthDiff;
12047 }
12048 function getEleDepth(ele) {
12049 var style = ele.pstyle('z-index-compare');
12050 if (style.value === 'auto') {
12051 return ele.isNode() ? 1 : 0;
12052 }
12053 // 'manual'
12054 return 0;
12055 }
12056 var eleDiff = getEleDepth(a) - getEleDepth(b);
12057 if (eleDiff !== 0) {
12058 return eleDiff;
12059 }
12060 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
12061 if (zDiff !== 0) {
12062 return zDiff;
12063 }
12064 // compare indices in the core (order added to graph w/ last on top)
12065 return a.poolIndex() - b.poolIndex();
12066 };
12067
12068 var elesfn$6 = {
12069 forEach: function forEach(fn, thisArg) {
12070 if (fn$6(fn)) {
12071 var N = this.length;
12072 for (var i = 0; i < N; i++) {
12073 var ele = this[i];
12074 var ret = thisArg ? fn.apply(thisArg, [ele, i, this]) : fn(ele, i, this);
12075 if (ret === false) {
12076 break;
12077 } // exit each early on return false
12078 }
12079 }
12080
12081 return this;
12082 },
12083 toArray: function toArray() {
12084 var array = [];
12085 for (var i = 0; i < this.length; i++) {
12086 array.push(this[i]);
12087 }
12088 return array;
12089 },
12090 slice: function slice(start, end) {
12091 var array = [];
12092 var thisSize = this.length;
12093 if (end == null) {
12094 end = thisSize;
12095 }
12096 if (start == null) {
12097 start = 0;
12098 }
12099 if (start < 0) {
12100 start = thisSize + start;
12101 }
12102 if (end < 0) {
12103 end = thisSize + end;
12104 }
12105 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
12106 array.push(this[i]);
12107 }
12108 return this.spawn(array);
12109 },
12110 size: function size() {
12111 return this.length;
12112 },
12113 eq: function eq(i) {
12114 return this[i] || this.spawn();
12115 },
12116 first: function first() {
12117 return this[0] || this.spawn();
12118 },
12119 last: function last() {
12120 return this[this.length - 1] || this.spawn();
12121 },
12122 empty: function empty() {
12123 return this.length === 0;
12124 },
12125 nonempty: function nonempty() {
12126 return !this.empty();
12127 },
12128 sort: function sort(sortFn) {
12129 if (!fn$6(sortFn)) {
12130 return this;
12131 }
12132 var sorted = this.toArray().sort(sortFn);
12133 return this.spawn(sorted);
12134 },
12135 sortByZIndex: function sortByZIndex() {
12136 return this.sort(zIndexSort);
12137 },
12138 zDepth: function zDepth() {
12139 var ele = this[0];
12140 if (!ele) {
12141 return undefined;
12142 }
12143
12144 // let cy = ele.cy();
12145 var _p = ele._private;
12146 var group = _p.group;
12147 if (group === 'nodes') {
12148 var depth = _p.data.parent ? ele.parents().size() : 0;
12149 if (!ele.isParent()) {
12150 return MAX_INT$1 - 1; // childless nodes always on top
12151 }
12152
12153 return depth;
12154 } else {
12155 var src = _p.source;
12156 var tgt = _p.target;
12157 var srcDepth = src.zDepth();
12158 var tgtDepth = tgt.zDepth();
12159 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
12160 }
12161 }
12162 };
12163
12164 elesfn$6.each = elesfn$6.forEach;
12165 var defineSymbolIterator = function defineSymbolIterator() {
12166 var typeofUndef = "undefined" ;
12167 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
12168
12169 if (isIteratorSupported) {
12170 elesfn$6[Symbol.iterator] = function () {
12171 var _this = this;
12172 // eslint-disable-line no-undef
12173 var entry = {
12174 value: undefined,
12175 done: false
12176 };
12177 var i = 0;
12178 var length = this.length;
12179 return _defineProperty$1({
12180 next: function next() {
12181 if (i < length) {
12182 entry.value = _this[i++];
12183 } else {
12184 entry.value = undefined;
12185 entry.done = true;
12186 }
12187 return entry;
12188 }
12189 }, Symbol.iterator, function () {
12190 // eslint-disable-line no-undef
12191 return this;
12192 });
12193 };
12194 }
12195 };
12196 defineSymbolIterator();
12197
12198 var getLayoutDimensionOptions = defaults$g({
12199 nodeDimensionsIncludeLabels: false
12200 });
12201 var elesfn$5 = {
12202 // Calculates and returns node dimensions { x, y } based on options given
12203 layoutDimensions: function layoutDimensions(options) {
12204 options = getLayoutDimensionOptions(options);
12205 var dims;
12206 if (!this.takesUpSpace()) {
12207 dims = {
12208 w: 0,
12209 h: 0
12210 };
12211 } else if (options.nodeDimensionsIncludeLabels) {
12212 var bbDim = this.boundingBox();
12213 dims = {
12214 w: bbDim.w,
12215 h: bbDim.h
12216 };
12217 } else {
12218 dims = {
12219 w: this.outerWidth(),
12220 h: this.outerHeight()
12221 };
12222 }
12223
12224 // sanitise the dimensions for external layouts (avoid division by zero)
12225 if (dims.w === 0 || dims.h === 0) {
12226 dims.w = dims.h = 1;
12227 }
12228 return dims;
12229 },
12230 // using standard layout options, apply position function (w/ or w/o animation)
12231 layoutPositions: function layoutPositions(layout, options, fn) {
12232 var nodes = this.nodes().filter(function (n) {
12233 return !n.isParent();
12234 });
12235 var cy = this.cy();
12236 var layoutEles = options.eles; // nodes & edges
12237 var getMemoizeKey = function getMemoizeKey(node) {
12238 return node.id();
12239 };
12240 var fnMem = memoize$1(fn, getMemoizeKey); // memoized version of position function
12241
12242 layout.emit({
12243 type: 'layoutstart',
12244 layout: layout
12245 });
12246 layout.animations = [];
12247 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
12248 var center = {
12249 x: nodesBb.x1 + nodesBb.w / 2,
12250 y: nodesBb.y1 + nodesBb.h / 2
12251 };
12252 var spacingVector = {
12253 // scale from center of bounding box (not necessarily 0,0)
12254 x: (pos.x - center.x) * spacing,
12255 y: (pos.y - center.y) * spacing
12256 };
12257 return {
12258 x: center.x + spacingVector.x,
12259 y: center.y + spacingVector.y
12260 };
12261 };
12262 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
12263 var spacingBb = function spacingBb() {
12264 if (!useSpacingFactor) {
12265 return null;
12266 }
12267 var bb = makeBoundingBox();
12268 for (var i = 0; i < nodes.length; i++) {
12269 var node = nodes[i];
12270 var pos = fnMem(node, i);
12271 expandBoundingBoxByPoint(bb, pos.x, pos.y);
12272 }
12273 return bb;
12274 };
12275 var bb = spacingBb();
12276 var getFinalPos = memoize$1(function (node, i) {
12277 var newPos = fnMem(node, i);
12278 if (useSpacingFactor) {
12279 var spacing = Math.abs(options.spacingFactor);
12280 newPos = calculateSpacing(spacing, bb, newPos);
12281 }
12282 if (options.transform != null) {
12283 newPos = options.transform(node, newPos);
12284 }
12285 return newPos;
12286 }, getMemoizeKey);
12287 if (options.animate) {
12288 for (var i = 0; i < nodes.length; i++) {
12289 var node = nodes[i];
12290 var newPos = getFinalPos(node, i);
12291 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
12292 if (animateNode) {
12293 var ani = node.animation({
12294 position: newPos,
12295 duration: options.animationDuration,
12296 easing: options.animationEasing
12297 });
12298 layout.animations.push(ani);
12299 } else {
12300 node.position(newPos);
12301 }
12302 }
12303 if (options.fit) {
12304 var fitAni = cy.animation({
12305 fit: {
12306 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
12307 padding: options.padding
12308 },
12309 duration: options.animationDuration,
12310 easing: options.animationEasing
12311 });
12312 layout.animations.push(fitAni);
12313 } else if (options.zoom !== undefined && options.pan !== undefined) {
12314 var zoomPanAni = cy.animation({
12315 zoom: options.zoom,
12316 pan: options.pan,
12317 duration: options.animationDuration,
12318 easing: options.animationEasing
12319 });
12320 layout.animations.push(zoomPanAni);
12321 }
12322 layout.animations.forEach(function (ani) {
12323 return ani.play();
12324 });
12325 layout.one('layoutready', options.ready);
12326 layout.emit({
12327 type: 'layoutready',
12328 layout: layout
12329 });
12330 Promise$1.all(layout.animations.map(function (ani) {
12331 return ani.promise();
12332 })).then(function () {
12333 layout.one('layoutstop', options.stop);
12334 layout.emit({
12335 type: 'layoutstop',
12336 layout: layout
12337 });
12338 });
12339 } else {
12340 nodes.positions(getFinalPos);
12341 if (options.fit) {
12342 cy.fit(options.eles, options.padding);
12343 }
12344 if (options.zoom != null) {
12345 cy.zoom(options.zoom);
12346 }
12347 if (options.pan) {
12348 cy.pan(options.pan);
12349 }
12350 layout.one('layoutready', options.ready);
12351 layout.emit({
12352 type: 'layoutready',
12353 layout: layout
12354 });
12355 layout.one('layoutstop', options.stop);
12356 layout.emit({
12357 type: 'layoutstop',
12358 layout: layout
12359 });
12360 }
12361 return this; // chaining
12362 },
12363
12364 layout: function layout(options) {
12365 var cy = this.cy();
12366 return cy.makeLayout(extend({}, options, {
12367 eles: this
12368 }));
12369 }
12370 };
12371
12372 // aliases:
12373 elesfn$5.createLayout = elesfn$5.makeLayout = elesfn$5.layout;
12374
12375 function styleCache(key, fn, ele) {
12376 var _p = ele._private;
12377 var cache = _p.styleCache = _p.styleCache || [];
12378 var val;
12379 if ((val = cache[key]) != null) {
12380 return val;
12381 } else {
12382 val = cache[key] = fn(ele);
12383 return val;
12384 }
12385 }
12386 function cacheStyleFunction(key, fn) {
12387 key = hashString(key);
12388 return function cachedStyleFunction(ele) {
12389 return styleCache(key, fn, ele);
12390 };
12391 }
12392 function cachePrototypeStyleFunction(key, fn) {
12393 key = hashString(key);
12394 var selfFn = function selfFn(ele) {
12395 return fn.call(ele);
12396 };
12397 return function cachedPrototypeStyleFunction() {
12398 var ele = this[0];
12399 if (ele) {
12400 return styleCache(key, selfFn, ele);
12401 }
12402 };
12403 }
12404 var elesfn$4 = {
12405 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
12406 var cy = this.cy();
12407 var renderer = cy.renderer();
12408 var styleEnabled = cy.styleEnabled();
12409 if (renderer && styleEnabled) {
12410 renderer.recalculateRenderedStyle(this, useCache);
12411 }
12412 return this;
12413 },
12414 dirtyStyleCache: function dirtyStyleCache() {
12415 var cy = this.cy();
12416 var dirty = function dirty(ele) {
12417 return ele._private.styleCache = null;
12418 };
12419 if (cy.hasCompoundNodes()) {
12420 var eles;
12421 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12422 eles.merge(eles.connectedEdges());
12423 eles.forEach(dirty);
12424 } else {
12425 this.forEach(function (ele) {
12426 dirty(ele);
12427 ele.connectedEdges().forEach(dirty);
12428 });
12429 }
12430 return this;
12431 },
12432 // fully updates (recalculates) the style for the elements
12433 updateStyle: function updateStyle(notifyRenderer) {
12434 var cy = this._private.cy;
12435 if (!cy.styleEnabled()) {
12436 return this;
12437 }
12438 if (cy.batching()) {
12439 var bEles = cy._private.batchStyleEles;
12440 bEles.merge(this);
12441 return this; // chaining and exit early when batching
12442 }
12443
12444 var hasCompounds = cy.hasCompoundNodes();
12445 var updatedEles = this;
12446 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
12447 if (hasCompounds) {
12448 // then add everything up and down for compound selector checks
12449 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
12450 }
12451
12452 // let changedEles = style.apply( updatedEles );
12453 var changedEles = updatedEles;
12454 if (notifyRenderer) {
12455 changedEles.emitAndNotify('style'); // let renderer know we changed style
12456 } else {
12457 changedEles.emit('style'); // just fire the event
12458 }
12459
12460 updatedEles.forEach(function (ele) {
12461 return ele._private.styleDirty = true;
12462 });
12463 return this; // chaining
12464 },
12465
12466 // private: clears dirty flag and recalculates style
12467 cleanStyle: function cleanStyle() {
12468 var cy = this.cy();
12469 if (!cy.styleEnabled()) {
12470 return;
12471 }
12472 for (var i = 0; i < this.length; i++) {
12473 var ele = this[i];
12474 if (ele._private.styleDirty) {
12475 // n.b. this flag should be set before apply() to avoid potential infinite recursion
12476 ele._private.styleDirty = false;
12477 cy.style().apply(ele);
12478 }
12479 }
12480 },
12481 // get the internal parsed style object for the specified property
12482 parsedStyle: function parsedStyle(property) {
12483 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12484 var ele = this[0];
12485 var cy = ele.cy();
12486 if (!cy.styleEnabled()) {
12487 return;
12488 }
12489 if (ele) {
12490 this.cleanStyle();
12491 var overriddenStyle = ele._private.style[property];
12492 if (overriddenStyle != null) {
12493 return overriddenStyle;
12494 } else if (includeNonDefault) {
12495 return cy.style().getDefaultProperty(property);
12496 } else {
12497 return null;
12498 }
12499 }
12500 },
12501 numericStyle: function numericStyle(property) {
12502 var ele = this[0];
12503 if (!ele.cy().styleEnabled()) {
12504 return;
12505 }
12506 if (ele) {
12507 var pstyle = ele.pstyle(property);
12508 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
12509 }
12510 },
12511 numericStyleUnits: function numericStyleUnits(property) {
12512 var ele = this[0];
12513 if (!ele.cy().styleEnabled()) {
12514 return;
12515 }
12516 if (ele) {
12517 return ele.pstyle(property).units;
12518 }
12519 },
12520 // get the specified css property as a rendered value (i.e. on-screen value)
12521 // or get the whole rendered style if no property specified (NB doesn't allow setting)
12522 renderedStyle: function renderedStyle(property) {
12523 var cy = this.cy();
12524 if (!cy.styleEnabled()) {
12525 return this;
12526 }
12527 var ele = this[0];
12528 if (ele) {
12529 return cy.style().getRenderedStyle(ele, property);
12530 }
12531 },
12532 // read the calculated css style of the element or override the style (via a bypass)
12533 style: function style(name, value) {
12534 var cy = this.cy();
12535 if (!cy.styleEnabled()) {
12536 return this;
12537 }
12538 var updateTransitions = false;
12539 var style = cy.style();
12540 if (plainObject(name)) {
12541 // then extend the bypass
12542 var props = name;
12543 style.applyBypass(this, props, updateTransitions);
12544 this.emitAndNotify('style'); // let the renderer know we've updated style
12545 } else if (string(name)) {
12546 if (value === undefined) {
12547 // then get the property from the style
12548 var ele = this[0];
12549 if (ele) {
12550 return style.getStylePropertyValue(ele, name);
12551 } else {
12552 // empty collection => can't get any value
12553 return;
12554 }
12555 } else {
12556 // then set the bypass with the property value
12557 style.applyBypass(this, name, value, updateTransitions);
12558 this.emitAndNotify('style'); // let the renderer know we've updated style
12559 }
12560 } else if (name === undefined) {
12561 var _ele = this[0];
12562 if (_ele) {
12563 return style.getRawStyle(_ele);
12564 } else {
12565 // empty collection => can't get any value
12566 return;
12567 }
12568 }
12569 return this; // chaining
12570 },
12571
12572 removeStyle: function removeStyle(names) {
12573 var cy = this.cy();
12574 if (!cy.styleEnabled()) {
12575 return this;
12576 }
12577 var updateTransitions = false;
12578 var style = cy.style();
12579 var eles = this;
12580 if (names === undefined) {
12581 for (var i = 0; i < eles.length; i++) {
12582 var ele = eles[i];
12583 style.removeAllBypasses(ele, updateTransitions);
12584 }
12585 } else {
12586 names = names.split(/\s+/);
12587 for (var _i = 0; _i < eles.length; _i++) {
12588 var _ele2 = eles[_i];
12589 style.removeBypasses(_ele2, names, updateTransitions);
12590 }
12591 }
12592 this.emitAndNotify('style'); // let the renderer know we've updated style
12593
12594 return this; // chaining
12595 },
12596
12597 show: function show() {
12598 this.css('display', 'element');
12599 return this; // chaining
12600 },
12601
12602 hide: function hide() {
12603 this.css('display', 'none');
12604 return this; // chaining
12605 },
12606
12607 effectiveOpacity: function effectiveOpacity() {
12608 var cy = this.cy();
12609 if (!cy.styleEnabled()) {
12610 return 1;
12611 }
12612 var hasCompoundNodes = cy.hasCompoundNodes();
12613 var ele = this[0];
12614 if (ele) {
12615 var _p = ele._private;
12616 var parentOpacity = ele.pstyle('opacity').value;
12617 if (!hasCompoundNodes) {
12618 return parentOpacity;
12619 }
12620 var parents = !_p.data.parent ? null : ele.parents();
12621 if (parents) {
12622 for (var i = 0; i < parents.length; i++) {
12623 var parent = parents[i];
12624 var opacity = parent.pstyle('opacity').value;
12625 parentOpacity = opacity * parentOpacity;
12626 }
12627 }
12628 return parentOpacity;
12629 }
12630 },
12631 transparent: function transparent() {
12632 var cy = this.cy();
12633 if (!cy.styleEnabled()) {
12634 return false;
12635 }
12636 var ele = this[0];
12637 var hasCompoundNodes = ele.cy().hasCompoundNodes();
12638 if (ele) {
12639 if (!hasCompoundNodes) {
12640 return ele.pstyle('opacity').value === 0;
12641 } else {
12642 return ele.effectiveOpacity() === 0;
12643 }
12644 }
12645 },
12646 backgrounding: function backgrounding() {
12647 var cy = this.cy();
12648 if (!cy.styleEnabled()) {
12649 return false;
12650 }
12651 var ele = this[0];
12652 return ele._private.backgrounding ? true : false;
12653 }
12654 };
12655 function checkCompound(ele, parentOk) {
12656 var _p = ele._private;
12657 var parents = _p.data.parent ? ele.parents() : null;
12658 if (parents) {
12659 for (var i = 0; i < parents.length; i++) {
12660 var parent = parents[i];
12661 if (!parentOk(parent)) {
12662 return false;
12663 }
12664 }
12665 }
12666 return true;
12667 }
12668 function defineDerivedStateFunction(specs) {
12669 var ok = specs.ok;
12670 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
12671 var parentOk = specs.parentOk || specs.ok;
12672 return function () {
12673 var cy = this.cy();
12674 if (!cy.styleEnabled()) {
12675 return true;
12676 }
12677 var ele = this[0];
12678 var hasCompoundNodes = cy.hasCompoundNodes();
12679 if (ele) {
12680 var _p = ele._private;
12681 if (!ok(ele)) {
12682 return false;
12683 }
12684 if (ele.isNode()) {
12685 return !hasCompoundNodes || checkCompound(ele, parentOk);
12686 } else {
12687 var src = _p.source;
12688 var tgt = _p.target;
12689 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
12690 }
12691 }
12692 };
12693 }
12694 var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
12695 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
12696 });
12697 elesfn$4.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
12698 ok: eleTakesUpSpace
12699 }));
12700 var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
12701 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
12702 });
12703 var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
12704 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
12705 });
12706 elesfn$4.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
12707 ok: eleInteractive,
12708 parentOk: parentInteractive,
12709 edgeOkViaNode: eleTakesUpSpace
12710 }));
12711 elesfn$4.noninteractive = function () {
12712 var ele = this[0];
12713 if (ele) {
12714 return !ele.interactive();
12715 }
12716 };
12717 var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
12718 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
12719 });
12720 var edgeVisibleViaNode = eleTakesUpSpace;
12721 elesfn$4.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
12722 ok: eleVisible,
12723 edgeOkViaNode: edgeVisibleViaNode
12724 }));
12725 elesfn$4.hidden = function () {
12726 var ele = this[0];
12727 if (ele) {
12728 return !ele.visible();
12729 }
12730 };
12731 elesfn$4.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
12732 if (!this.cy().styleEnabled()) {
12733 return false;
12734 }
12735 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
12736 });
12737 elesfn$4.bypass = elesfn$4.css = elesfn$4.style;
12738 elesfn$4.renderedCss = elesfn$4.renderedStyle;
12739 elesfn$4.removeBypass = elesfn$4.removeCss = elesfn$4.removeStyle;
12740 elesfn$4.pstyle = elesfn$4.parsedStyle;
12741
12742 var elesfn$3 = {};
12743 function defineSwitchFunction(params) {
12744 return function () {
12745 var args = arguments;
12746 var changedEles = [];
12747
12748 // e.g. cy.nodes().select( data, handler )
12749 if (args.length === 2) {
12750 var data = args[0];
12751 var handler = args[1];
12752 this.on(params.event, data, handler);
12753 }
12754
12755 // e.g. cy.nodes().select( handler )
12756 else if (args.length === 1 && fn$6(args[0])) {
12757 var _handler = args[0];
12758 this.on(params.event, _handler);
12759 }
12760
12761 // e.g. cy.nodes().select()
12762 // e.g. (private) cy.nodes().select(['tapselect'])
12763 else if (args.length === 0 || args.length === 1 && array(args[0])) {
12764 var addlEvents = args.length === 1 ? args[0] : null;
12765 for (var i = 0; i < this.length; i++) {
12766 var ele = this[i];
12767 var able = !params.ableField || ele._private[params.ableField];
12768 var changed = ele._private[params.field] != params.value;
12769 if (params.overrideAble) {
12770 var overrideAble = params.overrideAble(ele);
12771 if (overrideAble !== undefined) {
12772 able = overrideAble;
12773 if (!overrideAble) {
12774 return this;
12775 } // to save cycles assume not able for all on override
12776 }
12777 }
12778
12779 if (able) {
12780 ele._private[params.field] = params.value;
12781 if (changed) {
12782 changedEles.push(ele);
12783 }
12784 }
12785 }
12786 var changedColl = this.spawn(changedEles);
12787 changedColl.updateStyle(); // change of state => possible change of style
12788 changedColl.emit(params.event);
12789 if (addlEvents) {
12790 changedColl.emit(addlEvents);
12791 }
12792 }
12793 return this;
12794 };
12795 }
12796 function defineSwitchSet(params) {
12797 elesfn$3[params.field] = function () {
12798 var ele = this[0];
12799 if (ele) {
12800 if (params.overrideField) {
12801 var val = params.overrideField(ele);
12802 if (val !== undefined) {
12803 return val;
12804 }
12805 }
12806 return ele._private[params.field];
12807 }
12808 };
12809 elesfn$3[params.on] = defineSwitchFunction({
12810 event: params.on,
12811 field: params.field,
12812 ableField: params.ableField,
12813 overrideAble: params.overrideAble,
12814 value: true
12815 });
12816 elesfn$3[params.off] = defineSwitchFunction({
12817 event: params.off,
12818 field: params.field,
12819 ableField: params.ableField,
12820 overrideAble: params.overrideAble,
12821 value: false
12822 });
12823 }
12824 defineSwitchSet({
12825 field: 'locked',
12826 overrideField: function overrideField(ele) {
12827 return ele.cy().autolock() ? true : undefined;
12828 },
12829 on: 'lock',
12830 off: 'unlock'
12831 });
12832 defineSwitchSet({
12833 field: 'grabbable',
12834 overrideField: function overrideField(ele) {
12835 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12836 },
12837 on: 'grabify',
12838 off: 'ungrabify'
12839 });
12840 defineSwitchSet({
12841 field: 'selected',
12842 ableField: 'selectable',
12843 overrideAble: function overrideAble(ele) {
12844 return ele.cy().autounselectify() ? false : undefined;
12845 },
12846 on: 'select',
12847 off: 'unselect'
12848 });
12849 defineSwitchSet({
12850 field: 'selectable',
12851 overrideField: function overrideField(ele) {
12852 return ele.cy().autounselectify() ? false : undefined;
12853 },
12854 on: 'selectify',
12855 off: 'unselectify'
12856 });
12857 elesfn$3.deselect = elesfn$3.unselect;
12858 elesfn$3.grabbed = function () {
12859 var ele = this[0];
12860 if (ele) {
12861 return ele._private.grabbed;
12862 }
12863 };
12864 defineSwitchSet({
12865 field: 'active',
12866 on: 'activate',
12867 off: 'unactivate'
12868 });
12869 defineSwitchSet({
12870 field: 'pannable',
12871 on: 'panify',
12872 off: 'unpanify'
12873 });
12874 elesfn$3.inactive = function () {
12875 var ele = this[0];
12876 if (ele) {
12877 return !ele._private.active;
12878 }
12879 };
12880
12881 var elesfn$2 = {};
12882
12883 // DAG functions
12884 ////////////////
12885
12886 var defineDagExtremity = function defineDagExtremity(params) {
12887 return function dagExtremityImpl(selector) {
12888 var eles = this;
12889 var ret = [];
12890 for (var i = 0; i < eles.length; i++) {
12891 var ele = eles[i];
12892 if (!ele.isNode()) {
12893 continue;
12894 }
12895 var disqualified = false;
12896 var edges = ele.connectedEdges();
12897 for (var j = 0; j < edges.length; j++) {
12898 var edge = edges[j];
12899 var src = edge.source();
12900 var tgt = edge.target();
12901 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12902 disqualified = true;
12903 break;
12904 }
12905 }
12906 if (!disqualified) {
12907 ret.push(ele);
12908 }
12909 }
12910 return this.spawn(ret, true).filter(selector);
12911 };
12912 };
12913 var defineDagOneHop = function defineDagOneHop(params) {
12914 return function (selector) {
12915 var eles = this;
12916 var oEles = [];
12917 for (var i = 0; i < eles.length; i++) {
12918 var ele = eles[i];
12919 if (!ele.isNode()) {
12920 continue;
12921 }
12922 var edges = ele.connectedEdges();
12923 for (var j = 0; j < edges.length; j++) {
12924 var edge = edges[j];
12925 var src = edge.source();
12926 var tgt = edge.target();
12927 if (params.outgoing && src === ele) {
12928 oEles.push(edge);
12929 oEles.push(tgt);
12930 } else if (params.incoming && tgt === ele) {
12931 oEles.push(edge);
12932 oEles.push(src);
12933 }
12934 }
12935 }
12936 return this.spawn(oEles, true).filter(selector);
12937 };
12938 };
12939 var defineDagAllHops = function defineDagAllHops(params) {
12940 return function (selector) {
12941 var eles = this;
12942 var sEles = [];
12943 var sElesIds = {};
12944 for (;;) {
12945 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12946 if (next.length === 0) {
12947 break;
12948 } // done if none left
12949
12950 var newNext = false;
12951 for (var i = 0; i < next.length; i++) {
12952 var n = next[i];
12953 var nid = n.id();
12954 if (!sElesIds[nid]) {
12955 sElesIds[nid] = true;
12956 sEles.push(n);
12957 newNext = true;
12958 }
12959 }
12960 if (!newNext) {
12961 break;
12962 } // done if touched all outgoers already
12963
12964 eles = next;
12965 }
12966 return this.spawn(sEles, true).filter(selector);
12967 };
12968 };
12969 elesfn$2.clearTraversalCache = function () {
12970 for (var i = 0; i < this.length; i++) {
12971 this[i]._private.traversalCache = null;
12972 }
12973 };
12974 extend(elesfn$2, {
12975 // get the root nodes in the DAG
12976 roots: defineDagExtremity({
12977 noIncomingEdges: true
12978 }),
12979 // get the leaf nodes in the DAG
12980 leaves: defineDagExtremity({
12981 noOutgoingEdges: true
12982 }),
12983 // normally called children in graph theory
12984 // these nodes =edges=> outgoing nodes
12985 outgoers: cache(defineDagOneHop({
12986 outgoing: true
12987 }), 'outgoers'),
12988 // aka DAG descendants
12989 successors: defineDagAllHops({
12990 outgoing: true
12991 }),
12992 // normally called parents in graph theory
12993 // these nodes <=edges= incoming nodes
12994 incomers: cache(defineDagOneHop({
12995 incoming: true
12996 }), 'incomers'),
12997 // aka DAG ancestors
12998 predecessors: defineDagAllHops({
12999 incoming: true
13000 })
13001 });
13002
13003 // Neighbourhood functions
13004 //////////////////////////
13005
13006 extend(elesfn$2, {
13007 neighborhood: cache(function (selector) {
13008 var elements = [];
13009 var nodes = this.nodes();
13010 for (var i = 0; i < nodes.length; i++) {
13011 // for all nodes
13012 var node = nodes[i];
13013 var connectedEdges = node.connectedEdges();
13014
13015 // for each connected edge, add the edge and the other node
13016 for (var j = 0; j < connectedEdges.length; j++) {
13017 var edge = connectedEdges[j];
13018 var src = edge.source();
13019 var tgt = edge.target();
13020 var otherNode = node === src ? tgt : src;
13021
13022 // need check in case of loop
13023 if (otherNode.length > 0) {
13024 elements.push(otherNode[0]); // add node 1 hop away
13025 }
13026
13027 // add connected edge
13028 elements.push(edge[0]);
13029 }
13030 }
13031 return this.spawn(elements, true).filter(selector);
13032 }, 'neighborhood'),
13033 closedNeighborhood: function closedNeighborhood(selector) {
13034 return this.neighborhood().add(this).filter(selector);
13035 },
13036 openNeighborhood: function openNeighborhood(selector) {
13037 return this.neighborhood(selector);
13038 }
13039 });
13040
13041 // aliases
13042 elesfn$2.neighbourhood = elesfn$2.neighborhood;
13043 elesfn$2.closedNeighbourhood = elesfn$2.closedNeighborhood;
13044 elesfn$2.openNeighbourhood = elesfn$2.openNeighborhood;
13045
13046 // Edge functions
13047 /////////////////
13048
13049 extend(elesfn$2, {
13050 source: cache(function sourceImpl(selector) {
13051 var ele = this[0];
13052 var src;
13053 if (ele) {
13054 src = ele._private.source || ele.cy().collection();
13055 }
13056 return src && selector ? src.filter(selector) : src;
13057 }, 'source'),
13058 target: cache(function targetImpl(selector) {
13059 var ele = this[0];
13060 var tgt;
13061 if (ele) {
13062 tgt = ele._private.target || ele.cy().collection();
13063 }
13064 return tgt && selector ? tgt.filter(selector) : tgt;
13065 }, 'target'),
13066 sources: defineSourceFunction({
13067 attr: 'source'
13068 }),
13069 targets: defineSourceFunction({
13070 attr: 'target'
13071 })
13072 });
13073 function defineSourceFunction(params) {
13074 return function sourceImpl(selector) {
13075 var sources = [];
13076 for (var i = 0; i < this.length; i++) {
13077 var ele = this[i];
13078 var src = ele._private[params.attr];
13079 if (src) {
13080 sources.push(src);
13081 }
13082 }
13083 return this.spawn(sources, true).filter(selector);
13084 };
13085 }
13086 extend(elesfn$2, {
13087 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
13088 edgesTo: cache(defineEdgesWithFunction({
13089 thisIsSrc: true
13090 }), 'edgesTo')
13091 });
13092 function defineEdgesWithFunction(params) {
13093 return function edgesWithImpl(otherNodes) {
13094 var elements = [];
13095 var cy = this._private.cy;
13096 var p = params || {};
13097
13098 // get elements if a selector is specified
13099 if (string(otherNodes)) {
13100 otherNodes = cy.$(otherNodes);
13101 }
13102 for (var h = 0; h < otherNodes.length; h++) {
13103 var edges = otherNodes[h]._private.edges;
13104 for (var i = 0; i < edges.length; i++) {
13105 var edge = edges[i];
13106 var edgeData = edge._private.data;
13107 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
13108 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
13109 var edgeConnectsThisAndOther = thisToOther || otherToThis;
13110 if (!edgeConnectsThisAndOther) {
13111 continue;
13112 }
13113 if (p.thisIsSrc || p.thisIsTgt) {
13114 if (p.thisIsSrc && !thisToOther) {
13115 continue;
13116 }
13117 if (p.thisIsTgt && !otherToThis) {
13118 continue;
13119 }
13120 }
13121 elements.push(edge);
13122 }
13123 }
13124 return this.spawn(elements, true);
13125 };
13126 }
13127 extend(elesfn$2, {
13128 connectedEdges: cache(function (selector) {
13129 var retEles = [];
13130 var eles = this;
13131 for (var i = 0; i < eles.length; i++) {
13132 var node = eles[i];
13133 if (!node.isNode()) {
13134 continue;
13135 }
13136 var edges = node._private.edges;
13137 for (var j = 0; j < edges.length; j++) {
13138 var edge = edges[j];
13139 retEles.push(edge);
13140 }
13141 }
13142 return this.spawn(retEles, true).filter(selector);
13143 }, 'connectedEdges'),
13144 connectedNodes: cache(function (selector) {
13145 var retEles = [];
13146 var eles = this;
13147 for (var i = 0; i < eles.length; i++) {
13148 var edge = eles[i];
13149 if (!edge.isEdge()) {
13150 continue;
13151 }
13152 retEles.push(edge.source()[0]);
13153 retEles.push(edge.target()[0]);
13154 }
13155 return this.spawn(retEles, true).filter(selector);
13156 }, 'connectedNodes'),
13157 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
13158 codirectedEdges: cache(defineParallelEdgesFunction({
13159 codirected: true
13160 }), 'codirectedEdges')
13161 });
13162 function defineParallelEdgesFunction(params) {
13163 var defaults = {
13164 codirected: false
13165 };
13166 params = extend({}, defaults, params);
13167 return function parallelEdgesImpl(selector) {
13168 // micro-optimised for renderer
13169 var elements = [];
13170 var edges = this.edges();
13171 var p = params;
13172
13173 // look at all the edges in the collection
13174 for (var i = 0; i < edges.length; i++) {
13175 var edge1 = edges[i];
13176 var edge1_p = edge1._private;
13177 var src1 = edge1_p.source;
13178 var srcid1 = src1._private.data.id;
13179 var tgtid1 = edge1_p.data.target;
13180 var srcEdges1 = src1._private.edges;
13181
13182 // look at edges connected to the src node of this edge
13183 for (var j = 0; j < srcEdges1.length; j++) {
13184 var edge2 = srcEdges1[j];
13185 var edge2data = edge2._private.data;
13186 var tgtid2 = edge2data.target;
13187 var srcid2 = edge2data.source;
13188 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
13189 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
13190 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
13191 elements.push(edge2);
13192 }
13193 }
13194 }
13195 return this.spawn(elements, true).filter(selector);
13196 };
13197 }
13198
13199 // Misc functions
13200 /////////////////
13201
13202 extend(elesfn$2, {
13203 components: function components(root) {
13204 var self = this;
13205 var cy = self.cy();
13206 var visited = cy.collection();
13207 var unvisited = root == null ? self.nodes() : root.nodes();
13208 var components = [];
13209 if (root != null && unvisited.empty()) {
13210 // root may contain only edges
13211 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
13212 }
13213
13214 var visitInComponent = function visitInComponent(node, component) {
13215 visited.merge(node);
13216 unvisited.unmerge(node);
13217 component.merge(node);
13218 };
13219 if (unvisited.empty()) {
13220 return self.spawn();
13221 }
13222 var _loop = function _loop() {
13223 // each iteration yields a component
13224 var cmpt = cy.collection();
13225 components.push(cmpt);
13226 var root = unvisited[0];
13227 visitInComponent(root, cmpt);
13228 self.bfs({
13229 directed: false,
13230 roots: root,
13231 visit: function visit(v) {
13232 return visitInComponent(v, cmpt);
13233 }
13234 });
13235 cmpt.forEach(function (node) {
13236 node.connectedEdges().forEach(function (e) {
13237 // connectedEdges() usually cached
13238 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
13239 // has() is cheap
13240 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
13241 }
13242 });
13243 });
13244 };
13245 do {
13246 _loop();
13247 } while (unvisited.length > 0);
13248 return components;
13249 },
13250 component: function component() {
13251 var ele = this[0];
13252 return ele.cy().mutableElements().components(ele)[0];
13253 }
13254 });
13255 elesfn$2.componentsOf = elesfn$2.components;
13256
13257 // represents a set of nodes, edges, or both together
13258 var Collection = function Collection(cy, elements) {
13259 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
13260 var removed = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
13261 if (cy === undefined) {
13262 error('A collection must have a reference to the core');
13263 return;
13264 }
13265 var map = new Map$2();
13266 var createdElements = false;
13267 if (!elements) {
13268 elements = [];
13269 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
13270 createdElements = true;
13271
13272 // make elements from json and restore all at once later
13273 var eles = [];
13274 var elesIds = new Set$1();
13275 for (var i = 0, l = elements.length; i < l; i++) {
13276 var json = elements[i];
13277 if (json.data == null) {
13278 json.data = {};
13279 }
13280 var _data = json.data;
13281
13282 // make sure newly created elements have valid ids
13283 if (_data.id == null) {
13284 _data.id = uuid();
13285 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
13286 continue; // can't create element if prior id already exists
13287 }
13288
13289 var ele = new Element(cy, json, false);
13290 eles.push(ele);
13291 elesIds.add(_data.id);
13292 }
13293 elements = eles;
13294 }
13295 this.length = 0;
13296 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
13297 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
13298 if (element$1 == null) {
13299 continue;
13300 }
13301 var id = element$1._private.data.id;
13302 if (!unique || !map.has(id)) {
13303 if (unique) {
13304 map.set(id, {
13305 index: this.length,
13306 ele: element$1
13307 });
13308 }
13309 this[this.length] = element$1;
13310 this.length++;
13311 }
13312 }
13313 this._private = {
13314 eles: this,
13315 cy: cy,
13316 get map() {
13317 if (this.lazyMap == null) {
13318 this.rebuildMap();
13319 }
13320 return this.lazyMap;
13321 },
13322 set map(m) {
13323 this.lazyMap = m;
13324 },
13325 rebuildMap: function rebuildMap() {
13326 var m = this.lazyMap = new Map$2();
13327 var eles = this.eles;
13328 for (var _i2 = 0; _i2 < eles.length; _i2++) {
13329 var _ele = eles[_i2];
13330 m.set(_ele.id(), {
13331 index: _i2,
13332 ele: _ele
13333 });
13334 }
13335 }
13336 };
13337 if (unique) {
13338 this._private.map = map;
13339 }
13340
13341 // restore the elements if we created them from json
13342 if (createdElements && !removed) {
13343 this.restore();
13344 }
13345 };
13346
13347 // Functions
13348 ////////////////////////////////////////////////////////////////////////////////////////////////////
13349
13350 // keep the prototypes in sync (an element has the same functions as a collection)
13351 // and use elefn and elesfn as shorthands to the prototypes
13352 var elesfn$1 = Element.prototype = Collection.prototype = Object.create(Array.prototype);
13353 elesfn$1.instanceString = function () {
13354 return 'collection';
13355 };
13356 elesfn$1.spawn = function (eles, unique) {
13357 return new Collection(this.cy(), eles, unique);
13358 };
13359 elesfn$1.spawnSelf = function () {
13360 return this.spawn(this);
13361 };
13362 elesfn$1.cy = function () {
13363 return this._private.cy;
13364 };
13365 elesfn$1.renderer = function () {
13366 return this._private.cy.renderer();
13367 };
13368 elesfn$1.element = function () {
13369 return this[0];
13370 };
13371 elesfn$1.collection = function () {
13372 if (collection(this)) {
13373 return this;
13374 } else {
13375 // an element
13376 return new Collection(this._private.cy, [this]);
13377 }
13378 };
13379 elesfn$1.unique = function () {
13380 return new Collection(this._private.cy, this, true);
13381 };
13382 elesfn$1.hasElementWithId = function (id) {
13383 id = '' + id; // id must be string
13384
13385 return this._private.map.has(id);
13386 };
13387 elesfn$1.getElementById = function (id) {
13388 id = '' + id; // id must be string
13389
13390 var cy = this._private.cy;
13391 var entry = this._private.map.get(id);
13392 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
13393 };
13394
13395 elesfn$1.$id = elesfn$1.getElementById;
13396 elesfn$1.poolIndex = function () {
13397 var cy = this._private.cy;
13398 var eles = cy._private.elements;
13399 var id = this[0]._private.data.id;
13400 return eles._private.map.get(id).index;
13401 };
13402 elesfn$1.indexOf = function (ele) {
13403 var id = ele[0]._private.data.id;
13404 return this._private.map.get(id).index;
13405 };
13406 elesfn$1.indexOfId = function (id) {
13407 id = '' + id; // id must be string
13408
13409 return this._private.map.get(id).index;
13410 };
13411 elesfn$1.json = function (obj) {
13412 var ele = this.element();
13413 var cy = this.cy();
13414 if (ele == null && obj) {
13415 return this;
13416 } // can't set to no eles
13417
13418 if (ele == null) {
13419 return undefined;
13420 } // can't get from no eles
13421
13422 var p = ele._private;
13423 if (plainObject(obj)) {
13424 // set
13425
13426 cy.startBatch();
13427 if (obj.data) {
13428 ele.data(obj.data);
13429 var _data2 = p.data;
13430 if (ele.isEdge()) {
13431 // source and target are immutable via data()
13432 var move = false;
13433 var spec = {};
13434 var src = obj.data.source;
13435 var tgt = obj.data.target;
13436 if (src != null && src != _data2.source) {
13437 spec.source = '' + src; // id must be string
13438 move = true;
13439 }
13440 if (tgt != null && tgt != _data2.target) {
13441 spec.target = '' + tgt; // id must be string
13442 move = true;
13443 }
13444 if (move) {
13445 ele = ele.move(spec);
13446 }
13447 } else {
13448 // parent is immutable via data()
13449 var newParentValSpecd = ('parent' in obj.data);
13450 var parent = obj.data.parent;
13451 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
13452 if (parent === undefined) {
13453 // can't set undefined imperatively, so use null
13454 parent = null;
13455 }
13456 if (parent != null) {
13457 parent = '' + parent; // id must be string
13458 }
13459
13460 ele = ele.move({
13461 parent: parent
13462 });
13463 }
13464 }
13465 }
13466 if (obj.position) {
13467 ele.position(obj.position);
13468 }
13469
13470 // ignore group -- immutable
13471
13472 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
13473 var obj_k = obj[k];
13474 if (obj_k != null && obj_k !== p[k]) {
13475 if (obj_k) {
13476 ele[trueFnName]();
13477 } else {
13478 ele[falseFnName]();
13479 }
13480 }
13481 };
13482 checkSwitch('removed', 'remove', 'restore');
13483 checkSwitch('selected', 'select', 'unselect');
13484 checkSwitch('selectable', 'selectify', 'unselectify');
13485 checkSwitch('locked', 'lock', 'unlock');
13486 checkSwitch('grabbable', 'grabify', 'ungrabify');
13487 checkSwitch('pannable', 'panify', 'unpanify');
13488 if (obj.classes != null) {
13489 ele.classes(obj.classes);
13490 }
13491 cy.endBatch();
13492 return this;
13493 } else if (obj === undefined) {
13494 // get
13495
13496 var json = {
13497 data: copy(p.data),
13498 position: copy(p.position),
13499 group: p.group,
13500 removed: p.removed,
13501 selected: p.selected,
13502 selectable: p.selectable,
13503 locked: p.locked,
13504 grabbable: p.grabbable,
13505 pannable: p.pannable,
13506 classes: null
13507 };
13508 json.classes = '';
13509 var i = 0;
13510 p.classes.forEach(function (cls) {
13511 return json.classes += i++ === 0 ? cls : ' ' + cls;
13512 });
13513 return json;
13514 }
13515 };
13516 elesfn$1.jsons = function () {
13517 var jsons = [];
13518 for (var i = 0; i < this.length; i++) {
13519 var ele = this[i];
13520 var json = ele.json();
13521 jsons.push(json);
13522 }
13523 return jsons;
13524 };
13525 elesfn$1.clone = function () {
13526 var cy = this.cy();
13527 var elesArr = [];
13528 for (var i = 0; i < this.length; i++) {
13529 var ele = this[i];
13530 var json = ele.json();
13531 var clone = new Element(cy, json, false); // NB no restore
13532
13533 elesArr.push(clone);
13534 }
13535 return new Collection(cy, elesArr);
13536 };
13537 elesfn$1.copy = elesfn$1.clone;
13538 elesfn$1.restore = function () {
13539 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13540 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13541 var self = this;
13542 var cy = self.cy();
13543 var cy_p = cy._private;
13544
13545 // create arrays of nodes and edges, since we need to
13546 // restore the nodes first
13547 var nodes = [];
13548 var edges = [];
13549 var elements;
13550 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
13551 var ele = self[_i3];
13552 if (addToPool && !ele.removed()) {
13553 // don't need to handle this ele
13554 continue;
13555 }
13556
13557 // keep nodes first in the array and edges after
13558 if (ele.isNode()) {
13559 // put to front of array if node
13560 nodes.push(ele);
13561 } else {
13562 // put to end of array if edge
13563 edges.push(ele);
13564 }
13565 }
13566 elements = nodes.concat(edges);
13567 var i;
13568 var removeFromElements = function removeFromElements() {
13569 elements.splice(i, 1);
13570 i--;
13571 };
13572
13573 // now, restore each element
13574 for (i = 0; i < elements.length; i++) {
13575 var _ele2 = elements[i];
13576 var _private = _ele2._private;
13577 var _data3 = _private.data;
13578
13579 // the traversal cache should start fresh when ele is added
13580 _ele2.clearTraversalCache();
13581
13582 // set id and validate
13583 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
13584 _data3.id = uuid();
13585 } else if (number$1(_data3.id)) {
13586 _data3.id = '' + _data3.id; // now it's a string
13587 } else if (emptyString(_data3.id) || !string(_data3.id)) {
13588 error('Can not create element with invalid string ID `' + _data3.id + '`');
13589
13590 // can't create element if it has empty string as id or non-string id
13591 removeFromElements();
13592 continue;
13593 } else if (cy.hasElementWithId(_data3.id)) {
13594 error('Can not create second element with ID `' + _data3.id + '`');
13595
13596 // can't create element if one already has that id
13597 removeFromElements();
13598 continue;
13599 }
13600 var id = _data3.id; // id is finalised, now let's keep a ref
13601
13602 if (_ele2.isNode()) {
13603 // extra checks for nodes
13604 var pos = _private.position;
13605
13606 // make sure the nodes have a defined position
13607
13608 if (pos.x == null) {
13609 pos.x = 0;
13610 }
13611 if (pos.y == null) {
13612 pos.y = 0;
13613 }
13614 }
13615 if (_ele2.isEdge()) {
13616 // extra checks for edges
13617
13618 var edge = _ele2;
13619 var fields = ['source', 'target'];
13620 var fieldsLength = fields.length;
13621 var badSourceOrTarget = false;
13622 for (var j = 0; j < fieldsLength; j++) {
13623 var field = fields[j];
13624 var val = _data3[field];
13625 if (number$1(val)) {
13626 val = _data3[field] = '' + _data3[field]; // now string
13627 }
13628
13629 if (val == null || val === '') {
13630 // can't create if source or target is not defined properly
13631 error('Can not create edge `' + id + '` with unspecified ' + field);
13632 badSourceOrTarget = true;
13633 } else if (!cy.hasElementWithId(val)) {
13634 // can't create edge if one of its nodes doesn't exist
13635 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
13636 badSourceOrTarget = true;
13637 }
13638 }
13639 if (badSourceOrTarget) {
13640 removeFromElements();
13641 continue;
13642 } // can't create this
13643
13644 var src = cy.getElementById(_data3.source);
13645 var tgt = cy.getElementById(_data3.target);
13646
13647 // only one edge in node if loop
13648 if (src.same(tgt)) {
13649 src._private.edges.push(edge);
13650 } else {
13651 src._private.edges.push(edge);
13652 tgt._private.edges.push(edge);
13653 }
13654 edge._private.source = src;
13655 edge._private.target = tgt;
13656 } // if is edge
13657
13658 // create mock ids / indexes maps for element so it can be used like collections
13659 _private.map = new Map$2();
13660 _private.map.set(id, {
13661 ele: _ele2,
13662 index: 0
13663 });
13664 _private.removed = false;
13665 if (addToPool) {
13666 cy.addToPool(_ele2);
13667 }
13668 } // for each element
13669
13670 // do compound node sanity checks
13671 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
13672 // each node
13673 var node = nodes[_i4];
13674 var _data4 = node._private.data;
13675 if (number$1(_data4.parent)) {
13676 // then automake string
13677 _data4.parent = '' + _data4.parent;
13678 }
13679 var parentId = _data4.parent;
13680 var specifiedParent = parentId != null;
13681 if (specifiedParent || node._private.parent) {
13682 var parent = node._private.parent ? cy.collection().merge(node._private.parent) : cy.getElementById(parentId);
13683 if (parent.empty()) {
13684 // non-existant parent; just remove it
13685 _data4.parent = undefined;
13686 } else if (parent[0].removed()) {
13687 warn('Node added with missing parent, reference to parent removed');
13688 _data4.parent = undefined;
13689 node._private.parent = null;
13690 } else {
13691 var selfAsParent = false;
13692 var ancestor = parent;
13693 while (!ancestor.empty()) {
13694 if (node.same(ancestor)) {
13695 // mark self as parent and remove from data
13696 selfAsParent = true;
13697 _data4.parent = undefined; // remove parent reference
13698
13699 // exit or we loop forever
13700 break;
13701 }
13702 ancestor = ancestor.parent();
13703 }
13704 if (!selfAsParent) {
13705 // connect with children
13706 parent[0]._private.children.push(node);
13707 node._private.parent = parent[0];
13708
13709 // let the core know we have a compound graph
13710 cy_p.hasCompoundNodes = true;
13711 }
13712 } // else
13713 } // if specified parent
13714 } // for each node
13715
13716 if (elements.length > 0) {
13717 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13718 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13719 var _ele3 = restored[_i5];
13720 if (_ele3.isNode()) {
13721 continue;
13722 }
13723
13724 // adding an edge invalidates the traversal caches for the parallel edges
13725 _ele3.parallelEdges().clearTraversalCache();
13726
13727 // adding an edge invalidates the traversal cache for the connected nodes
13728 _ele3.source().clearTraversalCache();
13729 _ele3.target().clearTraversalCache();
13730 }
13731 var toUpdateStyle;
13732 if (cy_p.hasCompoundNodes) {
13733 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13734 } else {
13735 toUpdateStyle = restored;
13736 }
13737 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13738 if (notifyRenderer) {
13739 restored.emitAndNotify('add');
13740 } else if (addToPool) {
13741 restored.emit('add');
13742 }
13743 }
13744 return self; // chainability
13745 };
13746
13747 elesfn$1.removed = function () {
13748 var ele = this[0];
13749 return ele && ele._private.removed;
13750 };
13751 elesfn$1.inside = function () {
13752 var ele = this[0];
13753 return ele && !ele._private.removed;
13754 };
13755 elesfn$1.remove = function () {
13756 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13757 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13758 var self = this;
13759 var elesToRemove = [];
13760 var elesToRemoveIds = {};
13761 var cy = self._private.cy;
13762
13763 // add connected edges
13764 function addConnectedEdges(node) {
13765 var edges = node._private.edges;
13766 for (var i = 0; i < edges.length; i++) {
13767 add(edges[i]);
13768 }
13769 }
13770
13771 // add descendant nodes
13772 function addChildren(node) {
13773 var children = node._private.children;
13774 for (var i = 0; i < children.length; i++) {
13775 add(children[i]);
13776 }
13777 }
13778 function add(ele) {
13779 var alreadyAdded = elesToRemoveIds[ele.id()];
13780 if (removeFromPool && ele.removed() || alreadyAdded) {
13781 return;
13782 } else {
13783 elesToRemoveIds[ele.id()] = true;
13784 }
13785 if (ele.isNode()) {
13786 elesToRemove.push(ele); // nodes are removed last
13787
13788 addConnectedEdges(ele);
13789 addChildren(ele);
13790 } else {
13791 elesToRemove.unshift(ele); // edges are removed first
13792 }
13793 }
13794
13795 // make the list of elements to remove
13796 // (may be removing more than specified due to connected edges etc)
13797
13798 for (var i = 0, l = self.length; i < l; i++) {
13799 var ele = self[i];
13800 add(ele);
13801 }
13802 function removeEdgeRef(node, edge) {
13803 var connectedEdges = node._private.edges;
13804 removeFromArray(connectedEdges, edge);
13805
13806 // removing an edges invalidates the traversal cache for its nodes
13807 node.clearTraversalCache();
13808 }
13809 function removeParallelRef(pllEdge) {
13810 // removing an edge invalidates the traversal caches for the parallel edges
13811 pllEdge.clearTraversalCache();
13812 }
13813 var alteredParents = [];
13814 alteredParents.ids = {};
13815 function removeChildRef(parent, ele) {
13816 ele = ele[0];
13817 parent = parent[0];
13818 var children = parent._private.children;
13819 var pid = parent.id();
13820 removeFromArray(children, ele); // remove parent => child ref
13821
13822 ele._private.parent = null; // remove child => parent ref
13823
13824 if (!alteredParents.ids[pid]) {
13825 alteredParents.ids[pid] = true;
13826 alteredParents.push(parent);
13827 }
13828 }
13829 self.dirtyCompoundBoundsCache();
13830 if (removeFromPool) {
13831 cy.removeFromPool(elesToRemove); // remove from core pool
13832 }
13833
13834 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13835 var _ele4 = elesToRemove[_i6];
13836 if (_ele4.isEdge()) {
13837 // remove references to this edge in its connected nodes
13838 var src = _ele4.source()[0];
13839 var tgt = _ele4.target()[0];
13840 removeEdgeRef(src, _ele4);
13841 removeEdgeRef(tgt, _ele4);
13842 var pllEdges = _ele4.parallelEdges();
13843 for (var j = 0; j < pllEdges.length; j++) {
13844 var pllEdge = pllEdges[j];
13845 removeParallelRef(pllEdge);
13846 if (pllEdge.isBundledBezier()) {
13847 pllEdge.dirtyBoundingBoxCache();
13848 }
13849 }
13850 } else {
13851 // remove reference to parent
13852 var parent = _ele4.parent();
13853 if (parent.length !== 0) {
13854 removeChildRef(parent, _ele4);
13855 }
13856 }
13857 if (removeFromPool) {
13858 // mark as removed
13859 _ele4._private.removed = true;
13860 }
13861 }
13862
13863 // check to see if we have a compound graph or not
13864 var elesStillInside = cy._private.elements;
13865 cy._private.hasCompoundNodes = false;
13866 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13867 var _ele5 = elesStillInside[_i7];
13868 if (_ele5.isParent()) {
13869 cy._private.hasCompoundNodes = true;
13870 break;
13871 }
13872 }
13873 var removedElements = new Collection(this.cy(), elesToRemove);
13874 if (removedElements.size() > 0) {
13875 // must manually notify since trigger won't do this automatically once removed
13876
13877 if (notifyRenderer) {
13878 removedElements.emitAndNotify('remove');
13879 } else if (removeFromPool) {
13880 removedElements.emit('remove');
13881 }
13882 }
13883
13884 // the parents who were modified by the removal need their style updated
13885 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13886 var _ele6 = alteredParents[_i8];
13887 if (!removeFromPool || !_ele6.removed()) {
13888 _ele6.updateStyle();
13889 }
13890 }
13891 return removedElements;
13892 };
13893 elesfn$1.move = function (struct) {
13894 var cy = this._private.cy;
13895 var eles = this;
13896
13897 // just clean up refs, caches, etc. in the same way as when removing and then restoring
13898 // (our calls to remove/restore do not remove from the graph or make events)
13899 var notifyRenderer = false;
13900 var modifyPool = false;
13901 var toString = function toString(id) {
13902 return id == null ? id : '' + id;
13903 }; // id must be string
13904
13905 if (struct.source !== undefined || struct.target !== undefined) {
13906 var srcId = toString(struct.source);
13907 var tgtId = toString(struct.target);
13908 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13909 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13910 if (srcExists || tgtExists) {
13911 cy.batch(function () {
13912 // avoid duplicate style updates
13913 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13914 eles.emitAndNotify('moveout');
13915 for (var i = 0; i < eles.length; i++) {
13916 var ele = eles[i];
13917 var _data5 = ele._private.data;
13918 if (ele.isEdge()) {
13919 if (srcExists) {
13920 _data5.source = srcId;
13921 }
13922 if (tgtExists) {
13923 _data5.target = tgtId;
13924 }
13925 }
13926 }
13927 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13928 });
13929
13930 eles.emitAndNotify('move');
13931 }
13932 } else if (struct.parent !== undefined) {
13933 // move node to new parent
13934 var parentId = toString(struct.parent);
13935 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13936 if (parentExists) {
13937 var pidToAssign = parentId === null ? undefined : parentId;
13938 cy.batch(function () {
13939 // avoid duplicate style updates
13940 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13941 updated.emitAndNotify('moveout');
13942 for (var i = 0; i < eles.length; i++) {
13943 var ele = eles[i];
13944 var _data6 = ele._private.data;
13945 if (ele.isNode()) {
13946 _data6.parent = pidToAssign;
13947 }
13948 }
13949 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13950 });
13951
13952 eles.emitAndNotify('move');
13953 }
13954 }
13955 return this;
13956 };
13957 [elesfn$j, elesfn$i, elesfn$h, elesfn$g, elesfn$f, data, elesfn$d, dimensions, elesfn$9, elesfn$8, elesfn$7, elesfn$6, elesfn$5, elesfn$4, elesfn$3, elesfn$2].forEach(function (props) {
13958 extend(elesfn$1, props);
13959 });
13960
13961 var corefn$9 = {
13962 add: function add(opts) {
13963 var elements;
13964 var cy = this;
13965
13966 // add the elements
13967 if (elementOrCollection(opts)) {
13968 var eles = opts;
13969 if (eles._private.cy === cy) {
13970 // same instance => just restore
13971 elements = eles.restore();
13972 } else {
13973 // otherwise, copy from json
13974 var jsons = [];
13975 for (var i = 0; i < eles.length; i++) {
13976 var ele = eles[i];
13977 jsons.push(ele.json());
13978 }
13979 elements = new Collection(cy, jsons);
13980 }
13981 }
13982
13983 // specify an array of options
13984 else if (array(opts)) {
13985 var _jsons = opts;
13986 elements = new Collection(cy, _jsons);
13987 }
13988
13989 // specify via opts.nodes and opts.edges
13990 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13991 var elesByGroup = opts;
13992 var _jsons2 = [];
13993 var grs = ['nodes', 'edges'];
13994 for (var _i = 0, il = grs.length; _i < il; _i++) {
13995 var group = grs[_i];
13996 var elesArray = elesByGroup[group];
13997 if (array(elesArray)) {
13998 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13999 var json = extend({
14000 group: group
14001 }, elesArray[j]);
14002 _jsons2.push(json);
14003 }
14004 }
14005 }
14006 elements = new Collection(cy, _jsons2);
14007 }
14008
14009 // specify options for one element
14010 else {
14011 var _json = opts;
14012 elements = new Element(cy, _json).collection();
14013 }
14014 return elements;
14015 },
14016 remove: function remove(collection) {
14017 if (elementOrCollection(collection)) ; else if (string(collection)) {
14018 var selector = collection;
14019 collection = this.$(selector);
14020 }
14021 return collection.remove();
14022 }
14023 };
14024
14025 /* global Float32Array */
14026
14027 /*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14028 function generateCubicBezier(mX1, mY1, mX2, mY2) {
14029 var NEWTON_ITERATIONS = 4,
14030 NEWTON_MIN_SLOPE = 0.001,
14031 SUBDIVISION_PRECISION = 0.0000001,
14032 SUBDIVISION_MAX_ITERATIONS = 10,
14033 kSplineTableSize = 11,
14034 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
14035 float32ArraySupported = typeof Float32Array !== 'undefined';
14036
14037 /* Must contain four arguments. */
14038 if (arguments.length !== 4) {
14039 return false;
14040 }
14041
14042 /* Arguments must be numbers. */
14043 for (var i = 0; i < 4; ++i) {
14044 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
14045 return false;
14046 }
14047 }
14048
14049 /* X values must be in the [0, 1] range. */
14050 mX1 = Math.min(mX1, 1);
14051 mX2 = Math.min(mX2, 1);
14052 mX1 = Math.max(mX1, 0);
14053 mX2 = Math.max(mX2, 0);
14054 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
14055 function A(aA1, aA2) {
14056 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
14057 }
14058 function B(aA1, aA2) {
14059 return 3.0 * aA2 - 6.0 * aA1;
14060 }
14061 function C(aA1) {
14062 return 3.0 * aA1;
14063 }
14064 function calcBezier(aT, aA1, aA2) {
14065 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
14066 }
14067 function getSlope(aT, aA1, aA2) {
14068 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
14069 }
14070 function newtonRaphsonIterate(aX, aGuessT) {
14071 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
14072 var currentSlope = getSlope(aGuessT, mX1, mX2);
14073 if (currentSlope === 0.0) {
14074 return aGuessT;
14075 }
14076 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
14077 aGuessT -= currentX / currentSlope;
14078 }
14079 return aGuessT;
14080 }
14081 function calcSampleValues() {
14082 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
14083 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
14084 }
14085 }
14086 function binarySubdivide(aX, aA, aB) {
14087 var currentX,
14088 currentT,
14089 i = 0;
14090 do {
14091 currentT = aA + (aB - aA) / 2.0;
14092 currentX = calcBezier(currentT, mX1, mX2) - aX;
14093 if (currentX > 0.0) {
14094 aB = currentT;
14095 } else {
14096 aA = currentT;
14097 }
14098 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
14099 return currentT;
14100 }
14101 function getTForX(aX) {
14102 var intervalStart = 0.0,
14103 currentSample = 1,
14104 lastSample = kSplineTableSize - 1;
14105 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
14106 intervalStart += kSampleStepSize;
14107 }
14108 --currentSample;
14109 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
14110 guessForT = intervalStart + dist * kSampleStepSize,
14111 initialSlope = getSlope(guessForT, mX1, mX2);
14112 if (initialSlope >= NEWTON_MIN_SLOPE) {
14113 return newtonRaphsonIterate(aX, guessForT);
14114 } else if (initialSlope === 0.0) {
14115 return guessForT;
14116 } else {
14117 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
14118 }
14119 }
14120 var _precomputed = false;
14121 function precompute() {
14122 _precomputed = true;
14123 if (mX1 !== mY1 || mX2 !== mY2) {
14124 calcSampleValues();
14125 }
14126 }
14127 var f = function f(aX) {
14128 if (!_precomputed) {
14129 precompute();
14130 }
14131 if (mX1 === mY1 && mX2 === mY2) {
14132 return aX;
14133 }
14134 if (aX === 0) {
14135 return 0;
14136 }
14137 if (aX === 1) {
14138 return 1;
14139 }
14140 return calcBezier(getTForX(aX), mY1, mY2);
14141 };
14142 f.getControlPoints = function () {
14143 return [{
14144 x: mX1,
14145 y: mY1
14146 }, {
14147 x: mX2,
14148 y: mY2
14149 }];
14150 };
14151 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
14152 f.toString = function () {
14153 return str;
14154 };
14155 return f;
14156 }
14157
14158 /*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
14159 /* 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
14160 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
14161 var generateSpringRK4 = function () {
14162 function springAccelerationForState(state) {
14163 return -state.tension * state.x - state.friction * state.v;
14164 }
14165 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
14166 var state = {
14167 x: initialState.x + derivative.dx * dt,
14168 v: initialState.v + derivative.dv * dt,
14169 tension: initialState.tension,
14170 friction: initialState.friction
14171 };
14172 return {
14173 dx: state.v,
14174 dv: springAccelerationForState(state)
14175 };
14176 }
14177 function springIntegrateState(state, dt) {
14178 var a = {
14179 dx: state.v,
14180 dv: springAccelerationForState(state)
14181 },
14182 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
14183 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
14184 d = springEvaluateStateWithDerivative(state, dt, c),
14185 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
14186 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
14187 state.x = state.x + dxdt * dt;
14188 state.v = state.v + dvdt * dt;
14189 return state;
14190 }
14191 return function springRK4Factory(tension, friction, duration) {
14192 var initState = {
14193 x: -1,
14194 v: 0,
14195 tension: null,
14196 friction: null
14197 },
14198 path = [0],
14199 time_lapsed = 0,
14200 tolerance = 1 / 10000,
14201 DT = 16 / 1000,
14202 have_duration,
14203 dt,
14204 last_state;
14205 tension = parseFloat(tension) || 500;
14206 friction = parseFloat(friction) || 20;
14207 duration = duration || null;
14208 initState.tension = tension;
14209 initState.friction = friction;
14210 have_duration = duration !== null;
14211
14212 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
14213 if (have_duration) {
14214 /* Run the simulation without a duration. */
14215 time_lapsed = springRK4Factory(tension, friction);
14216 /* Compute the adjusted time delta. */
14217 dt = time_lapsed / duration * DT;
14218 } else {
14219 dt = DT;
14220 }
14221 for (;;) {
14222 /* Next/step function .*/
14223 last_state = springIntegrateState(last_state || initState, dt);
14224 /* Store the position. */
14225 path.push(1 + last_state.x);
14226 time_lapsed += 16;
14227 /* If the change threshold is reached, break. */
14228 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
14229 break;
14230 }
14231 }
14232
14233 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
14234 computed path and returns a snapshot of the position according to a given percentComplete. */
14235 return !have_duration ? time_lapsed : function (percentComplete) {
14236 return path[percentComplete * (path.length - 1) | 0];
14237 };
14238 };
14239 }();
14240
14241 var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
14242 var bezier = generateCubicBezier(t1, p1, t2, p2);
14243 return function (start, end, percent) {
14244 return start + (end - start) * bezier(percent);
14245 };
14246 };
14247 var easings = {
14248 'linear': function linear(start, end, percent) {
14249 return start + (end - start) * percent;
14250 },
14251 // default easings
14252 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
14253 'ease-in': cubicBezier(0.42, 0, 1, 1),
14254 'ease-out': cubicBezier(0, 0, 0.58, 1),
14255 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
14256 // sine
14257 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
14258 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
14259 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
14260 // quad
14261 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
14262 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
14263 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
14264 // cubic
14265 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
14266 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
14267 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
14268 // quart
14269 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
14270 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
14271 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
14272 // quint
14273 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
14274 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
14275 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
14276 // expo
14277 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
14278 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
14279 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
14280 // circ
14281 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
14282 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
14283 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
14284 // user param easings...
14285
14286 'spring': function spring(tension, friction, duration) {
14287 if (duration === 0) {
14288 // can't get a spring w/ duration 0
14289 return easings.linear; // duration 0 => jump to end so impl doesn't matter
14290 }
14291
14292 var spring = generateSpringRK4(tension, friction, duration);
14293 return function (start, end, percent) {
14294 return start + (end - start) * spring(percent);
14295 };
14296 },
14297 'cubic-bezier': cubicBezier
14298 };
14299
14300 function getEasedValue(type, start, end, percent, easingFn) {
14301 if (percent === 1) {
14302 return end;
14303 }
14304 if (start === end) {
14305 return end;
14306 }
14307 var val = easingFn(start, end, percent);
14308 if (type == null) {
14309 return val;
14310 }
14311 if (type.roundValue || type.color) {
14312 val = Math.round(val);
14313 }
14314 if (type.min !== undefined) {
14315 val = Math.max(val, type.min);
14316 }
14317 if (type.max !== undefined) {
14318 val = Math.min(val, type.max);
14319 }
14320 return val;
14321 }
14322 function getValue(prop, spec) {
14323 if (prop.pfValue != null || prop.value != null) {
14324 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
14325 return prop.pfValue;
14326 } else {
14327 return prop.value;
14328 }
14329 } else {
14330 return prop;
14331 }
14332 }
14333 function ease(startProp, endProp, percent, easingFn, propSpec) {
14334 var type = propSpec != null ? propSpec.type : null;
14335 if (percent < 0) {
14336 percent = 0;
14337 } else if (percent > 1) {
14338 percent = 1;
14339 }
14340 var start = getValue(startProp, propSpec);
14341 var end = getValue(endProp, propSpec);
14342 if (number$1(start) && number$1(end)) {
14343 return getEasedValue(type, start, end, percent, easingFn);
14344 } else if (array(start) && array(end)) {
14345 var easedArr = [];
14346 for (var i = 0; i < end.length; i++) {
14347 var si = start[i];
14348 var ei = end[i];
14349 if (si != null && ei != null) {
14350 var val = getEasedValue(type, si, ei, percent, easingFn);
14351 easedArr.push(val);
14352 } else {
14353 easedArr.push(ei);
14354 }
14355 }
14356 return easedArr;
14357 }
14358 return undefined;
14359 }
14360
14361 function step$1(self, ani, now, isCore) {
14362 var isEles = !isCore;
14363 var _p = self._private;
14364 var ani_p = ani._private;
14365 var pEasing = ani_p.easing;
14366 var startTime = ani_p.startTime;
14367 var cy = isCore ? self : self.cy();
14368 var style = cy.style();
14369 if (!ani_p.easingImpl) {
14370 if (pEasing == null) {
14371 // use default
14372 ani_p.easingImpl = easings['linear'];
14373 } else {
14374 // then define w/ name
14375 var easingVals;
14376 if (string(pEasing)) {
14377 var easingProp = style.parse('transition-timing-function', pEasing);
14378 easingVals = easingProp.value;
14379 } else {
14380 // then assume preparsed array
14381 easingVals = pEasing;
14382 }
14383 var name, args;
14384 if (string(easingVals)) {
14385 name = easingVals;
14386 args = [];
14387 } else {
14388 name = easingVals[1];
14389 args = easingVals.slice(2).map(function (n) {
14390 return +n;
14391 });
14392 }
14393 if (args.length > 0) {
14394 // create with args
14395 if (name === 'spring') {
14396 args.push(ani_p.duration); // need duration to generate spring
14397 }
14398
14399 ani_p.easingImpl = easings[name].apply(null, args);
14400 } else {
14401 // static impl by name
14402 ani_p.easingImpl = easings[name];
14403 }
14404 }
14405 }
14406 var easing = ani_p.easingImpl;
14407 var percent;
14408 if (ani_p.duration === 0) {
14409 percent = 1;
14410 } else {
14411 percent = (now - startTime) / ani_p.duration;
14412 }
14413 if (ani_p.applying) {
14414 percent = ani_p.progress;
14415 }
14416 if (percent < 0) {
14417 percent = 0;
14418 } else if (percent > 1) {
14419 percent = 1;
14420 }
14421 if (ani_p.delay == null) {
14422 // then update
14423
14424 var startPos = ani_p.startPosition;
14425 var endPos = ani_p.position;
14426 if (endPos && isEles && !self.locked()) {
14427 var newPos = {};
14428 if (valid(startPos.x, endPos.x)) {
14429 newPos.x = ease(startPos.x, endPos.x, percent, easing);
14430 }
14431 if (valid(startPos.y, endPos.y)) {
14432 newPos.y = ease(startPos.y, endPos.y, percent, easing);
14433 }
14434 self.position(newPos);
14435 }
14436 var startPan = ani_p.startPan;
14437 var endPan = ani_p.pan;
14438 var pan = _p.pan;
14439 var animatingPan = endPan != null && isCore;
14440 if (animatingPan) {
14441 if (valid(startPan.x, endPan.x)) {
14442 pan.x = ease(startPan.x, endPan.x, percent, easing);
14443 }
14444 if (valid(startPan.y, endPan.y)) {
14445 pan.y = ease(startPan.y, endPan.y, percent, easing);
14446 }
14447 self.emit('pan');
14448 }
14449 var startZoom = ani_p.startZoom;
14450 var endZoom = ani_p.zoom;
14451 var animatingZoom = endZoom != null && isCore;
14452 if (animatingZoom) {
14453 if (valid(startZoom, endZoom)) {
14454 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
14455 }
14456 self.emit('zoom');
14457 }
14458 if (animatingPan || animatingZoom) {
14459 self.emit('viewport');
14460 }
14461 var props = ani_p.style;
14462 if (props && props.length > 0 && isEles) {
14463 for (var i = 0; i < props.length; i++) {
14464 var prop = props[i];
14465 var _name = prop.name;
14466 var end = prop;
14467 var start = ani_p.startStyle[_name];
14468 var propSpec = style.properties[start.name];
14469 var easedVal = ease(start, end, percent, easing, propSpec);
14470 style.overrideBypass(self, _name, easedVal);
14471 } // for props
14472
14473 self.emit('style');
14474 } // if
14475 }
14476
14477 ani_p.progress = percent;
14478 return percent;
14479 }
14480 function valid(start, end) {
14481 if (start == null || end == null) {
14482 return false;
14483 }
14484 if (number$1(start) && number$1(end)) {
14485 return true;
14486 } else if (start && end) {
14487 return true;
14488 }
14489 return false;
14490 }
14491
14492 function startAnimation(self, ani, now, isCore) {
14493 var ani_p = ani._private;
14494 ani_p.started = true;
14495 ani_p.startTime = now - ani_p.progress * ani_p.duration;
14496 }
14497
14498 function stepAll(now, cy) {
14499 var eles = cy._private.aniEles;
14500 var doneEles = [];
14501 function stepOne(ele, isCore) {
14502 var _p = ele._private;
14503 var current = _p.animation.current;
14504 var queue = _p.animation.queue;
14505 var ranAnis = false;
14506
14507 // if nothing currently animating, get something from the queue
14508 if (current.length === 0) {
14509 var next = queue.shift();
14510 if (next) {
14511 current.push(next);
14512 }
14513 }
14514 var callbacks = function callbacks(_callbacks) {
14515 for (var j = _callbacks.length - 1; j >= 0; j--) {
14516 var cb = _callbacks[j];
14517 cb();
14518 }
14519 _callbacks.splice(0, _callbacks.length);
14520 };
14521
14522 // step and remove if done
14523 for (var i = current.length - 1; i >= 0; i--) {
14524 var ani = current[i];
14525 var ani_p = ani._private;
14526 if (ani_p.stopped) {
14527 current.splice(i, 1);
14528 ani_p.hooked = false;
14529 ani_p.playing = false;
14530 ani_p.started = false;
14531 callbacks(ani_p.frames);
14532 continue;
14533 }
14534 if (!ani_p.playing && !ani_p.applying) {
14535 continue;
14536 }
14537
14538 // an apply() while playing shouldn't do anything
14539 if (ani_p.playing && ani_p.applying) {
14540 ani_p.applying = false;
14541 }
14542 if (!ani_p.started) {
14543 startAnimation(ele, ani, now);
14544 }
14545 step$1(ele, ani, now, isCore);
14546 if (ani_p.applying) {
14547 ani_p.applying = false;
14548 }
14549 callbacks(ani_p.frames);
14550 if (ani_p.step != null) {
14551 ani_p.step(now);
14552 }
14553 if (ani.completed()) {
14554 current.splice(i, 1);
14555 ani_p.hooked = false;
14556 ani_p.playing = false;
14557 ani_p.started = false;
14558 callbacks(ani_p.completes);
14559 }
14560 ranAnis = true;
14561 }
14562 if (!isCore && current.length === 0 && queue.length === 0) {
14563 doneEles.push(ele);
14564 }
14565 return ranAnis;
14566 } // stepElement
14567
14568 // handle all eles
14569 var ranEleAni = false;
14570 for (var e = 0; e < eles.length; e++) {
14571 var ele = eles[e];
14572 var handledThisEle = stepOne(ele);
14573 ranEleAni = ranEleAni || handledThisEle;
14574 } // each element
14575
14576 var ranCoreAni = stepOne(cy, true);
14577
14578 // notify renderer
14579 if (ranEleAni || ranCoreAni) {
14580 if (eles.length > 0) {
14581 cy.notify('draw', eles);
14582 } else {
14583 cy.notify('draw');
14584 }
14585 }
14586
14587 // remove elements from list of currently animating if its queues are empty
14588 eles.unmerge(doneEles);
14589 cy.emit('step');
14590 } // stepAll
14591
14592 var corefn$8 = {
14593 // pull in animation functions
14594 animate: define.animate(),
14595 animation: define.animation(),
14596 animated: define.animated(),
14597 clearQueue: define.clearQueue(),
14598 delay: define.delay(),
14599 delayAnimation: define.delayAnimation(),
14600 stop: define.stop(),
14601 addToAnimationPool: function addToAnimationPool(eles) {
14602 var cy = this;
14603 if (!cy.styleEnabled()) {
14604 return;
14605 } // save cycles when no style used
14606
14607 cy._private.aniEles.merge(eles);
14608 },
14609 stopAnimationLoop: function stopAnimationLoop() {
14610 this._private.animationsRunning = false;
14611 },
14612 startAnimationLoop: function startAnimationLoop() {
14613 var cy = this;
14614 cy._private.animationsRunning = true;
14615 if (!cy.styleEnabled()) {
14616 return;
14617 } // save cycles when no style used
14618
14619 // NB the animation loop will exec in headless environments if style enabled
14620 // and explicit cy.destroy() is necessary to stop the loop
14621
14622 function headlessStep() {
14623 if (!cy._private.animationsRunning) {
14624 return;
14625 }
14626 requestAnimationFrame(function animationStep(now) {
14627 stepAll(now, cy);
14628 headlessStep();
14629 });
14630 }
14631 var renderer = cy.renderer();
14632 if (renderer && renderer.beforeRender) {
14633 // let the renderer schedule animations
14634 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14635 stepAll(now, cy);
14636 }, renderer.beforeRenderPriorities.animations);
14637 } else {
14638 // manage the animation loop ourselves
14639 headlessStep(); // first call
14640 }
14641 }
14642 };
14643
14644 var emitterOptions = {
14645 qualifierCompare: function qualifierCompare(selector1, selector2) {
14646 if (selector1 == null || selector2 == null) {
14647 return selector1 == null && selector2 == null;
14648 } else {
14649 return selector1.sameText(selector2);
14650 }
14651 },
14652 eventMatches: function eventMatches(cy, listener, eventObj) {
14653 var selector = listener.qualifier;
14654 if (selector != null) {
14655 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14656 }
14657 return true;
14658 },
14659 addEventFields: function addEventFields(cy, evt) {
14660 evt.cy = cy;
14661 evt.target = cy;
14662 },
14663 callbackContext: function callbackContext(cy, listener, eventObj) {
14664 return listener.qualifier != null ? eventObj.target : cy;
14665 }
14666 };
14667 var argSelector = function argSelector(arg) {
14668 if (string(arg)) {
14669 return new Selector(arg);
14670 } else {
14671 return arg;
14672 }
14673 };
14674 var elesfn = {
14675 createEmitter: function createEmitter() {
14676 var _p = this._private;
14677 if (!_p.emitter) {
14678 _p.emitter = new Emitter(emitterOptions, this);
14679 }
14680 return this;
14681 },
14682 emitter: function emitter() {
14683 return this._private.emitter;
14684 },
14685 on: function on(events, selector, callback) {
14686 this.emitter().on(events, argSelector(selector), callback);
14687 return this;
14688 },
14689 removeListener: function removeListener(events, selector, callback) {
14690 this.emitter().removeListener(events, argSelector(selector), callback);
14691 return this;
14692 },
14693 removeAllListeners: function removeAllListeners() {
14694 this.emitter().removeAllListeners();
14695 return this;
14696 },
14697 one: function one(events, selector, callback) {
14698 this.emitter().one(events, argSelector(selector), callback);
14699 return this;
14700 },
14701 once: function once(events, selector, callback) {
14702 this.emitter().one(events, argSelector(selector), callback);
14703 return this;
14704 },
14705 emit: function emit(events, extraParams) {
14706 this.emitter().emit(events, extraParams);
14707 return this;
14708 },
14709 emitAndNotify: function emitAndNotify(event, eles) {
14710 this.emit(event);
14711 this.notify(event, eles);
14712 return this;
14713 }
14714 };
14715 define.eventAliasesOn(elesfn);
14716
14717 var corefn$7 = {
14718 png: function png(options) {
14719 var renderer = this._private.renderer;
14720 options = options || {};
14721 return renderer.png(options);
14722 },
14723 jpg: function jpg(options) {
14724 var renderer = this._private.renderer;
14725 options = options || {};
14726 options.bg = options.bg || '#fff';
14727 return renderer.jpg(options);
14728 }
14729 };
14730 corefn$7.jpeg = corefn$7.jpg;
14731
14732 var corefn$6 = {
14733 layout: function layout(options) {
14734 var cy = this;
14735 if (options == null) {
14736 error('Layout options must be specified to make a layout');
14737 return;
14738 }
14739 if (options.name == null) {
14740 error('A `name` must be specified to make a layout');
14741 return;
14742 }
14743 var name = options.name;
14744 var Layout = cy.extension('layout', name);
14745 if (Layout == null) {
14746 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14747 return;
14748 }
14749 var eles;
14750 if (string(options.eles)) {
14751 eles = cy.$(options.eles);
14752 } else {
14753 eles = options.eles != null ? options.eles : cy.$();
14754 }
14755 var layout = new Layout(extend({}, options, {
14756 cy: cy,
14757 eles: eles
14758 }));
14759 return layout;
14760 }
14761 };
14762 corefn$6.createLayout = corefn$6.makeLayout = corefn$6.layout;
14763
14764 var corefn$5 = {
14765 notify: function notify(eventName, eventEles) {
14766 var _p = this._private;
14767 if (this.batching()) {
14768 _p.batchNotifications = _p.batchNotifications || {};
14769 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14770 if (eventEles != null) {
14771 eles.merge(eventEles);
14772 }
14773 return; // notifications are disabled during batching
14774 }
14775
14776 if (!_p.notificationsEnabled) {
14777 return;
14778 } // exit on disabled
14779
14780 var renderer = this.renderer();
14781
14782 // exit if destroy() called on core or renderer in between frames #1499 #1528
14783 if (this.destroyed() || !renderer) {
14784 return;
14785 }
14786 renderer.notify(eventName, eventEles);
14787 },
14788 notifications: function notifications(bool) {
14789 var p = this._private;
14790 if (bool === undefined) {
14791 return p.notificationsEnabled;
14792 } else {
14793 p.notificationsEnabled = bool ? true : false;
14794 }
14795 return this;
14796 },
14797 noNotifications: function noNotifications(callback) {
14798 this.notifications(false);
14799 callback();
14800 this.notifications(true);
14801 },
14802 batching: function batching() {
14803 return this._private.batchCount > 0;
14804 },
14805 startBatch: function startBatch() {
14806 var _p = this._private;
14807 if (_p.batchCount == null) {
14808 _p.batchCount = 0;
14809 }
14810 if (_p.batchCount === 0) {
14811 _p.batchStyleEles = this.collection();
14812 _p.batchNotifications = {};
14813 }
14814 _p.batchCount++;
14815 return this;
14816 },
14817 endBatch: function endBatch() {
14818 var _p = this._private;
14819 if (_p.batchCount === 0) {
14820 return this;
14821 }
14822 _p.batchCount--;
14823 if (_p.batchCount === 0) {
14824 // update style for dirty eles
14825 _p.batchStyleEles.updateStyle();
14826 var renderer = this.renderer();
14827
14828 // notify the renderer of queued eles and event types
14829 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14830 var eles = _p.batchNotifications[eventName];
14831 if (eles.empty()) {
14832 renderer.notify(eventName);
14833 } else {
14834 renderer.notify(eventName, eles);
14835 }
14836 });
14837 }
14838 return this;
14839 },
14840 batch: function batch(callback) {
14841 this.startBatch();
14842 callback();
14843 this.endBatch();
14844 return this;
14845 },
14846 // for backwards compatibility
14847 batchData: function batchData(map) {
14848 var cy = this;
14849 return this.batch(function () {
14850 var ids = Object.keys(map);
14851 for (var i = 0; i < ids.length; i++) {
14852 var id = ids[i];
14853 var data = map[id];
14854 var ele = cy.getElementById(id);
14855 ele.data(data);
14856 }
14857 });
14858 }
14859 };
14860
14861 var rendererDefaults = defaults$g({
14862 hideEdgesOnViewport: false,
14863 textureOnViewport: false,
14864 motionBlur: false,
14865 motionBlurOpacity: 0.05,
14866 pixelRatio: undefined,
14867 desktopTapThreshold: 4,
14868 touchTapThreshold: 8,
14869 wheelSensitivity: 1,
14870 debug: false,
14871 showFps: false
14872 });
14873 var corefn$4 = {
14874 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14875 var r = this._private.renderer;
14876 r.renderTo(context, zoom, pan, pxRatio);
14877 return this;
14878 },
14879 renderer: function renderer() {
14880 return this._private.renderer;
14881 },
14882 forceRender: function forceRender() {
14883 this.notify('draw');
14884 return this;
14885 },
14886 resize: function resize() {
14887 this.invalidateSize();
14888 this.emitAndNotify('resize');
14889 return this;
14890 },
14891 initRenderer: function initRenderer(options) {
14892 var cy = this;
14893 var RendererProto = cy.extension('renderer', options.name);
14894 if (RendererProto == null) {
14895 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14896 return;
14897 }
14898 if (options.wheelSensitivity !== undefined) {
14899 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.");
14900 }
14901 var rOpts = rendererDefaults(options);
14902 rOpts.cy = cy;
14903 cy._private.renderer = new RendererProto(rOpts);
14904 this.notify('init');
14905 },
14906 destroyRenderer: function destroyRenderer() {
14907 var cy = this;
14908 cy.notify('destroy'); // destroy the renderer
14909
14910 var domEle = cy.container();
14911 if (domEle) {
14912 domEle._cyreg = null;
14913 while (domEle.childNodes.length > 0) {
14914 domEle.removeChild(domEle.childNodes[0]);
14915 }
14916 }
14917 cy._private.renderer = null; // to be extra safe, remove the ref
14918 cy.mutableElements().forEach(function (ele) {
14919 var _p = ele._private;
14920 _p.rscratch = {};
14921 _p.rstyle = {};
14922 _p.animation.current = [];
14923 _p.animation.queue = [];
14924 });
14925 },
14926 onRender: function onRender(fn) {
14927 return this.on('render', fn);
14928 },
14929 offRender: function offRender(fn) {
14930 return this.off('render', fn);
14931 }
14932 };
14933 corefn$4.invalidateDimensions = corefn$4.resize;
14934
14935 var corefn$3 = {
14936 // get a collection
14937 // - empty collection on no args
14938 // - collection of elements in the graph on selector arg
14939 // - guarantee a returned collection when elements or collection specified
14940 collection: function collection(eles, opts) {
14941 if (string(eles)) {
14942 return this.$(eles);
14943 } else if (elementOrCollection(eles)) {
14944 return eles.collection();
14945 } else if (array(eles)) {
14946 if (!opts) {
14947 opts = {};
14948 }
14949 return new Collection(this, eles, opts.unique, opts.removed);
14950 }
14951 return new Collection(this);
14952 },
14953 nodes: function nodes(selector) {
14954 var nodes = this.$(function (ele) {
14955 return ele.isNode();
14956 });
14957 if (selector) {
14958 return nodes.filter(selector);
14959 }
14960 return nodes;
14961 },
14962 edges: function edges(selector) {
14963 var edges = this.$(function (ele) {
14964 return ele.isEdge();
14965 });
14966 if (selector) {
14967 return edges.filter(selector);
14968 }
14969 return edges;
14970 },
14971 // search the graph like jQuery
14972 $: function $(selector) {
14973 var eles = this._private.elements;
14974 if (selector) {
14975 return eles.filter(selector);
14976 } else {
14977 return eles.spawnSelf();
14978 }
14979 },
14980 mutableElements: function mutableElements() {
14981 return this._private.elements;
14982 }
14983 };
14984
14985 // aliases
14986 corefn$3.elements = corefn$3.filter = corefn$3.$;
14987
14988 var styfn$8 = {};
14989
14990 // keys for style blocks, e.g. ttfftt
14991 var TRUE = 't';
14992 var FALSE = 'f';
14993
14994 // (potentially expensive calculation)
14995 // apply the style to the element based on
14996 // - its bypass
14997 // - what selectors match it
14998 styfn$8.apply = function (eles) {
14999 var self = this;
15000 var _p = self._private;
15001 var cy = _p.cy;
15002 var updatedEles = cy.collection();
15003 for (var ie = 0; ie < eles.length; ie++) {
15004 var ele = eles[ie];
15005 var cxtMeta = self.getContextMeta(ele);
15006 if (cxtMeta.empty) {
15007 continue;
15008 }
15009 var cxtStyle = self.getContextStyle(cxtMeta);
15010 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
15011 if (ele._private.appliedInitStyle) {
15012 self.updateTransitions(ele, app.diffProps);
15013 } else {
15014 ele._private.appliedInitStyle = true;
15015 }
15016 var hintsDiff = self.updateStyleHints(ele);
15017 if (hintsDiff) {
15018 updatedEles.push(ele);
15019 }
15020 } // for elements
15021
15022 return updatedEles;
15023 };
15024 styfn$8.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
15025 var self = this;
15026 var cache = self._private.propDiffs = self._private.propDiffs || {};
15027 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
15028 var cachedVal = cache[dualCxtKey];
15029 if (cachedVal) {
15030 return cachedVal;
15031 }
15032 var diffProps = [];
15033 var addedProp = {};
15034 for (var i = 0; i < self.length; i++) {
15035 var cxt = self[i];
15036 var oldHasCxt = oldCxtKey[i] === TRUE;
15037 var newHasCxt = newCxtKey[i] === TRUE;
15038 var cxtHasDiffed = oldHasCxt !== newHasCxt;
15039 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
15040 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
15041 var props = void 0;
15042 if (cxtHasDiffed && cxtHasMappedProps) {
15043 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
15044 } else if (cxtHasDiffed) {
15045 props = cxt.properties; // need to check them all
15046 } else if (cxtHasMappedProps) {
15047 props = cxt.mappedProperties; // only need to check mapped
15048 }
15049
15050 for (var j = 0; j < props.length; j++) {
15051 var prop = props[j];
15052 var name = prop.name;
15053
15054 // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
15055 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
15056 // is cached)
15057 var laterCxtOverrides = false;
15058 for (var k = i + 1; k < self.length; k++) {
15059 var laterCxt = self[k];
15060 var hasLaterCxt = newCxtKey[k] === TRUE;
15061 if (!hasLaterCxt) {
15062 continue;
15063 } // can't override unless the context is active
15064
15065 laterCxtOverrides = laterCxt.properties[prop.name] != null;
15066 if (laterCxtOverrides) {
15067 break;
15068 } // exit early as long as one later context overrides
15069 }
15070
15071 if (!addedProp[name] && !laterCxtOverrides) {
15072 addedProp[name] = true;
15073 diffProps.push(name);
15074 }
15075 } // for props
15076 } // if
15077 } // for contexts
15078
15079 cache[dualCxtKey] = diffProps;
15080 return diffProps;
15081 };
15082 styfn$8.getContextMeta = function (ele) {
15083 var self = this;
15084 var cxtKey = '';
15085 var diffProps;
15086 var prevKey = ele._private.styleCxtKey || '';
15087
15088 // get the cxt key
15089 for (var i = 0; i < self.length; i++) {
15090 var context = self[i];
15091 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
15092
15093 if (contextSelectorMatches) {
15094 cxtKey += TRUE;
15095 } else {
15096 cxtKey += FALSE;
15097 }
15098 } // for context
15099
15100 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
15101 ele._private.styleCxtKey = cxtKey;
15102 return {
15103 key: cxtKey,
15104 diffPropNames: diffProps,
15105 empty: diffProps.length === 0
15106 };
15107 };
15108
15109 // gets a computed ele style object based on matched contexts
15110 styfn$8.getContextStyle = function (cxtMeta) {
15111 var cxtKey = cxtMeta.key;
15112 var self = this;
15113 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {};
15114
15115 // if already computed style, returned cached copy
15116 if (cxtStyles[cxtKey]) {
15117 return cxtStyles[cxtKey];
15118 }
15119 var style = {
15120 _private: {
15121 key: cxtKey
15122 }
15123 };
15124 for (var i = 0; i < self.length; i++) {
15125 var cxt = self[i];
15126 var hasCxt = cxtKey[i] === TRUE;
15127 if (!hasCxt) {
15128 continue;
15129 }
15130 for (var j = 0; j < cxt.properties.length; j++) {
15131 var prop = cxt.properties[j];
15132 style[prop.name] = prop;
15133 }
15134 }
15135 cxtStyles[cxtKey] = style;
15136 return style;
15137 };
15138 styfn$8.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
15139 var self = this;
15140 var diffProps = cxtMeta.diffPropNames;
15141 var retDiffProps = {};
15142 var types = self.types;
15143 for (var i = 0; i < diffProps.length; i++) {
15144 var diffPropName = diffProps[i];
15145 var cxtProp = cxtStyle[diffPropName];
15146 var eleProp = ele.pstyle(diffPropName);
15147 if (!cxtProp) {
15148 // no context prop means delete
15149 if (!eleProp) {
15150 continue; // no existing prop means nothing needs to be removed
15151 // nb affects initial application on mapped values like control-point-distances
15152 } else if (eleProp.bypass) {
15153 cxtProp = {
15154 name: diffPropName,
15155 deleteBypassed: true
15156 };
15157 } else {
15158 cxtProp = {
15159 name: diffPropName,
15160 "delete": true
15161 };
15162 }
15163 }
15164
15165 // save cycles when the context prop doesn't need to be applied
15166 if (eleProp === cxtProp) {
15167 continue;
15168 }
15169
15170 // save cycles when a mapped context prop doesn't need to be applied
15171 if (cxtProp.mapped === types.fn // context prop is function mapper
15172 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
15173 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
15174 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
15175 ) {
15176 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
15177 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
15178 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
15179
15180 if (fnValue === mapping.prevFnValue) {
15181 continue;
15182 }
15183 }
15184 var retDiffProp = retDiffProps[diffPropName] = {
15185 prev: eleProp
15186 };
15187 self.applyParsedProperty(ele, cxtProp);
15188 retDiffProp.next = ele.pstyle(diffPropName);
15189 if (retDiffProp.next && retDiffProp.next.bypass) {
15190 retDiffProp.next = retDiffProp.next.bypassed;
15191 }
15192 }
15193 return {
15194 diffProps: retDiffProps
15195 };
15196 };
15197 styfn$8.updateStyleHints = function (ele) {
15198 var _p = ele._private;
15199 var self = this;
15200 var propNames = self.propertyGroupNames;
15201 var propGrKeys = self.propertyGroupKeys;
15202 var propHash = function propHash(ele, propNames, seedKey) {
15203 return self.getPropertiesHash(ele, propNames, seedKey);
15204 };
15205 var oldStyleKey = _p.styleKey;
15206 if (ele.removed()) {
15207 return false;
15208 }
15209 var isNode = _p.group === 'nodes';
15210
15211 // get the style key hashes per prop group
15212 // but lazily -- only use non-default prop values to reduce the number of hashes
15213 //
15214
15215 var overriddenStyles = ele._private.style;
15216 propNames = Object.keys(overriddenStyles);
15217 for (var i = 0; i < propGrKeys.length; i++) {
15218 var grKey = propGrKeys[i];
15219 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15220 }
15221 var updateGrKey1 = function updateGrKey1(val, grKey) {
15222 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
15223 };
15224 var updateGrKey2 = function updateGrKey2(val, grKey) {
15225 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
15226 };
15227 var updateGrKey = function updateGrKey(val, grKey) {
15228 updateGrKey1(val, grKey);
15229 updateGrKey2(val, grKey);
15230 };
15231 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
15232 for (var j = 0; j < strVal.length; j++) {
15233 var ch = strVal.charCodeAt(j);
15234 updateGrKey1(ch, grKey);
15235 updateGrKey2(ch, grKey);
15236 }
15237 };
15238
15239 // - hashing works on 32 bit ints b/c we use bitwise ops
15240 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
15241 // - raise up small numbers so more significant digits are seen by hashing
15242 // - make small numbers larger than a normal value to avoid collisions
15243 // - works in practice and it's relatively cheap
15244 var N = 2000000000;
15245 var cleanNum = function cleanNum(val) {
15246 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
15247 };
15248 for (var _i = 0; _i < propNames.length; _i++) {
15249 var name = propNames[_i];
15250 var parsedProp = overriddenStyles[name];
15251 if (parsedProp == null) {
15252 continue;
15253 }
15254 var propInfo = this.properties[name];
15255 var type = propInfo.type;
15256 var _grKey = propInfo.groupKey;
15257 var normalizedNumberVal = void 0;
15258 if (propInfo.hashOverride != null) {
15259 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
15260 } else if (parsedProp.pfValue != null) {
15261 normalizedNumberVal = parsedProp.pfValue;
15262 }
15263
15264 // might not be a number if it allows enums
15265 var numberVal = propInfo.enums == null ? parsedProp.value : null;
15266 var haveNormNum = normalizedNumberVal != null;
15267 var haveUnitedNum = numberVal != null;
15268 var haveNum = haveNormNum || haveUnitedNum;
15269 var units = parsedProp.units;
15270
15271 // numbers are cheaper to hash than strings
15272 // 1 hash op vs n hash ops (for length n string)
15273 if (type.number && haveNum && !type.multiple) {
15274 var v = haveNormNum ? normalizedNumberVal : numberVal;
15275 updateGrKey(cleanNum(v), _grKey);
15276 if (!haveNormNum && units != null) {
15277 updateGrKeyWStr(units, _grKey);
15278 }
15279 } else {
15280 updateGrKeyWStr(parsedProp.strValue, _grKey);
15281 }
15282 }
15283
15284 // overall style key
15285 //
15286
15287 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
15288 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
15289 var _grKey2 = propGrKeys[_i2];
15290 var grHash = _p.styleKeys[_grKey2];
15291 hash[0] = hashInt(grHash[0], hash[0]);
15292 hash[1] = hashIntAlt(grHash[1], hash[1]);
15293 }
15294 _p.styleKey = combineHashes(hash[0], hash[1]);
15295
15296 // label dims
15297 //
15298
15299 var sk = _p.styleKeys;
15300 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
15301 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
15302 _p.labelKey = combineHashesArray(labelKeys);
15303 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
15304 if (!isNode) {
15305 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
15306 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
15307 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
15308 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
15309 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
15310 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
15311 }
15312
15313 // node
15314 //
15315
15316 if (isNode) {
15317 var _p$styleKeys = _p.styleKeys,
15318 nodeBody = _p$styleKeys.nodeBody,
15319 nodeBorder = _p$styleKeys.nodeBorder,
15320 nodeOutline = _p$styleKeys.nodeOutline,
15321 backgroundImage = _p$styleKeys.backgroundImage,
15322 compound = _p$styleKeys.compound,
15323 pie = _p$styleKeys.pie;
15324 var nodeKeys = [nodeBody, nodeBorder, nodeOutline, backgroundImage, compound, pie].filter(function (k) {
15325 return k != null;
15326 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
15327 _p.nodeKey = combineHashesArray(nodeKeys);
15328 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
15329 }
15330 return oldStyleKey !== _p.styleKey;
15331 };
15332 styfn$8.clearStyleHints = function (ele) {
15333 var _p = ele._private;
15334 _p.styleCxtKey = '';
15335 _p.styleKeys = {};
15336 _p.styleKey = null;
15337 _p.labelKey = null;
15338 _p.labelStyleKey = null;
15339 _p.sourceLabelKey = null;
15340 _p.sourceLabelStyleKey = null;
15341 _p.targetLabelKey = null;
15342 _p.targetLabelStyleKey = null;
15343 _p.nodeKey = null;
15344 _p.hasPie = null;
15345 };
15346
15347 // apply a property to the style (for internal use)
15348 // returns whether application was successful
15349 //
15350 // now, this function flattens the property, and here's how:
15351 //
15352 // for parsedProp:{ bypass: true, deleteBypass: true }
15353 // no property is generated, instead the bypass property in the
15354 // element's style is replaced by what's pointed to by the `bypassed`
15355 // field in the bypass property (i.e. restoring the property the
15356 // bypass was overriding)
15357 //
15358 // for parsedProp:{ mapped: truthy }
15359 // the generated flattenedProp:{ mapping: prop }
15360 //
15361 // for parsedProp:{ bypass: true }
15362 // the generated flattenedProp:{ bypassed: parsedProp }
15363 styfn$8.applyParsedProperty = function (ele, parsedProp) {
15364 var self = this;
15365 var prop = parsedProp;
15366 var style = ele._private.style;
15367 var flatProp;
15368 var types = self.types;
15369 var type = self.properties[prop.name].type;
15370 var propIsBypass = prop.bypass;
15371 var origProp = style[prop.name];
15372 var origPropIsBypass = origProp && origProp.bypass;
15373 var _p = ele._private;
15374 var flatPropMapping = 'mapping';
15375 var getVal = function getVal(p) {
15376 if (p == null) {
15377 return null;
15378 } else if (p.pfValue != null) {
15379 return p.pfValue;
15380 } else {
15381 return p.value;
15382 }
15383 };
15384 var checkTriggers = function checkTriggers() {
15385 var fromVal = getVal(origProp);
15386 var toVal = getVal(prop);
15387 self.checkTriggers(ele, prop.name, fromVal, toVal);
15388 };
15389
15390 // edge sanity checks to prevent the client from making serious mistakes
15391 if (parsedProp.name === 'curve-style' && ele.isEdge() && (
15392 // loops must be bundled beziers
15393 parsedProp.value !== 'bezier' && ele.isLoop() ||
15394 // edges connected to compound nodes can not be haystacks
15395 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
15396 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
15397 }
15398 if (prop["delete"]) {
15399 // delete the property and use the default value on falsey value
15400 style[prop.name] = undefined;
15401 checkTriggers();
15402 return true;
15403 }
15404 if (prop.deleteBypassed) {
15405 // delete the property that the
15406 if (!origProp) {
15407 checkTriggers();
15408 return true; // can't delete if no prop
15409 } else if (origProp.bypass) {
15410 // delete bypassed
15411 origProp.bypassed = undefined;
15412 checkTriggers();
15413 return true;
15414 } else {
15415 return false; // we're unsuccessful deleting the bypassed
15416 }
15417 }
15418
15419 // check if we need to delete the current bypass
15420 if (prop.deleteBypass) {
15421 // then this property is just here to indicate we need to delete
15422 if (!origProp) {
15423 checkTriggers();
15424 return true; // property is already not defined
15425 } else if (origProp.bypass) {
15426 // then replace the bypass property with the original
15427 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
15428 style[prop.name] = origProp.bypassed;
15429 checkTriggers();
15430 return true;
15431 } else {
15432 return false; // we're unsuccessful deleting the bypass
15433 }
15434 }
15435
15436 var printMappingErr = function printMappingErr() {
15437 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');
15438 };
15439
15440 // put the property in the style objects
15441 switch (prop.mapped) {
15442 // flatten the property if mapped
15443 case types.mapData:
15444 {
15445 // flatten the field (e.g. data.foo.bar)
15446 var fields = prop.field.split('.');
15447 var fieldVal = _p.data;
15448 for (var i = 0; i < fields.length && fieldVal; i++) {
15449 var field = fields[i];
15450 fieldVal = fieldVal[field];
15451 }
15452 if (fieldVal == null) {
15453 printMappingErr();
15454 return false;
15455 }
15456 var percent;
15457 if (!number$1(fieldVal)) {
15458 // then don't apply and fall back on the existing style
15459 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15460 return false;
15461 } else {
15462 var fieldWidth = prop.fieldMax - prop.fieldMin;
15463 if (fieldWidth === 0) {
15464 // safety check -- not strictly necessary as no props of zero range should be passed here
15465 percent = 0;
15466 } else {
15467 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15468 }
15469 }
15470
15471 // make sure to bound percent value
15472 if (percent < 0) {
15473 percent = 0;
15474 } else if (percent > 1) {
15475 percent = 1;
15476 }
15477 if (type.color) {
15478 var r1 = prop.valueMin[0];
15479 var r2 = prop.valueMax[0];
15480 var g1 = prop.valueMin[1];
15481 var g2 = prop.valueMax[1];
15482 var b1 = prop.valueMin[2];
15483 var b2 = prop.valueMax[2];
15484 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15485 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15486 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)];
15487 flatProp = {
15488 // colours are simple, so just create the flat property instead of expensive string parsing
15489 bypass: prop.bypass,
15490 // we're a bypass if the mapping property is a bypass
15491 name: prop.name,
15492 value: clr,
15493 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15494 };
15495 } else if (type.number) {
15496 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15497 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15498 } else {
15499 return false; // can only map to colours and numbers
15500 }
15501
15502 if (!flatProp) {
15503 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15504 printMappingErr();
15505 return false;
15506 }
15507 flatProp.mapping = prop; // keep a reference to the mapping
15508 prop = flatProp; // the flattened (mapped) property is the one we want
15509
15510 break;
15511 }
15512
15513 // direct mapping
15514 case types.data:
15515 {
15516 // flatten the field (e.g. data.foo.bar)
15517 var _fields = prop.field.split('.');
15518 var _fieldVal = _p.data;
15519 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15520 var _field = _fields[_i3];
15521 _fieldVal = _fieldVal[_field];
15522 }
15523 if (_fieldVal != null) {
15524 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15525 }
15526 if (!flatProp) {
15527 // if we can't flatten the property, then don't apply and fall back on the existing style
15528 printMappingErr();
15529 return false;
15530 }
15531 flatProp.mapping = prop; // keep a reference to the mapping
15532 prop = flatProp; // the flattened (mapped) property is the one we want
15533
15534 break;
15535 }
15536 case types.fn:
15537 {
15538 var fn = prop.value;
15539 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15540
15541 prop.prevFnValue = fnRetVal;
15542 if (fnRetVal == null) {
15543 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15544 return false;
15545 }
15546 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15547 if (!flatProp) {
15548 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15549 return false;
15550 }
15551 flatProp.mapping = copy(prop); // keep a reference to the mapping
15552 prop = flatProp; // the flattened (mapped) property is the one we want
15553
15554 break;
15555 }
15556 case undefined:
15557 break;
15558 // just set the property
15559
15560 default:
15561 return false;
15562 // not a valid mapping
15563 }
15564
15565 // if the property is a bypass property, then link the resultant property to the original one
15566 if (propIsBypass) {
15567 if (origPropIsBypass) {
15568 // then this bypass overrides the existing one
15569 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15570 } else {
15571 // then link the orig prop to the new bypass
15572 prop.bypassed = origProp;
15573 }
15574 style[prop.name] = prop; // and set
15575 } else {
15576 // prop is not bypass
15577 if (origPropIsBypass) {
15578 // then keep the orig prop (since it's a bypass) and link to the new prop
15579 origProp.bypassed = prop;
15580 } else {
15581 // then just replace the old prop with the new one
15582 style[prop.name] = prop;
15583 }
15584 }
15585 checkTriggers();
15586 return true;
15587 };
15588 styfn$8.cleanElements = function (eles, keepBypasses) {
15589 for (var i = 0; i < eles.length; i++) {
15590 var ele = eles[i];
15591 this.clearStyleHints(ele);
15592 ele.dirtyCompoundBoundsCache();
15593 ele.dirtyBoundingBoxCache();
15594 if (!keepBypasses) {
15595 ele._private.style = {};
15596 } else {
15597 var style = ele._private.style;
15598 var propNames = Object.keys(style);
15599 for (var j = 0; j < propNames.length; j++) {
15600 var propName = propNames[j];
15601 var eleProp = style[propName];
15602 if (eleProp != null) {
15603 if (eleProp.bypass) {
15604 eleProp.bypassed = null;
15605 } else {
15606 style[propName] = null;
15607 }
15608 }
15609 }
15610 }
15611 }
15612 };
15613
15614 // updates the visual style for all elements (useful for manual style modification after init)
15615 styfn$8.update = function () {
15616 var cy = this._private.cy;
15617 var eles = cy.mutableElements();
15618 eles.updateStyle();
15619 };
15620
15621 // diffProps : { name => { prev, next } }
15622 styfn$8.updateTransitions = function (ele, diffProps) {
15623 var self = this;
15624 var _p = ele._private;
15625 var props = ele.pstyle('transition-property').value;
15626 var duration = ele.pstyle('transition-duration').pfValue;
15627 var delay = ele.pstyle('transition-delay').pfValue;
15628 if (props.length > 0 && duration > 0) {
15629 var style = {};
15630
15631 // build up the style to animate towards
15632 var anyPrev = false;
15633 for (var i = 0; i < props.length; i++) {
15634 var prop = props[i];
15635 var styProp = ele.pstyle(prop);
15636 var diffProp = diffProps[prop];
15637 if (!diffProp) {
15638 continue;
15639 }
15640 var prevProp = diffProp.prev;
15641 var fromProp = prevProp;
15642 var toProp = diffProp.next != null ? diffProp.next : styProp;
15643 var diff = false;
15644 var initVal = void 0;
15645 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15646
15647 if (!fromProp) {
15648 continue;
15649 }
15650
15651 // consider px values
15652 if (number$1(fromProp.pfValue) && number$1(toProp.pfValue)) {
15653 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15654 initVal = fromProp.pfValue + initDt * diff;
15655
15656 // consider numerical values
15657 } else if (number$1(fromProp.value) && number$1(toProp.value)) {
15658 diff = toProp.value - fromProp.value; // nonzero is truthy
15659 initVal = fromProp.value + initDt * diff;
15660
15661 // consider colour values
15662 } else if (array(fromProp.value) && array(toProp.value)) {
15663 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15664 initVal = fromProp.strValue;
15665 }
15666
15667 // the previous value is good for an animation only if it's different
15668 if (diff) {
15669 style[prop] = toProp.strValue; // to val
15670 this.applyBypass(ele, prop, initVal); // from val
15671 anyPrev = true;
15672 }
15673 } // end if props allow ani
15674
15675 // can't transition if there's nothing previous to transition from
15676 if (!anyPrev) {
15677 return;
15678 }
15679 _p.transitioning = true;
15680 new Promise$1(function (resolve) {
15681 if (delay > 0) {
15682 ele.delayAnimation(delay).play().promise().then(resolve);
15683 } else {
15684 resolve();
15685 }
15686 }).then(function () {
15687 return ele.animation({
15688 style: style,
15689 duration: duration,
15690 easing: ele.pstyle('transition-timing-function').value,
15691 queue: false
15692 }).play().promise();
15693 }).then(function () {
15694 // if( !isBypass ){
15695 self.removeBypasses(ele, props);
15696 ele.emitAndNotify('style');
15697 // }
15698
15699 _p.transitioning = false;
15700 });
15701 } else if (_p.transitioning) {
15702 this.removeBypasses(ele, props);
15703 ele.emitAndNotify('style');
15704 _p.transitioning = false;
15705 }
15706 };
15707 styfn$8.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15708 var prop = this.properties[name];
15709 var triggerCheck = getTrigger(prop);
15710 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15711 onTrigger(prop);
15712 }
15713 };
15714 styfn$8.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15715 var _this = this;
15716 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15717 return prop.triggersZOrder;
15718 }, function () {
15719 _this._private.cy.notify('zorder', ele);
15720 });
15721 };
15722 styfn$8.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15723 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15724 return prop.triggersBounds;
15725 }, function (prop) {
15726 ele.dirtyCompoundBoundsCache();
15727 ele.dirtyBoundingBoxCache();
15728
15729 // if the prop change makes the bb of pll bezier edges invalid,
15730 // then dirty the pll edge bb cache as well
15731 if (
15732 // only for beziers -- so performance of other edges isn't affected
15733 prop.triggersBoundsOfParallelBeziers && name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) {
15734 ele.parallelEdges().forEach(function (pllEdge) {
15735 if (pllEdge.isBundledBezier()) {
15736 pllEdge.dirtyBoundingBoxCache();
15737 }
15738 });
15739 }
15740 if (prop.triggersBoundsOfConnectedEdges && name === 'display' && (fromValue === 'none' || toValue === 'none')) {
15741 ele.connectedEdges().forEach(function (edge) {
15742 edge.dirtyBoundingBoxCache();
15743 });
15744 }
15745 });
15746 };
15747 styfn$8.checkTriggers = function (ele, name, fromValue, toValue) {
15748 ele.dirtyStyleCache();
15749 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15750 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15751 };
15752
15753 var styfn$7 = {};
15754
15755 // bypasses are applied to an existing style on an element, and just tacked on temporarily
15756 // returns true iff application was successful for at least 1 specified property
15757 styfn$7.applyBypass = function (eles, name, value, updateTransitions) {
15758 var self = this;
15759 var props = [];
15760 var isBypass = true;
15761
15762 // put all the properties (can specify one or many) in an array after parsing them
15763 if (name === '*' || name === '**') {
15764 // apply to all property names
15765
15766 if (value !== undefined) {
15767 for (var i = 0; i < self.properties.length; i++) {
15768 var prop = self.properties[i];
15769 var _name = prop.name;
15770 var parsedProp = this.parse(_name, value, true);
15771 if (parsedProp) {
15772 props.push(parsedProp);
15773 }
15774 }
15775 }
15776 } else if (string(name)) {
15777 // then parse the single property
15778 var _parsedProp = this.parse(name, value, true);
15779 if (_parsedProp) {
15780 props.push(_parsedProp);
15781 }
15782 } else if (plainObject(name)) {
15783 // then parse each property
15784 var specifiedProps = name;
15785 updateTransitions = value;
15786 var names = Object.keys(specifiedProps);
15787 for (var _i = 0; _i < names.length; _i++) {
15788 var _name2 = names[_i];
15789 var _value = specifiedProps[_name2];
15790 if (_value === undefined) {
15791 // try camel case name too
15792 _value = specifiedProps[dash2camel(_name2)];
15793 }
15794 if (_value !== undefined) {
15795 var _parsedProp2 = this.parse(_name2, _value, true);
15796 if (_parsedProp2) {
15797 props.push(_parsedProp2);
15798 }
15799 }
15800 }
15801 } else {
15802 // can't do anything without well defined properties
15803 return false;
15804 }
15805
15806 // we've failed if there are no valid properties
15807 if (props.length === 0) {
15808 return false;
15809 }
15810
15811 // now, apply the bypass properties on the elements
15812 var ret = false; // return true if at least one succesful bypass applied
15813 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15814 // for each ele
15815 var ele = eles[_i2];
15816 var diffProps = {};
15817 var diffProp = void 0;
15818 for (var j = 0; j < props.length; j++) {
15819 // for each prop
15820 var _prop = props[j];
15821 if (updateTransitions) {
15822 var prevProp = ele.pstyle(_prop.name);
15823 diffProp = diffProps[_prop.name] = {
15824 prev: prevProp
15825 };
15826 }
15827 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15828 if (updateTransitions) {
15829 diffProp.next = ele.pstyle(_prop.name);
15830 }
15831 } // for props
15832
15833 if (ret) {
15834 this.updateStyleHints(ele);
15835 }
15836 if (updateTransitions) {
15837 this.updateTransitions(ele, diffProps, isBypass);
15838 }
15839 } // for eles
15840
15841 return ret;
15842 };
15843
15844 // only useful in specific cases like animation
15845 styfn$7.overrideBypass = function (eles, name, value) {
15846 name = camel2dash(name);
15847 for (var i = 0; i < eles.length; i++) {
15848 var ele = eles[i];
15849 var prop = ele._private.style[name];
15850 var type = this.properties[name].type;
15851 var isColor = type.color;
15852 var isMulti = type.mutiple;
15853 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15854 if (!prop || !prop.bypass) {
15855 // need a bypass if one doesn't exist
15856 this.applyBypass(ele, name, value);
15857 } else {
15858 prop.value = value;
15859 if (prop.pfValue != null) {
15860 prop.pfValue = value;
15861 }
15862 if (isColor) {
15863 prop.strValue = 'rgb(' + value.join(',') + ')';
15864 } else if (isMulti) {
15865 prop.strValue = value.join(' ');
15866 } else {
15867 prop.strValue = '' + value;
15868 }
15869 this.updateStyleHints(ele);
15870 }
15871 this.checkTriggers(ele, name, oldValue, value);
15872 }
15873 };
15874 styfn$7.removeAllBypasses = function (eles, updateTransitions) {
15875 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15876 };
15877 styfn$7.removeBypasses = function (eles, props, updateTransitions) {
15878 var isBypass = true;
15879 for (var j = 0; j < eles.length; j++) {
15880 var ele = eles[j];
15881 var diffProps = {};
15882 for (var i = 0; i < props.length; i++) {
15883 var name = props[i];
15884 var prop = this.properties[name];
15885 var prevProp = ele.pstyle(prop.name);
15886 if (!prevProp || !prevProp.bypass) {
15887 // if a bypass doesn't exist for the prop, nothing needs to be removed
15888 continue;
15889 }
15890 var value = ''; // empty => remove bypass
15891 var parsedProp = this.parse(name, value, true);
15892 var diffProp = diffProps[prop.name] = {
15893 prev: prevProp
15894 };
15895 this.applyParsedProperty(ele, parsedProp);
15896 diffProp.next = ele.pstyle(prop.name);
15897 } // for props
15898
15899 this.updateStyleHints(ele);
15900 if (updateTransitions) {
15901 this.updateTransitions(ele, diffProps, isBypass);
15902 }
15903 } // for eles
15904 };
15905
15906 var styfn$6 = {};
15907
15908 // gets what an em size corresponds to in pixels relative to a dom element
15909 styfn$6.getEmSizeInPixels = function () {
15910 var px = this.containerCss('font-size');
15911 if (px != null) {
15912 return parseFloat(px);
15913 } else {
15914 return 1; // for headless
15915 }
15916 };
15917
15918 // gets css property from the core container
15919 styfn$6.containerCss = function (propName) {
15920 var cy = this._private.cy;
15921 var domElement = cy.container();
15922 var containerWindow = cy.window();
15923 if (containerWindow && domElement && containerWindow.getComputedStyle) {
15924 return containerWindow.getComputedStyle(domElement).getPropertyValue(propName);
15925 }
15926 };
15927
15928 var styfn$5 = {};
15929
15930 // gets the rendered style for an element
15931 styfn$5.getRenderedStyle = function (ele, prop) {
15932 if (prop) {
15933 return this.getStylePropertyValue(ele, prop, true);
15934 } else {
15935 return this.getRawStyle(ele, true);
15936 }
15937 };
15938
15939 // gets the raw style for an element
15940 styfn$5.getRawStyle = function (ele, isRenderedVal) {
15941 var self = this;
15942 ele = ele[0]; // insure it's an element
15943
15944 if (ele) {
15945 var rstyle = {};
15946 for (var i = 0; i < self.properties.length; i++) {
15947 var prop = self.properties[i];
15948 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15949 if (val != null) {
15950 rstyle[prop.name] = val;
15951 rstyle[dash2camel(prop.name)] = val;
15952 }
15953 }
15954 return rstyle;
15955 }
15956 };
15957 styfn$5.getIndexedStyle = function (ele, property, subproperty, index) {
15958 var pstyle = ele.pstyle(property)[subproperty][index];
15959 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15960 };
15961 styfn$5.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15962 var self = this;
15963 ele = ele[0]; // insure it's an element
15964
15965 if (ele) {
15966 var prop = self.properties[propName];
15967 if (prop.alias) {
15968 prop = prop.pointsTo;
15969 }
15970 var type = prop.type;
15971 var styleProp = ele.pstyle(prop.name);
15972 if (styleProp) {
15973 var value = styleProp.value,
15974 units = styleProp.units,
15975 strValue = styleProp.strValue;
15976 if (isRenderedVal && type.number && value != null && number$1(value)) {
15977 var zoom = ele.cy().zoom();
15978 var getRenderedValue = function getRenderedValue(val) {
15979 return val * zoom;
15980 };
15981 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15982 return getRenderedValue(val) + units;
15983 };
15984 var isArrayValue = array(value);
15985 var haveUnits = isArrayValue ? units.every(function (u) {
15986 return u != null;
15987 }) : units != null;
15988 if (haveUnits) {
15989 if (isArrayValue) {
15990 return value.map(function (v, i) {
15991 return getValueStringWithUnits(v, units[i]);
15992 }).join(' ');
15993 } else {
15994 return getValueStringWithUnits(value, units);
15995 }
15996 } else {
15997 if (isArrayValue) {
15998 return value.map(function (v) {
15999 return string(v) ? v : '' + getRenderedValue(v);
16000 }).join(' ');
16001 } else {
16002 return '' + getRenderedValue(value);
16003 }
16004 }
16005 } else if (strValue != null) {
16006 return strValue;
16007 }
16008 }
16009 return null;
16010 }
16011 };
16012 styfn$5.getAnimationStartStyle = function (ele, aniProps) {
16013 var rstyle = {};
16014 for (var i = 0; i < aniProps.length; i++) {
16015 var aniProp = aniProps[i];
16016 var name = aniProp.name;
16017 var styleProp = ele.pstyle(name);
16018 if (styleProp !== undefined) {
16019 // then make a prop of it
16020 if (plainObject(styleProp)) {
16021 styleProp = this.parse(name, styleProp.strValue);
16022 } else {
16023 styleProp = this.parse(name, styleProp);
16024 }
16025 }
16026 if (styleProp) {
16027 rstyle[name] = styleProp;
16028 }
16029 }
16030 return rstyle;
16031 };
16032 styfn$5.getPropsList = function (propsObj) {
16033 var self = this;
16034 var rstyle = [];
16035 var style = propsObj;
16036 var props = self.properties;
16037 if (style) {
16038 var names = Object.keys(style);
16039 for (var i = 0; i < names.length; i++) {
16040 var name = names[i];
16041 var val = style[name];
16042 var prop = props[name] || props[camel2dash(name)];
16043 var styleProp = this.parse(prop.name, val);
16044 if (styleProp) {
16045 rstyle.push(styleProp);
16046 }
16047 }
16048 }
16049 return rstyle;
16050 };
16051 styfn$5.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
16052 var hash = seed.slice();
16053 var name, val, strVal, chVal;
16054 var i, j;
16055 for (i = 0; i < propNames.length; i++) {
16056 name = propNames[i];
16057 val = ele.pstyle(name, false);
16058 if (val == null) {
16059 continue;
16060 } else if (val.pfValue != null) {
16061 hash[0] = hashInt(chVal, hash[0]);
16062 hash[1] = hashIntAlt(chVal, hash[1]);
16063 } else {
16064 strVal = val.strValue;
16065 for (j = 0; j < strVal.length; j++) {
16066 chVal = strVal.charCodeAt(j);
16067 hash[0] = hashInt(chVal, hash[0]);
16068 hash[1] = hashIntAlt(chVal, hash[1]);
16069 }
16070 }
16071 }
16072 return hash;
16073 };
16074 styfn$5.getPropertiesHash = styfn$5.getNonDefaultPropertiesHash;
16075
16076 var styfn$4 = {};
16077 styfn$4.appendFromJson = function (json) {
16078 var style = this;
16079 for (var i = 0; i < json.length; i++) {
16080 var context = json[i];
16081 var selector = context.selector;
16082 var props = context.style || context.css;
16083 var names = Object.keys(props);
16084 style.selector(selector); // apply selector
16085
16086 for (var j = 0; j < names.length; j++) {
16087 var name = names[j];
16088 var value = props[name];
16089 style.css(name, value); // apply property
16090 }
16091 }
16092
16093 return style;
16094 };
16095
16096 // accessible cy.style() function
16097 styfn$4.fromJson = function (json) {
16098 var style = this;
16099 style.resetToDefault();
16100 style.appendFromJson(json);
16101 return style;
16102 };
16103
16104 // get json from cy.style() api
16105 styfn$4.json = function () {
16106 var json = [];
16107 for (var i = this.defaultLength; i < this.length; i++) {
16108 var cxt = this[i];
16109 var selector = cxt.selector;
16110 var props = cxt.properties;
16111 var css = {};
16112 for (var j = 0; j < props.length; j++) {
16113 var prop = props[j];
16114 css[prop.name] = prop.strValue;
16115 }
16116 json.push({
16117 selector: !selector ? 'core' : selector.toString(),
16118 style: css
16119 });
16120 }
16121 return json;
16122 };
16123
16124 var styfn$3 = {};
16125 styfn$3.appendFromString = function (string) {
16126 var self = this;
16127 var style = this;
16128 var remaining = '' + string;
16129 var selAndBlockStr;
16130 var blockRem;
16131 var propAndValStr;
16132
16133 // remove comments from the style string
16134 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
16135 function removeSelAndBlockFromRemaining() {
16136 // remove the parsed selector and block from the remaining text to parse
16137 if (remaining.length > selAndBlockStr.length) {
16138 remaining = remaining.substr(selAndBlockStr.length);
16139 } else {
16140 remaining = '';
16141 }
16142 }
16143 function removePropAndValFromRem() {
16144 // remove the parsed property and value from the remaining block text to parse
16145 if (blockRem.length > propAndValStr.length) {
16146 blockRem = blockRem.substr(propAndValStr.length);
16147 } else {
16148 blockRem = '';
16149 }
16150 }
16151 for (;;) {
16152 var nothingLeftToParse = remaining.match(/^\s*$/);
16153 if (nothingLeftToParse) {
16154 break;
16155 }
16156 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
16157 if (!selAndBlock) {
16158 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
16159 break;
16160 }
16161 selAndBlockStr = selAndBlock[0];
16162
16163 // parse the selector
16164 var selectorStr = selAndBlock[1];
16165 if (selectorStr !== 'core') {
16166 var selector = new Selector(selectorStr);
16167 if (selector.invalid) {
16168 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr);
16169
16170 // skip this selector and block
16171 removeSelAndBlockFromRemaining();
16172 continue;
16173 }
16174 }
16175
16176 // parse the block of properties and values
16177 var blockStr = selAndBlock[2];
16178 var invalidBlock = false;
16179 blockRem = blockStr;
16180 var props = [];
16181 for (;;) {
16182 var _nothingLeftToParse = blockRem.match(/^\s*$/);
16183 if (_nothingLeftToParse) {
16184 break;
16185 }
16186 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)(?:\s*;|\s*$)/);
16187 if (!propAndVal) {
16188 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
16189 invalidBlock = true;
16190 break;
16191 }
16192 propAndValStr = propAndVal[0];
16193 var propStr = propAndVal[1];
16194 var valStr = propAndVal[2];
16195 var prop = self.properties[propStr];
16196 if (!prop) {
16197 warn('Skipping property: Invalid property name in: ' + propAndValStr);
16198
16199 // skip this property in the block
16200 removePropAndValFromRem();
16201 continue;
16202 }
16203 var parsedProp = style.parse(propStr, valStr);
16204 if (!parsedProp) {
16205 warn('Skipping property: Invalid property definition in: ' + propAndValStr);
16206
16207 // skip this property in the block
16208 removePropAndValFromRem();
16209 continue;
16210 }
16211 props.push({
16212 name: propStr,
16213 val: valStr
16214 });
16215 removePropAndValFromRem();
16216 }
16217 if (invalidBlock) {
16218 removeSelAndBlockFromRemaining();
16219 break;
16220 }
16221
16222 // put the parsed block in the style
16223 style.selector(selectorStr);
16224 for (var i = 0; i < props.length; i++) {
16225 var _prop = props[i];
16226 style.css(_prop.name, _prop.val);
16227 }
16228 removeSelAndBlockFromRemaining();
16229 }
16230 return style;
16231 };
16232 styfn$3.fromString = function (string) {
16233 var style = this;
16234 style.resetToDefault();
16235 style.appendFromString(string);
16236 return style;
16237 };
16238
16239 var styfn$2 = {};
16240 (function () {
16241 var number$1 = number;
16242 var rgba = rgbaNoBackRefs;
16243 var hsla = hslaNoBackRefs;
16244 var hex3$1 = hex3;
16245 var hex6$1 = hex6;
16246 var data = function data(prefix) {
16247 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
16248 };
16249 var mapData = function mapData(prefix) {
16250 var mapArg = number$1 + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
16251 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number$1 + ')\\s*\\,\\s*(' + number$1 + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
16252 };
16253 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$'];
16254
16255 // each visual style property has a type and needs to be validated according to it
16256 styfn$2.types = {
16257 time: {
16258 number: true,
16259 min: 0,
16260 units: 's|ms',
16261 implicitUnits: 'ms'
16262 },
16263 percent: {
16264 number: true,
16265 min: 0,
16266 max: 100,
16267 units: '%',
16268 implicitUnits: '%'
16269 },
16270 percentages: {
16271 number: true,
16272 min: 0,
16273 max: 100,
16274 units: '%',
16275 implicitUnits: '%',
16276 multiple: true
16277 },
16278 zeroOneNumber: {
16279 number: true,
16280 min: 0,
16281 max: 1,
16282 unitless: true
16283 },
16284 zeroOneNumbers: {
16285 number: true,
16286 min: 0,
16287 max: 1,
16288 unitless: true,
16289 multiple: true
16290 },
16291 nOneOneNumber: {
16292 number: true,
16293 min: -1,
16294 max: 1,
16295 unitless: true
16296 },
16297 nonNegativeInt: {
16298 number: true,
16299 min: 0,
16300 integer: true,
16301 unitless: true
16302 },
16303 nonNegativeNumber: {
16304 number: true,
16305 min: 0,
16306 unitless: true
16307 },
16308 position: {
16309 enums: ['parent', 'origin']
16310 },
16311 nodeSize: {
16312 number: true,
16313 min: 0,
16314 enums: ['label']
16315 },
16316 number: {
16317 number: true,
16318 unitless: true
16319 },
16320 numbers: {
16321 number: true,
16322 unitless: true,
16323 multiple: true
16324 },
16325 positiveNumber: {
16326 number: true,
16327 unitless: true,
16328 min: 0,
16329 strictMin: true
16330 },
16331 size: {
16332 number: true,
16333 min: 0
16334 },
16335 bidirectionalSize: {
16336 number: true
16337 },
16338 // allows negative
16339 bidirectionalSizeMaybePercent: {
16340 number: true,
16341 allowPercent: true
16342 },
16343 // allows negative
16344 bidirectionalSizes: {
16345 number: true,
16346 multiple: true
16347 },
16348 // allows negative
16349 sizeMaybePercent: {
16350 number: true,
16351 min: 0,
16352 allowPercent: true
16353 },
16354 axisDirection: {
16355 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16356 },
16357 paddingRelativeTo: {
16358 enums: ['width', 'height', 'average', 'min', 'max']
16359 },
16360 bgWH: {
16361 number: true,
16362 min: 0,
16363 allowPercent: true,
16364 enums: ['auto'],
16365 multiple: true
16366 },
16367 bgPos: {
16368 number: true,
16369 allowPercent: true,
16370 multiple: true
16371 },
16372 bgRelativeTo: {
16373 enums: ['inner', 'include-padding'],
16374 multiple: true
16375 },
16376 bgRepeat: {
16377 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16378 multiple: true
16379 },
16380 bgFit: {
16381 enums: ['none', 'contain', 'cover'],
16382 multiple: true
16383 },
16384 bgCrossOrigin: {
16385 enums: ['anonymous', 'use-credentials', 'null'],
16386 multiple: true
16387 },
16388 bgClip: {
16389 enums: ['none', 'node'],
16390 multiple: true
16391 },
16392 bgContainment: {
16393 enums: ['inside', 'over'],
16394 multiple: true
16395 },
16396 color: {
16397 color: true
16398 },
16399 colors: {
16400 color: true,
16401 multiple: true
16402 },
16403 fill: {
16404 enums: ['solid', 'linear-gradient', 'radial-gradient']
16405 },
16406 bool: {
16407 enums: ['yes', 'no']
16408 },
16409 bools: {
16410 enums: ['yes', 'no'],
16411 multiple: true
16412 },
16413 lineStyle: {
16414 enums: ['solid', 'dotted', 'dashed']
16415 },
16416 lineCap: {
16417 enums: ['butt', 'round', 'square']
16418 },
16419 linePosition: {
16420 enums: ['center', 'inside', 'outside']
16421 },
16422 lineJoin: {
16423 enums: ['round', 'bevel', 'miter']
16424 },
16425 borderStyle: {
16426 enums: ['solid', 'dotted', 'dashed', 'double']
16427 },
16428 curveStyle: {
16429 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'straight-triangle', 'taxi', 'round-segments', 'round-taxi']
16430 },
16431 radiusType: {
16432 enums: ['arc-radius', 'influence-radius'],
16433 multiple: true
16434 },
16435 fontFamily: {
16436 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16437 },
16438 fontStyle: {
16439 enums: ['italic', 'normal', 'oblique']
16440 },
16441 fontWeight: {
16442 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16443 },
16444 textDecoration: {
16445 enums: ['none', 'underline', 'overline', 'line-through']
16446 },
16447 textTransform: {
16448 enums: ['none', 'uppercase', 'lowercase']
16449 },
16450 textWrap: {
16451 enums: ['none', 'wrap', 'ellipsis']
16452 },
16453 textOverflowWrap: {
16454 enums: ['whitespace', 'anywhere']
16455 },
16456 textBackgroundShape: {
16457 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16458 },
16459 nodeShape: {
16460 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', 'right-rhomboid', 'polygon']
16461 },
16462 overlayShape: {
16463 enums: ['roundrectangle', 'round-rectangle', 'ellipse']
16464 },
16465 cornerRadius: {
16466 number: true,
16467 min: 0,
16468 units: 'px|em',
16469 implicitUnits: 'px',
16470 enums: ['auto']
16471 },
16472 compoundIncludeLabels: {
16473 enums: ['include', 'exclude']
16474 },
16475 arrowShape: {
16476 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16477 },
16478 arrowFill: {
16479 enums: ['filled', 'hollow']
16480 },
16481 arrowWidth: {
16482 number: true,
16483 units: '%|px|em',
16484 implicitUnits: 'px',
16485 enums: ['match-line']
16486 },
16487 display: {
16488 enums: ['element', 'none']
16489 },
16490 visibility: {
16491 enums: ['hidden', 'visible']
16492 },
16493 zCompoundDepth: {
16494 enums: ['bottom', 'orphan', 'auto', 'top']
16495 },
16496 zIndexCompare: {
16497 enums: ['auto', 'manual']
16498 },
16499 valign: {
16500 enums: ['top', 'center', 'bottom']
16501 },
16502 halign: {
16503 enums: ['left', 'center', 'right']
16504 },
16505 justification: {
16506 enums: ['left', 'center', 'right', 'auto']
16507 },
16508 text: {
16509 string: true
16510 },
16511 data: {
16512 mapping: true,
16513 regex: data('data')
16514 },
16515 layoutData: {
16516 mapping: true,
16517 regex: data('layoutData')
16518 },
16519 scratch: {
16520 mapping: true,
16521 regex: data('scratch')
16522 },
16523 mapData: {
16524 mapping: true,
16525 regex: mapData('mapData')
16526 },
16527 mapLayoutData: {
16528 mapping: true,
16529 regex: mapData('mapLayoutData')
16530 },
16531 mapScratch: {
16532 mapping: true,
16533 regex: mapData('mapScratch')
16534 },
16535 fn: {
16536 mapping: true,
16537 fn: true
16538 },
16539 url: {
16540 regexes: urlRegexes,
16541 singleRegexMatchValue: true
16542 },
16543 urls: {
16544 regexes: urlRegexes,
16545 singleRegexMatchValue: true,
16546 multiple: true
16547 },
16548 propList: {
16549 propList: true
16550 },
16551 angle: {
16552 number: true,
16553 units: 'deg|rad',
16554 implicitUnits: 'rad'
16555 },
16556 textRotation: {
16557 number: true,
16558 units: 'deg|rad',
16559 implicitUnits: 'rad',
16560 enums: ['none', 'autorotate']
16561 },
16562 polygonPointList: {
16563 number: true,
16564 multiple: true,
16565 evenMultiple: true,
16566 min: -1,
16567 max: 1,
16568 unitless: true
16569 },
16570 edgeDistances: {
16571 enums: ['intersection', 'node-position', 'endpoints']
16572 },
16573 edgeEndpoint: {
16574 number: true,
16575 multiple: true,
16576 units: '%|px|em|deg|rad',
16577 implicitUnits: 'px',
16578 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16579 singleEnum: true,
16580 validate: function validate(valArr, unitsArr) {
16581 switch (valArr.length) {
16582 case 2:
16583 // can be % or px only
16584 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16585 case 1:
16586 // can be enum, deg, or rad only
16587 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16588 default:
16589 return false;
16590 }
16591 }
16592 },
16593 easing: {
16594 regexes: ['^(spring)\\s*\\(\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*,\\s*(' + number$1 + ')\\s*\\)$'],
16595 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']
16596 },
16597 gradientDirection: {
16598 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' // different order
16599 ]
16600 },
16601
16602 boundsExpansion: {
16603 number: true,
16604 multiple: true,
16605 min: 0,
16606 validate: function validate(valArr) {
16607 var length = valArr.length;
16608 return length === 1 || length === 2 || length === 4;
16609 }
16610 }
16611 };
16612 var diff = {
16613 zeroNonZero: function zeroNonZero(val1, val2) {
16614 if ((val1 == null || val2 == null) && val1 !== val2) {
16615 return true; // null cases could represent any value
16616 }
16617 if (val1 == 0 && val2 != 0) {
16618 return true;
16619 } else if (val1 != 0 && val2 == 0) {
16620 return true;
16621 } else {
16622 return false;
16623 }
16624 },
16625 any: function any(val1, val2) {
16626 return val1 != val2;
16627 },
16628 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16629 var empty1 = emptyString(str1);
16630 var empty2 = emptyString(str2);
16631 return empty1 && !empty2 || !empty1 && empty2;
16632 }
16633 };
16634
16635 // define visual style properties
16636 //
16637 // - n.b. adding a new group of props may require updates to updateStyleHints()
16638 // - adding new props to an existing group gets handled automatically
16639
16640 var t = styfn$2.types;
16641 var mainLabel = [{
16642 name: 'label',
16643 type: t.text,
16644 triggersBounds: diff.any,
16645 triggersZOrder: diff.emptyNonEmpty
16646 }, {
16647 name: 'text-rotation',
16648 type: t.textRotation,
16649 triggersBounds: diff.any
16650 }, {
16651 name: 'text-margin-x',
16652 type: t.bidirectionalSize,
16653 triggersBounds: diff.any
16654 }, {
16655 name: 'text-margin-y',
16656 type: t.bidirectionalSize,
16657 triggersBounds: diff.any
16658 }];
16659 var sourceLabel = [{
16660 name: 'source-label',
16661 type: t.text,
16662 triggersBounds: diff.any
16663 }, {
16664 name: 'source-text-rotation',
16665 type: t.textRotation,
16666 triggersBounds: diff.any
16667 }, {
16668 name: 'source-text-margin-x',
16669 type: t.bidirectionalSize,
16670 triggersBounds: diff.any
16671 }, {
16672 name: 'source-text-margin-y',
16673 type: t.bidirectionalSize,
16674 triggersBounds: diff.any
16675 }, {
16676 name: 'source-text-offset',
16677 type: t.size,
16678 triggersBounds: diff.any
16679 }];
16680 var targetLabel = [{
16681 name: 'target-label',
16682 type: t.text,
16683 triggersBounds: diff.any
16684 }, {
16685 name: 'target-text-rotation',
16686 type: t.textRotation,
16687 triggersBounds: diff.any
16688 }, {
16689 name: 'target-text-margin-x',
16690 type: t.bidirectionalSize,
16691 triggersBounds: diff.any
16692 }, {
16693 name: 'target-text-margin-y',
16694 type: t.bidirectionalSize,
16695 triggersBounds: diff.any
16696 }, {
16697 name: 'target-text-offset',
16698 type: t.size,
16699 triggersBounds: diff.any
16700 }];
16701 var labelDimensions = [{
16702 name: 'font-family',
16703 type: t.fontFamily,
16704 triggersBounds: diff.any
16705 }, {
16706 name: 'font-style',
16707 type: t.fontStyle,
16708 triggersBounds: diff.any
16709 }, {
16710 name: 'font-weight',
16711 type: t.fontWeight,
16712 triggersBounds: diff.any
16713 }, {
16714 name: 'font-size',
16715 type: t.size,
16716 triggersBounds: diff.any
16717 }, {
16718 name: 'text-transform',
16719 type: t.textTransform,
16720 triggersBounds: diff.any
16721 }, {
16722 name: 'text-wrap',
16723 type: t.textWrap,
16724 triggersBounds: diff.any
16725 }, {
16726 name: 'text-overflow-wrap',
16727 type: t.textOverflowWrap,
16728 triggersBounds: diff.any
16729 }, {
16730 name: 'text-max-width',
16731 type: t.size,
16732 triggersBounds: diff.any
16733 }, {
16734 name: 'text-outline-width',
16735 type: t.size,
16736 triggersBounds: diff.any
16737 }, {
16738 name: 'line-height',
16739 type: t.positiveNumber,
16740 triggersBounds: diff.any
16741 }];
16742 var commonLabel = [{
16743 name: 'text-valign',
16744 type: t.valign,
16745 triggersBounds: diff.any
16746 }, {
16747 name: 'text-halign',
16748 type: t.halign,
16749 triggersBounds: diff.any
16750 }, {
16751 name: 'color',
16752 type: t.color
16753 }, {
16754 name: 'text-outline-color',
16755 type: t.color
16756 }, {
16757 name: 'text-outline-opacity',
16758 type: t.zeroOneNumber
16759 }, {
16760 name: 'text-background-color',
16761 type: t.color
16762 }, {
16763 name: 'text-background-opacity',
16764 type: t.zeroOneNumber
16765 }, {
16766 name: 'text-background-padding',
16767 type: t.size,
16768 triggersBounds: diff.any
16769 }, {
16770 name: 'text-border-opacity',
16771 type: t.zeroOneNumber
16772 }, {
16773 name: 'text-border-color',
16774 type: t.color
16775 }, {
16776 name: 'text-border-width',
16777 type: t.size,
16778 triggersBounds: diff.any
16779 }, {
16780 name: 'text-border-style',
16781 type: t.borderStyle,
16782 triggersBounds: diff.any
16783 }, {
16784 name: 'text-background-shape',
16785 type: t.textBackgroundShape,
16786 triggersBounds: diff.any
16787 }, {
16788 name: 'text-justification',
16789 type: t.justification
16790 }];
16791 var behavior = [{
16792 name: 'events',
16793 type: t.bool,
16794 triggersZOrder: diff.any
16795 }, {
16796 name: 'text-events',
16797 type: t.bool,
16798 triggersZOrder: diff.any
16799 }];
16800 var visibility = [{
16801 name: 'display',
16802 type: t.display,
16803 triggersZOrder: diff.any,
16804 triggersBounds: diff.any,
16805 triggersBoundsOfConnectedEdges: true
16806 }, {
16807 name: 'visibility',
16808 type: t.visibility,
16809 triggersZOrder: diff.any
16810 }, {
16811 name: 'opacity',
16812 type: t.zeroOneNumber,
16813 triggersZOrder: diff.zeroNonZero
16814 }, {
16815 name: 'text-opacity',
16816 type: t.zeroOneNumber
16817 }, {
16818 name: 'min-zoomed-font-size',
16819 type: t.size
16820 }, {
16821 name: 'z-compound-depth',
16822 type: t.zCompoundDepth,
16823 triggersZOrder: diff.any
16824 }, {
16825 name: 'z-index-compare',
16826 type: t.zIndexCompare,
16827 triggersZOrder: diff.any
16828 }, {
16829 name: 'z-index',
16830 type: t.number,
16831 triggersZOrder: diff.any
16832 }];
16833 var overlay = [{
16834 name: 'overlay-padding',
16835 type: t.size,
16836 triggersBounds: diff.any
16837 }, {
16838 name: 'overlay-color',
16839 type: t.color
16840 }, {
16841 name: 'overlay-opacity',
16842 type: t.zeroOneNumber,
16843 triggersBounds: diff.zeroNonZero
16844 }, {
16845 name: 'overlay-shape',
16846 type: t.overlayShape,
16847 triggersBounds: diff.any
16848 }, {
16849 name: 'overlay-corner-radius',
16850 type: t.cornerRadius
16851 }];
16852 var underlay = [{
16853 name: 'underlay-padding',
16854 type: t.size,
16855 triggersBounds: diff.any
16856 }, {
16857 name: 'underlay-color',
16858 type: t.color
16859 }, {
16860 name: 'underlay-opacity',
16861 type: t.zeroOneNumber,
16862 triggersBounds: diff.zeroNonZero
16863 }, {
16864 name: 'underlay-shape',
16865 type: t.overlayShape,
16866 triggersBounds: diff.any
16867 }, {
16868 name: 'underlay-corner-radius',
16869 type: t.cornerRadius
16870 }];
16871 var transition = [{
16872 name: 'transition-property',
16873 type: t.propList
16874 }, {
16875 name: 'transition-duration',
16876 type: t.time
16877 }, {
16878 name: 'transition-delay',
16879 type: t.time
16880 }, {
16881 name: 'transition-timing-function',
16882 type: t.easing
16883 }];
16884 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16885 if (parsedProp.value === 'label') {
16886 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16887 } else {
16888 return parsedProp.pfValue;
16889 }
16890 };
16891 var nodeBody = [{
16892 name: 'height',
16893 type: t.nodeSize,
16894 triggersBounds: diff.any,
16895 hashOverride: nodeSizeHashOverride
16896 }, {
16897 name: 'width',
16898 type: t.nodeSize,
16899 triggersBounds: diff.any,
16900 hashOverride: nodeSizeHashOverride
16901 }, {
16902 name: 'shape',
16903 type: t.nodeShape,
16904 triggersBounds: diff.any
16905 }, {
16906 name: 'shape-polygon-points',
16907 type: t.polygonPointList,
16908 triggersBounds: diff.any
16909 }, {
16910 name: 'corner-radius',
16911 type: t.cornerRadius
16912 }, {
16913 name: 'background-color',
16914 type: t.color
16915 }, {
16916 name: 'background-fill',
16917 type: t.fill
16918 }, {
16919 name: 'background-opacity',
16920 type: t.zeroOneNumber
16921 }, {
16922 name: 'background-blacken',
16923 type: t.nOneOneNumber
16924 }, {
16925 name: 'background-gradient-stop-colors',
16926 type: t.colors
16927 }, {
16928 name: 'background-gradient-stop-positions',
16929 type: t.percentages
16930 }, {
16931 name: 'background-gradient-direction',
16932 type: t.gradientDirection
16933 }, {
16934 name: 'padding',
16935 type: t.sizeMaybePercent,
16936 triggersBounds: diff.any
16937 }, {
16938 name: 'padding-relative-to',
16939 type: t.paddingRelativeTo,
16940 triggersBounds: diff.any
16941 }, {
16942 name: 'bounds-expansion',
16943 type: t.boundsExpansion,
16944 triggersBounds: diff.any
16945 }];
16946 var nodeBorder = [{
16947 name: 'border-color',
16948 type: t.color
16949 }, {
16950 name: 'border-opacity',
16951 type: t.zeroOneNumber
16952 }, {
16953 name: 'border-width',
16954 type: t.size,
16955 triggersBounds: diff.any
16956 }, {
16957 name: 'border-style',
16958 type: t.borderStyle
16959 }, {
16960 name: 'border-cap',
16961 type: t.lineCap
16962 }, {
16963 name: 'border-join',
16964 type: t.lineJoin
16965 }, {
16966 name: 'border-dash-pattern',
16967 type: t.numbers
16968 }, {
16969 name: 'border-dash-offset',
16970 type: t.number
16971 }, {
16972 name: 'border-position',
16973 type: t.linePosition
16974 }];
16975 var nodeOutline = [{
16976 name: 'outline-color',
16977 type: t.color
16978 }, {
16979 name: 'outline-opacity',
16980 type: t.zeroOneNumber
16981 }, {
16982 name: 'outline-width',
16983 type: t.size,
16984 triggersBounds: diff.any
16985 }, {
16986 name: 'outline-style',
16987 type: t.borderStyle
16988 }, {
16989 name: 'outline-offset',
16990 type: t.size,
16991 triggersBounds: diff.any
16992 }];
16993 var backgroundImage = [{
16994 name: 'background-image',
16995 type: t.urls
16996 }, {
16997 name: 'background-image-crossorigin',
16998 type: t.bgCrossOrigin
16999 }, {
17000 name: 'background-image-opacity',
17001 type: t.zeroOneNumbers
17002 }, {
17003 name: 'background-image-containment',
17004 type: t.bgContainment
17005 }, {
17006 name: 'background-image-smoothing',
17007 type: t.bools
17008 }, {
17009 name: 'background-position-x',
17010 type: t.bgPos
17011 }, {
17012 name: 'background-position-y',
17013 type: t.bgPos
17014 }, {
17015 name: 'background-width-relative-to',
17016 type: t.bgRelativeTo
17017 }, {
17018 name: 'background-height-relative-to',
17019 type: t.bgRelativeTo
17020 }, {
17021 name: 'background-repeat',
17022 type: t.bgRepeat
17023 }, {
17024 name: 'background-fit',
17025 type: t.bgFit
17026 }, {
17027 name: 'background-clip',
17028 type: t.bgClip
17029 }, {
17030 name: 'background-width',
17031 type: t.bgWH
17032 }, {
17033 name: 'background-height',
17034 type: t.bgWH
17035 }, {
17036 name: 'background-offset-x',
17037 type: t.bgPos
17038 }, {
17039 name: 'background-offset-y',
17040 type: t.bgPos
17041 }];
17042 var compound = [{
17043 name: 'position',
17044 type: t.position,
17045 triggersBounds: diff.any
17046 }, {
17047 name: 'compound-sizing-wrt-labels',
17048 type: t.compoundIncludeLabels,
17049 triggersBounds: diff.any
17050 }, {
17051 name: 'min-width',
17052 type: t.size,
17053 triggersBounds: diff.any
17054 }, {
17055 name: 'min-width-bias-left',
17056 type: t.sizeMaybePercent,
17057 triggersBounds: diff.any
17058 }, {
17059 name: 'min-width-bias-right',
17060 type: t.sizeMaybePercent,
17061 triggersBounds: diff.any
17062 }, {
17063 name: 'min-height',
17064 type: t.size,
17065 triggersBounds: diff.any
17066 }, {
17067 name: 'min-height-bias-top',
17068 type: t.sizeMaybePercent,
17069 triggersBounds: diff.any
17070 }, {
17071 name: 'min-height-bias-bottom',
17072 type: t.sizeMaybePercent,
17073 triggersBounds: diff.any
17074 }];
17075 var edgeLine = [{
17076 name: 'line-style',
17077 type: t.lineStyle
17078 }, {
17079 name: 'line-color',
17080 type: t.color
17081 }, {
17082 name: 'line-fill',
17083 type: t.fill
17084 }, {
17085 name: 'line-cap',
17086 type: t.lineCap
17087 }, {
17088 name: 'line-opacity',
17089 type: t.zeroOneNumber
17090 }, {
17091 name: 'line-dash-pattern',
17092 type: t.numbers
17093 }, {
17094 name: 'line-dash-offset',
17095 type: t.number
17096 }, {
17097 name: 'line-outline-width',
17098 type: t.size
17099 }, {
17100 name: 'line-outline-color',
17101 type: t.color
17102 }, {
17103 name: 'line-gradient-stop-colors',
17104 type: t.colors
17105 }, {
17106 name: 'line-gradient-stop-positions',
17107 type: t.percentages
17108 }, {
17109 name: 'curve-style',
17110 type: t.curveStyle,
17111 triggersBounds: diff.any,
17112 triggersBoundsOfParallelBeziers: true
17113 }, {
17114 name: 'haystack-radius',
17115 type: t.zeroOneNumber,
17116 triggersBounds: diff.any
17117 }, {
17118 name: 'source-endpoint',
17119 type: t.edgeEndpoint,
17120 triggersBounds: diff.any
17121 }, {
17122 name: 'target-endpoint',
17123 type: t.edgeEndpoint,
17124 triggersBounds: diff.any
17125 }, {
17126 name: 'control-point-step-size',
17127 type: t.size,
17128 triggersBounds: diff.any
17129 }, {
17130 name: 'control-point-distances',
17131 type: t.bidirectionalSizes,
17132 triggersBounds: diff.any
17133 }, {
17134 name: 'control-point-weights',
17135 type: t.numbers,
17136 triggersBounds: diff.any
17137 }, {
17138 name: 'segment-distances',
17139 type: t.bidirectionalSizes,
17140 triggersBounds: diff.any
17141 }, {
17142 name: 'segment-weights',
17143 type: t.numbers,
17144 triggersBounds: diff.any
17145 }, {
17146 name: 'segment-radii',
17147 type: t.numbers,
17148 triggersBounds: diff.any
17149 }, {
17150 name: 'radius-type',
17151 type: t.radiusType,
17152 triggersBounds: diff.any
17153 }, {
17154 name: 'taxi-turn',
17155 type: t.bidirectionalSizeMaybePercent,
17156 triggersBounds: diff.any
17157 }, {
17158 name: 'taxi-turn-min-distance',
17159 type: t.size,
17160 triggersBounds: diff.any
17161 }, {
17162 name: 'taxi-direction',
17163 type: t.axisDirection,
17164 triggersBounds: diff.any
17165 }, {
17166 name: 'taxi-radius',
17167 type: t.number,
17168 triggersBounds: diff.any
17169 }, {
17170 name: 'edge-distances',
17171 type: t.edgeDistances,
17172 triggersBounds: diff.any
17173 }, {
17174 name: 'arrow-scale',
17175 type: t.positiveNumber,
17176 triggersBounds: diff.any
17177 }, {
17178 name: 'loop-direction',
17179 type: t.angle,
17180 triggersBounds: diff.any
17181 }, {
17182 name: 'loop-sweep',
17183 type: t.angle,
17184 triggersBounds: diff.any
17185 }, {
17186 name: 'source-distance-from-node',
17187 type: t.size,
17188 triggersBounds: diff.any
17189 }, {
17190 name: 'target-distance-from-node',
17191 type: t.size,
17192 triggersBounds: diff.any
17193 }];
17194 var ghost = [{
17195 name: 'ghost',
17196 type: t.bool,
17197 triggersBounds: diff.any
17198 }, {
17199 name: 'ghost-offset-x',
17200 type: t.bidirectionalSize,
17201 triggersBounds: diff.any
17202 }, {
17203 name: 'ghost-offset-y',
17204 type: t.bidirectionalSize,
17205 triggersBounds: diff.any
17206 }, {
17207 name: 'ghost-opacity',
17208 type: t.zeroOneNumber
17209 }];
17210 var core = [{
17211 name: 'selection-box-color',
17212 type: t.color
17213 }, {
17214 name: 'selection-box-opacity',
17215 type: t.zeroOneNumber
17216 }, {
17217 name: 'selection-box-border-color',
17218 type: t.color
17219 }, {
17220 name: 'selection-box-border-width',
17221 type: t.size
17222 }, {
17223 name: 'active-bg-color',
17224 type: t.color
17225 }, {
17226 name: 'active-bg-opacity',
17227 type: t.zeroOneNumber
17228 }, {
17229 name: 'active-bg-size',
17230 type: t.size
17231 }, {
17232 name: 'outside-texture-bg-color',
17233 type: t.color
17234 }, {
17235 name: 'outside-texture-bg-opacity',
17236 type: t.zeroOneNumber
17237 }];
17238
17239 // pie backgrounds for nodes
17240 var pie = [];
17241 styfn$2.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
17242 pie.push({
17243 name: 'pie-size',
17244 type: t.sizeMaybePercent
17245 });
17246 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17247 pie.push({
17248 name: 'pie-' + i + '-background-color',
17249 type: t.color
17250 });
17251 pie.push({
17252 name: 'pie-' + i + '-background-size',
17253 type: t.percent
17254 });
17255 pie.push({
17256 name: 'pie-' + i + '-background-opacity',
17257 type: t.zeroOneNumber
17258 });
17259 }
17260
17261 // edge arrows
17262 var edgeArrow = [];
17263 var arrowPrefixes = styfn$2.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
17264 [{
17265 name: 'arrow-shape',
17266 type: t.arrowShape,
17267 triggersBounds: diff.any
17268 }, {
17269 name: 'arrow-color',
17270 type: t.color
17271 }, {
17272 name: 'arrow-fill',
17273 type: t.arrowFill
17274 }, {
17275 name: 'arrow-width',
17276 type: t.arrowWidth
17277 }].forEach(function (prop) {
17278 arrowPrefixes.forEach(function (prefix) {
17279 var name = prefix + '-' + prop.name;
17280 var type = prop.type,
17281 triggersBounds = prop.triggersBounds;
17282 edgeArrow.push({
17283 name: name,
17284 type: type,
17285 triggersBounds: triggersBounds
17286 });
17287 });
17288 }, {});
17289 var props = styfn$2.properties = [].concat(behavior, transition, visibility, overlay, underlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, nodeOutline, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
17290 var propGroups = styfn$2.propertyGroups = {
17291 // common to all eles
17292 behavior: behavior,
17293 transition: transition,
17294 visibility: visibility,
17295 overlay: overlay,
17296 underlay: underlay,
17297 ghost: ghost,
17298 // labels
17299 commonLabel: commonLabel,
17300 labelDimensions: labelDimensions,
17301 mainLabel: mainLabel,
17302 sourceLabel: sourceLabel,
17303 targetLabel: targetLabel,
17304 // node props
17305 nodeBody: nodeBody,
17306 nodeBorder: nodeBorder,
17307 nodeOutline: nodeOutline,
17308 backgroundImage: backgroundImage,
17309 pie: pie,
17310 compound: compound,
17311 // edge props
17312 edgeLine: edgeLine,
17313 edgeArrow: edgeArrow,
17314 core: core
17315 };
17316 var propGroupNames = styfn$2.propertyGroupNames = {};
17317 var propGroupKeys = styfn$2.propertyGroupKeys = Object.keys(propGroups);
17318 propGroupKeys.forEach(function (key) {
17319 propGroupNames[key] = propGroups[key].map(function (prop) {
17320 return prop.name;
17321 });
17322 propGroups[key].forEach(function (prop) {
17323 return prop.groupKey = key;
17324 });
17325 });
17326
17327 // define aliases
17328 var aliases = styfn$2.aliases = [{
17329 name: 'content',
17330 pointsTo: 'label'
17331 }, {
17332 name: 'control-point-distance',
17333 pointsTo: 'control-point-distances'
17334 }, {
17335 name: 'control-point-weight',
17336 pointsTo: 'control-point-weights'
17337 }, {
17338 name: 'segment-distance',
17339 pointsTo: 'segment-distances'
17340 }, {
17341 name: 'segment-weight',
17342 pointsTo: 'segment-weights'
17343 }, {
17344 name: 'segment-radius',
17345 pointsTo: 'segment-radii'
17346 }, {
17347 name: 'edge-text-rotation',
17348 pointsTo: 'text-rotation'
17349 }, {
17350 name: 'padding-left',
17351 pointsTo: 'padding'
17352 }, {
17353 name: 'padding-right',
17354 pointsTo: 'padding'
17355 }, {
17356 name: 'padding-top',
17357 pointsTo: 'padding'
17358 }, {
17359 name: 'padding-bottom',
17360 pointsTo: 'padding'
17361 }];
17362
17363 // list of property names
17364 styfn$2.propertyNames = props.map(function (p) {
17365 return p.name;
17366 });
17367
17368 // allow access of properties by name ( e.g. style.properties.height )
17369 for (var _i = 0; _i < props.length; _i++) {
17370 var prop = props[_i];
17371 props[prop.name] = prop; // allow lookup by name
17372 }
17373
17374 // map aliases
17375 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
17376 var alias = aliases[_i2];
17377 var pointsToProp = props[alias.pointsTo];
17378 var aliasProp = {
17379 name: alias.name,
17380 alias: true,
17381 pointsTo: pointsToProp
17382 };
17383
17384 // add alias prop for parsing
17385 props.push(aliasProp);
17386 props[alias.name] = aliasProp; // allow lookup by name
17387 }
17388 })();
17389
17390 styfn$2.getDefaultProperty = function (name) {
17391 return this.getDefaultProperties()[name];
17392 };
17393 styfn$2.getDefaultProperties = function () {
17394 var _p = this._private;
17395 if (_p.defaultProperties != null) {
17396 return _p.defaultProperties;
17397 }
17398 var rawProps = extend({
17399 // core props
17400 'selection-box-color': '#ddd',
17401 'selection-box-opacity': 0.65,
17402 'selection-box-border-color': '#aaa',
17403 'selection-box-border-width': 1,
17404 'active-bg-color': 'black',
17405 'active-bg-opacity': 0.15,
17406 'active-bg-size': 30,
17407 'outside-texture-bg-color': '#000',
17408 'outside-texture-bg-opacity': 0.125,
17409 // common node/edge props
17410 'events': 'yes',
17411 'text-events': 'no',
17412 'text-valign': 'top',
17413 'text-halign': 'center',
17414 'text-justification': 'auto',
17415 'line-height': 1,
17416 'color': '#000',
17417 'text-outline-color': '#000',
17418 'text-outline-width': 0,
17419 'text-outline-opacity': 1,
17420 'text-opacity': 1,
17421 'text-decoration': 'none',
17422 'text-transform': 'none',
17423 'text-wrap': 'none',
17424 'text-overflow-wrap': 'whitespace',
17425 'text-max-width': 9999,
17426 'text-background-color': '#000',
17427 'text-background-opacity': 0,
17428 'text-background-shape': 'rectangle',
17429 'text-background-padding': 0,
17430 'text-border-opacity': 0,
17431 'text-border-width': 0,
17432 'text-border-style': 'solid',
17433 'text-border-color': '#000',
17434 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
17435 'font-style': 'normal',
17436 'font-weight': 'normal',
17437 'font-size': 16,
17438 'min-zoomed-font-size': 0,
17439 'text-rotation': 'none',
17440 'source-text-rotation': 'none',
17441 'target-text-rotation': 'none',
17442 'visibility': 'visible',
17443 'display': 'element',
17444 'opacity': 1,
17445 'z-compound-depth': 'auto',
17446 'z-index-compare': 'auto',
17447 'z-index': 0,
17448 'label': '',
17449 'text-margin-x': 0,
17450 'text-margin-y': 0,
17451 'source-label': '',
17452 'source-text-offset': 0,
17453 'source-text-margin-x': 0,
17454 'source-text-margin-y': 0,
17455 'target-label': '',
17456 'target-text-offset': 0,
17457 'target-text-margin-x': 0,
17458 'target-text-margin-y': 0,
17459 'overlay-opacity': 0,
17460 'overlay-color': '#000',
17461 'overlay-padding': 10,
17462 'overlay-shape': 'round-rectangle',
17463 'overlay-corner-radius': 'auto',
17464 'underlay-opacity': 0,
17465 'underlay-color': '#000',
17466 'underlay-padding': 10,
17467 'underlay-shape': 'round-rectangle',
17468 'underlay-corner-radius': 'auto',
17469 'transition-property': 'none',
17470 'transition-duration': 0,
17471 'transition-delay': 0,
17472 'transition-timing-function': 'linear',
17473 // node props
17474 'background-blacken': 0,
17475 'background-color': '#999',
17476 'background-fill': 'solid',
17477 'background-opacity': 1,
17478 'background-image': 'none',
17479 'background-image-crossorigin': 'anonymous',
17480 'background-image-opacity': 1,
17481 'background-image-containment': 'inside',
17482 'background-image-smoothing': 'yes',
17483 'background-position-x': '50%',
17484 'background-position-y': '50%',
17485 'background-offset-x': 0,
17486 'background-offset-y': 0,
17487 'background-width-relative-to': 'include-padding',
17488 'background-height-relative-to': 'include-padding',
17489 'background-repeat': 'no-repeat',
17490 'background-fit': 'none',
17491 'background-clip': 'node',
17492 'background-width': 'auto',
17493 'background-height': 'auto',
17494 'border-color': '#000',
17495 'border-opacity': 1,
17496 'border-width': 0,
17497 'border-style': 'solid',
17498 'border-dash-pattern': [4, 2],
17499 'border-dash-offset': 0,
17500 'border-cap': 'butt',
17501 'border-join': 'miter',
17502 'border-position': 'center',
17503 'outline-color': '#999',
17504 'outline-opacity': 1,
17505 'outline-width': 0,
17506 'outline-offset': 0,
17507 'outline-style': 'solid',
17508 'height': 30,
17509 'width': 30,
17510 'shape': 'ellipse',
17511 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17512 'corner-radius': 'auto',
17513 'bounds-expansion': 0,
17514 // node gradient
17515 'background-gradient-direction': 'to-bottom',
17516 'background-gradient-stop-colors': '#999',
17517 'background-gradient-stop-positions': '0%',
17518 // ghost props
17519 'ghost': 'no',
17520 'ghost-offset-y': 0,
17521 'ghost-offset-x': 0,
17522 'ghost-opacity': 0,
17523 // compound props
17524 'padding': 0,
17525 'padding-relative-to': 'width',
17526 'position': 'origin',
17527 'compound-sizing-wrt-labels': 'include',
17528 'min-width': 0,
17529 'min-width-bias-left': 0,
17530 'min-width-bias-right': 0,
17531 'min-height': 0,
17532 'min-height-bias-top': 0,
17533 'min-height-bias-bottom': 0
17534 }, {
17535 // node pie bg
17536 'pie-size': '100%'
17537 }, [{
17538 name: 'pie-{{i}}-background-color',
17539 value: 'black'
17540 }, {
17541 name: 'pie-{{i}}-background-size',
17542 value: '0%'
17543 }, {
17544 name: 'pie-{{i}}-background-opacity',
17545 value: 1
17546 }].reduce(function (css, prop) {
17547 for (var i = 1; i <= styfn$2.pieBackgroundN; i++) {
17548 var name = prop.name.replace('{{i}}', i);
17549 var val = prop.value;
17550 css[name] = val;
17551 }
17552 return css;
17553 }, {}), {
17554 // edge props
17555 'line-style': 'solid',
17556 'line-color': '#999',
17557 'line-fill': 'solid',
17558 'line-cap': 'butt',
17559 'line-opacity': 1,
17560 'line-outline-width': 0,
17561 'line-outline-color': '#000',
17562 'line-gradient-stop-colors': '#999',
17563 'line-gradient-stop-positions': '0%',
17564 'control-point-step-size': 40,
17565 'control-point-weights': 0.5,
17566 'segment-weights': 0.5,
17567 'segment-distances': 20,
17568 'segment-radii': 15,
17569 'radius-type': 'arc-radius',
17570 'taxi-turn': '50%',
17571 'taxi-radius': 15,
17572 'taxi-turn-min-distance': 10,
17573 'taxi-direction': 'auto',
17574 'edge-distances': 'intersection',
17575 'curve-style': 'haystack',
17576 'haystack-radius': 0,
17577 'arrow-scale': 1,
17578 'loop-direction': '-45deg',
17579 'loop-sweep': '-90deg',
17580 'source-distance-from-node': 0,
17581 'target-distance-from-node': 0,
17582 'source-endpoint': 'outside-to-node',
17583 'target-endpoint': 'outside-to-node',
17584 'line-dash-pattern': [6, 3],
17585 'line-dash-offset': 0
17586 }, [{
17587 name: 'arrow-shape',
17588 value: 'none'
17589 }, {
17590 name: 'arrow-color',
17591 value: '#999'
17592 }, {
17593 name: 'arrow-fill',
17594 value: 'filled'
17595 }, {
17596 name: 'arrow-width',
17597 value: 1
17598 }].reduce(function (css, prop) {
17599 styfn$2.arrowPrefixes.forEach(function (prefix) {
17600 var name = prefix + '-' + prop.name;
17601 var val = prop.value;
17602 css[name] = val;
17603 });
17604 return css;
17605 }, {}));
17606 var parsedProps = {};
17607 for (var i = 0; i < this.properties.length; i++) {
17608 var prop = this.properties[i];
17609 if (prop.pointsTo) {
17610 continue;
17611 }
17612 var name = prop.name;
17613 var val = rawProps[name];
17614 var parsedProp = this.parse(name, val);
17615 parsedProps[name] = parsedProp;
17616 }
17617 _p.defaultProperties = parsedProps;
17618 return _p.defaultProperties;
17619 };
17620 styfn$2.addDefaultStylesheet = function () {
17621 this.selector(':parent').css({
17622 'shape': 'rectangle',
17623 'padding': 10,
17624 'background-color': '#eee',
17625 'border-color': '#ccc',
17626 'border-width': 1
17627 }).selector('edge').css({
17628 'width': 3
17629 }).selector(':loop').css({
17630 'curve-style': 'bezier'
17631 }).selector('edge:compound').css({
17632 'curve-style': 'bezier',
17633 'source-endpoint': 'outside-to-line',
17634 'target-endpoint': 'outside-to-line'
17635 }).selector(':selected').css({
17636 'background-color': '#0169D9',
17637 'line-color': '#0169D9',
17638 'source-arrow-color': '#0169D9',
17639 'target-arrow-color': '#0169D9',
17640 'mid-source-arrow-color': '#0169D9',
17641 'mid-target-arrow-color': '#0169D9'
17642 }).selector(':parent:selected').css({
17643 'background-color': '#CCE1F9',
17644 'border-color': '#aec8e5'
17645 }).selector(':active').css({
17646 'overlay-color': 'black',
17647 'overlay-padding': 10,
17648 'overlay-opacity': 0.25
17649 });
17650 this.defaultLength = this.length;
17651 };
17652
17653 var styfn$1 = {};
17654
17655 // a caching layer for property parsing
17656 styfn$1.parse = function (name, value, propIsBypass, propIsFlat) {
17657 var self = this;
17658
17659 // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17660 if (fn$6(value)) {
17661 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17662 }
17663 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17664 var bypassKey = propIsBypass ? 't' : 'f';
17665 var valueKey = '' + value;
17666 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17667 var propCache = self.propCache = self.propCache || [];
17668 var ret;
17669 if (!(ret = propCache[argHash])) {
17670 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17671 }
17672
17673 // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17674 // - mappings can't be shared b/c mappings are per-element
17675 if (propIsBypass || propIsFlat === 'mapping') {
17676 // need a copy since props are mutated later in their lifecycles
17677 ret = copy(ret);
17678 if (ret) {
17679 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17680 }
17681 }
17682
17683 return ret;
17684 };
17685 styfn$1.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17686 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17687 if (!prop && value != null) {
17688 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17689 }
17690 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17691 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17692 }
17693 return prop;
17694 };
17695
17696 // parse a property; return null on invalid; return parsed property otherwise
17697 // fields :
17698 // - name : the name of the property
17699 // - value : the parsed, native-typed value of the property
17700 // - strValue : a string value that represents the property value in valid css
17701 // - bypass : true iff the property is a bypass property
17702 styfn$1.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17703 var self = this;
17704 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17705
17706 var property = self.properties[name];
17707 var passedValue = value;
17708 var types = self.types;
17709 if (!property) {
17710 return null;
17711 } // return null on property of unknown name
17712 if (value === undefined) {
17713 return null;
17714 } // can't assign undefined
17715
17716 // the property may be an alias
17717 if (property.alias) {
17718 property = property.pointsTo;
17719 name = property.name;
17720 }
17721 var valueIsString = string(value);
17722 if (valueIsString) {
17723 // trim the value to make parsing easier
17724 value = value.trim();
17725 }
17726 var type = property.type;
17727 if (!type) {
17728 return null;
17729 } // no type, no luck
17730
17731 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17732 if (propIsBypass && (value === '' || value === null)) {
17733 return {
17734 name: name,
17735 value: value,
17736 bypass: true,
17737 deleteBypass: true
17738 };
17739 }
17740
17741 // check if value is a function used as a mapper
17742 if (fn$6(value)) {
17743 return {
17744 name: name,
17745 value: value,
17746 strValue: 'fn',
17747 mapped: types.fn,
17748 bypass: propIsBypass
17749 };
17750 }
17751
17752 // check if value is mapped
17753 var data, mapData;
17754 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))) {
17755 if (propIsBypass) {
17756 return false;
17757 } // mappers not allowed in bypass
17758
17759 var mapped = types.data;
17760 return {
17761 name: name,
17762 value: data,
17763 strValue: '' + value,
17764 mapped: mapped,
17765 field: data[1],
17766 bypass: propIsBypass
17767 };
17768 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17769 if (propIsBypass) {
17770 return false;
17771 } // mappers not allowed in bypass
17772 if (type.multiple) {
17773 return false;
17774 } // impossible to map to num
17775
17776 var _mapped = types.mapData;
17777
17778 // we can map only if the type is a colour or a number
17779 if (!(type.color || type.number)) {
17780 return false;
17781 }
17782 var valueMin = this.parse(name, mapData[4]); // parse to validate
17783 if (!valueMin || valueMin.mapped) {
17784 return false;
17785 } // can't be invalid or mapped
17786
17787 var valueMax = this.parse(name, mapData[5]); // parse to validate
17788 if (!valueMax || valueMax.mapped) {
17789 return false;
17790 } // can't be invalid or mapped
17791
17792 // check if valueMin and valueMax are the same
17793 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17794 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17795 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17796 } else if (type.color) {
17797 var c1 = valueMin.value;
17798 var c2 = valueMax.value;
17799 var same = c1[0] === c2[0] // red
17800 && c1[1] === c2[1] // green
17801 && c1[2] === c2[2] // blue
17802 && (
17803 // optional alpha
17804 c1[3] === c2[3] // same alpha outright
17805 || (c1[3] == null || c1[3] === 1 // full opacity for colour 1?
17806 ) && (c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17807 );
17808
17809 if (same) {
17810 return false;
17811 } // can't make a mapper without a range
17812 }
17813
17814 return {
17815 name: name,
17816 value: mapData,
17817 strValue: '' + value,
17818 mapped: _mapped,
17819 field: mapData[1],
17820 fieldMin: parseFloat(mapData[2]),
17821 // min & max are numeric
17822 fieldMax: parseFloat(mapData[3]),
17823 valueMin: valueMin.value,
17824 valueMax: valueMax.value,
17825 bypass: propIsBypass
17826 };
17827 }
17828 if (type.multiple && propIsFlat !== 'multiple') {
17829 var vals;
17830 if (valueIsString) {
17831 vals = value.split(/\s+/);
17832 } else if (array(value)) {
17833 vals = value;
17834 } else {
17835 vals = [value];
17836 }
17837 if (type.evenMultiple && vals.length % 2 !== 0) {
17838 return null;
17839 }
17840 var valArr = [];
17841 var unitsArr = [];
17842 var pfValArr = [];
17843 var strVal = '';
17844 var hasEnum = false;
17845 for (var i = 0; i < vals.length; i++) {
17846 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17847 hasEnum = hasEnum || string(p.value);
17848 valArr.push(p.value);
17849 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17850 unitsArr.push(p.units);
17851 strVal += (i > 0 ? ' ' : '') + p.strValue;
17852 }
17853 if (type.validate && !type.validate(valArr, unitsArr)) {
17854 return null;
17855 }
17856 if (type.singleEnum && hasEnum) {
17857 if (valArr.length === 1 && string(valArr[0])) {
17858 return {
17859 name: name,
17860 value: valArr[0],
17861 strValue: valArr[0],
17862 bypass: propIsBypass
17863 };
17864 } else {
17865 return null;
17866 }
17867 }
17868 return {
17869 name: name,
17870 value: valArr,
17871 pfValue: pfValArr,
17872 strValue: strVal,
17873 bypass: propIsBypass,
17874 units: unitsArr
17875 };
17876 }
17877
17878 // several types also allow enums
17879 var checkEnums = function checkEnums() {
17880 for (var _i = 0; _i < type.enums.length; _i++) {
17881 var en = type.enums[_i];
17882 if (en === value) {
17883 return {
17884 name: name,
17885 value: value,
17886 strValue: '' + value,
17887 bypass: propIsBypass
17888 };
17889 }
17890 }
17891 return null;
17892 };
17893
17894 // check the type and return the appropriate object
17895 if (type.number) {
17896 var units;
17897 var implicitUnits = 'px'; // not set => px
17898
17899 if (type.units) {
17900 // use specified units if set
17901 units = type.units;
17902 }
17903 if (type.implicitUnits) {
17904 implicitUnits = type.implicitUnits;
17905 }
17906 if (!type.unitless) {
17907 if (valueIsString) {
17908 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17909 if (units) {
17910 unitsRegex = units;
17911 } // only allow explicit units if so set
17912 var match = value.match('^(' + number + ')(' + unitsRegex + ')?' + '$');
17913 if (match) {
17914 value = match[1];
17915 units = match[2] || implicitUnits;
17916 }
17917 } else if (!units || type.implicitUnits) {
17918 units = implicitUnits; // implicitly px if unspecified
17919 }
17920 }
17921
17922 value = parseFloat(value);
17923
17924 // if not a number and enums not allowed, then the value is invalid
17925 if (isNaN(value) && type.enums === undefined) {
17926 return null;
17927 }
17928
17929 // check if this number type also accepts special keywords in place of numbers
17930 // (i.e. `left`, `auto`, etc)
17931 if (isNaN(value) && type.enums !== undefined) {
17932 value = passedValue;
17933 return checkEnums();
17934 }
17935
17936 // check if value must be an integer
17937 if (type.integer && !integer(value)) {
17938 return null;
17939 }
17940
17941 // check value is within range
17942 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17943 return null;
17944 }
17945 var ret = {
17946 name: name,
17947 value: value,
17948 strValue: '' + value + (units ? units : ''),
17949 units: units,
17950 bypass: propIsBypass
17951 };
17952
17953 // normalise value in pixels
17954 if (type.unitless || units !== 'px' && units !== 'em') {
17955 ret.pfValue = value;
17956 } else {
17957 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17958 }
17959
17960 // normalise value in ms
17961 if (units === 'ms' || units === 's') {
17962 ret.pfValue = units === 'ms' ? value : 1000 * value;
17963 }
17964
17965 // normalise value in rad
17966 if (units === 'deg' || units === 'rad') {
17967 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17968 }
17969
17970 // normalize value in %
17971 if (units === '%') {
17972 ret.pfValue = value / 100;
17973 }
17974 return ret;
17975 } else if (type.propList) {
17976 var props = [];
17977 var propsStr = '' + value;
17978 if (propsStr === 'none') ; else {
17979 // go over each prop
17980
17981 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17982 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17983 var propName = propsSplit[_i2].trim();
17984 if (self.properties[propName]) {
17985 props.push(propName);
17986 } else {
17987 warn('`' + propName + '` is not a valid property name');
17988 }
17989 }
17990 if (props.length === 0) {
17991 return null;
17992 }
17993 }
17994 return {
17995 name: name,
17996 value: props,
17997 strValue: props.length === 0 ? 'none' : props.join(' '),
17998 bypass: propIsBypass
17999 };
18000 } else if (type.color) {
18001 var tuple = color2tuple(value);
18002 if (!tuple) {
18003 return null;
18004 }
18005 return {
18006 name: name,
18007 value: tuple,
18008 pfValue: tuple,
18009 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
18010 // n.b. no spaces b/c of multiple support
18011 bypass: propIsBypass
18012 };
18013 } else if (type.regex || type.regexes) {
18014 // first check enums
18015 if (type.enums) {
18016 var enumProp = checkEnums();
18017 if (enumProp) {
18018 return enumProp;
18019 }
18020 }
18021 var regexes = type.regexes ? type.regexes : [type.regex];
18022 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
18023 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
18024 var m = regex.exec(value);
18025 if (m) {
18026 // regex matches
18027 return {
18028 name: name,
18029 value: type.singleRegexMatchValue ? m[1] : m,
18030 strValue: '' + value,
18031 bypass: propIsBypass
18032 };
18033 }
18034 }
18035 return null; // didn't match any
18036 } else if (type.string) {
18037 // just return
18038 return {
18039 name: name,
18040 value: '' + value,
18041 strValue: '' + value,
18042 bypass: propIsBypass
18043 };
18044 } else if (type.enums) {
18045 // check enums last because it's a combo type in others
18046 return checkEnums();
18047 } else {
18048 return null; // not a type we can handle
18049 }
18050 };
18051
18052 var Style = function Style(cy) {
18053 if (!(this instanceof Style)) {
18054 return new Style(cy);
18055 }
18056 if (!core(cy)) {
18057 error('A style must have a core reference');
18058 return;
18059 }
18060 this._private = {
18061 cy: cy,
18062 coreStyle: {}
18063 };
18064 this.length = 0;
18065 this.resetToDefault();
18066 };
18067 var styfn = Style.prototype;
18068 styfn.instanceString = function () {
18069 return 'style';
18070 };
18071
18072 // remove all contexts
18073 styfn.clear = function () {
18074 var _p = this._private;
18075 var cy = _p.cy;
18076 var eles = cy.elements();
18077 for (var i = 0; i < this.length; i++) {
18078 this[i] = undefined;
18079 }
18080 this.length = 0;
18081 _p.contextStyles = {};
18082 _p.propDiffs = {};
18083 this.cleanElements(eles, true);
18084 eles.forEach(function (ele) {
18085 var ele_p = ele[0]._private;
18086 ele_p.styleDirty = true;
18087 ele_p.appliedInitStyle = false;
18088 });
18089 return this; // chaining
18090 };
18091
18092 styfn.resetToDefault = function () {
18093 this.clear();
18094 this.addDefaultStylesheet();
18095 return this;
18096 };
18097
18098 // builds a style object for the 'core' selector
18099 styfn.core = function (propName) {
18100 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
18101 };
18102
18103 // create a new context from the specified selector string and switch to that context
18104 styfn.selector = function (selectorStr) {
18105 // 'core' is a special case and does not need a selector
18106 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
18107 var i = this.length++; // new context means new index
18108 this[i] = {
18109 selector: selector,
18110 properties: [],
18111 mappedProperties: [],
18112 index: i
18113 };
18114 return this; // chaining
18115 };
18116
18117 // add one or many css rules to the current context
18118 styfn.css = function () {
18119 var self = this;
18120 var args = arguments;
18121 if (args.length === 1) {
18122 var map = args[0];
18123 for (var i = 0; i < self.properties.length; i++) {
18124 var prop = self.properties[i];
18125 var mapVal = map[prop.name];
18126 if (mapVal === undefined) {
18127 mapVal = map[dash2camel(prop.name)];
18128 }
18129 if (mapVal !== undefined) {
18130 this.cssRule(prop.name, mapVal);
18131 }
18132 }
18133 } else if (args.length === 2) {
18134 this.cssRule(args[0], args[1]);
18135 }
18136
18137 // do nothing if args are invalid
18138
18139 return this; // chaining
18140 };
18141
18142 styfn.style = styfn.css;
18143
18144 // add a single css rule to the current context
18145 styfn.cssRule = function (name, value) {
18146 // name-value pair
18147 var property = this.parse(name, value);
18148
18149 // add property to current context if valid
18150 if (property) {
18151 var i = this.length - 1;
18152 this[i].properties.push(property);
18153 this[i].properties[property.name] = property; // allow access by name as well
18154
18155 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
18156 this._private.hasPie = true;
18157 }
18158 if (property.mapped) {
18159 this[i].mappedProperties.push(property);
18160 }
18161
18162 // add to core style if necessary
18163 var currentSelectorIsCore = !this[i].selector;
18164 if (currentSelectorIsCore) {
18165 this._private.coreStyle[property.name] = property;
18166 }
18167 }
18168 return this; // chaining
18169 };
18170
18171 styfn.append = function (style) {
18172 if (stylesheet(style)) {
18173 style.appendToStyle(this);
18174 } else if (array(style)) {
18175 this.appendFromJson(style);
18176 } else if (string(style)) {
18177 this.appendFromString(style);
18178 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
18179
18180 return this;
18181 };
18182
18183 // static function
18184 Style.fromJson = function (cy, json) {
18185 var style = new Style(cy);
18186 style.fromJson(json);
18187 return style;
18188 };
18189 Style.fromString = function (cy, string) {
18190 return new Style(cy).fromString(string);
18191 };
18192 [styfn$8, styfn$7, styfn$6, styfn$5, styfn$4, styfn$3, styfn$2, styfn$1].forEach(function (props) {
18193 extend(styfn, props);
18194 });
18195 Style.types = styfn.types;
18196 Style.properties = styfn.properties;
18197 Style.propertyGroups = styfn.propertyGroups;
18198 Style.propertyGroupNames = styfn.propertyGroupNames;
18199 Style.propertyGroupKeys = styfn.propertyGroupKeys;
18200
18201 var corefn$2 = {
18202 style: function style(newStyle) {
18203 if (newStyle) {
18204 var s = this.setStyle(newStyle);
18205 s.update();
18206 }
18207 return this._private.style;
18208 },
18209 setStyle: function setStyle(style) {
18210 var _p = this._private;
18211 if (stylesheet(style)) {
18212 _p.style = style.generateStyle(this);
18213 } else if (array(style)) {
18214 _p.style = Style.fromJson(this, style);
18215 } else if (string(style)) {
18216 _p.style = Style.fromString(this, style);
18217 } else {
18218 _p.style = Style(this);
18219 }
18220 return _p.style;
18221 },
18222 // e.g. cy.data() changed => recalc ele mappers
18223 updateStyle: function updateStyle() {
18224 this.mutableElements().updateStyle(); // just send to all eles
18225 }
18226 };
18227
18228 var defaultSelectionType = 'single';
18229 var corefn$1 = {
18230 autolock: function autolock(bool) {
18231 if (bool !== undefined) {
18232 this._private.autolock = bool ? true : false;
18233 } else {
18234 return this._private.autolock;
18235 }
18236 return this; // chaining
18237 },
18238
18239 autoungrabify: function autoungrabify(bool) {
18240 if (bool !== undefined) {
18241 this._private.autoungrabify = bool ? true : false;
18242 } else {
18243 return this._private.autoungrabify;
18244 }
18245 return this; // chaining
18246 },
18247
18248 autounselectify: function autounselectify(bool) {
18249 if (bool !== undefined) {
18250 this._private.autounselectify = bool ? true : false;
18251 } else {
18252 return this._private.autounselectify;
18253 }
18254 return this; // chaining
18255 },
18256
18257 selectionType: function selectionType(selType) {
18258 var _p = this._private;
18259 if (_p.selectionType == null) {
18260 _p.selectionType = defaultSelectionType;
18261 }
18262 if (selType !== undefined) {
18263 if (selType === 'additive' || selType === 'single') {
18264 _p.selectionType = selType;
18265 }
18266 } else {
18267 return _p.selectionType;
18268 }
18269 return this;
18270 },
18271 panningEnabled: function panningEnabled(bool) {
18272 if (bool !== undefined) {
18273 this._private.panningEnabled = bool ? true : false;
18274 } else {
18275 return this._private.panningEnabled;
18276 }
18277 return this; // chaining
18278 },
18279
18280 userPanningEnabled: function userPanningEnabled(bool) {
18281 if (bool !== undefined) {
18282 this._private.userPanningEnabled = bool ? true : false;
18283 } else {
18284 return this._private.userPanningEnabled;
18285 }
18286 return this; // chaining
18287 },
18288
18289 zoomingEnabled: function zoomingEnabled(bool) {
18290 if (bool !== undefined) {
18291 this._private.zoomingEnabled = bool ? true : false;
18292 } else {
18293 return this._private.zoomingEnabled;
18294 }
18295 return this; // chaining
18296 },
18297
18298 userZoomingEnabled: function userZoomingEnabled(bool) {
18299 if (bool !== undefined) {
18300 this._private.userZoomingEnabled = bool ? true : false;
18301 } else {
18302 return this._private.userZoomingEnabled;
18303 }
18304 return this; // chaining
18305 },
18306
18307 boxSelectionEnabled: function boxSelectionEnabled(bool) {
18308 if (bool !== undefined) {
18309 this._private.boxSelectionEnabled = bool ? true : false;
18310 } else {
18311 return this._private.boxSelectionEnabled;
18312 }
18313 return this; // chaining
18314 },
18315
18316 pan: function pan() {
18317 var args = arguments;
18318 var pan = this._private.pan;
18319 var dim, val, dims, x, y;
18320 switch (args.length) {
18321 case 0:
18322 // .pan()
18323 return pan;
18324 case 1:
18325 if (string(args[0])) {
18326 // .pan('x')
18327 dim = args[0];
18328 return pan[dim];
18329 } else if (plainObject(args[0])) {
18330 // .pan({ x: 0, y: 100 })
18331 if (!this._private.panningEnabled) {
18332 return this;
18333 }
18334 dims = args[0];
18335 x = dims.x;
18336 y = dims.y;
18337 if (number$1(x)) {
18338 pan.x = x;
18339 }
18340 if (number$1(y)) {
18341 pan.y = y;
18342 }
18343 this.emit('pan viewport');
18344 }
18345 break;
18346 case 2:
18347 // .pan('x', 100)
18348 if (!this._private.panningEnabled) {
18349 return this;
18350 }
18351 dim = args[0];
18352 val = args[1];
18353 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18354 pan[dim] = val;
18355 }
18356 this.emit('pan viewport');
18357 break;
18358 // invalid
18359 }
18360
18361 this.notify('viewport');
18362 return this; // chaining
18363 },
18364
18365 panBy: function panBy(arg0, arg1) {
18366 var args = arguments;
18367 var pan = this._private.pan;
18368 var dim, val, dims, x, y;
18369 if (!this._private.panningEnabled) {
18370 return this;
18371 }
18372 switch (args.length) {
18373 case 1:
18374 if (plainObject(arg0)) {
18375 // .panBy({ x: 0, y: 100 })
18376 dims = args[0];
18377 x = dims.x;
18378 y = dims.y;
18379 if (number$1(x)) {
18380 pan.x += x;
18381 }
18382 if (number$1(y)) {
18383 pan.y += y;
18384 }
18385 this.emit('pan viewport');
18386 }
18387 break;
18388 case 2:
18389 // .panBy('x', 100)
18390 dim = arg0;
18391 val = arg1;
18392 if ((dim === 'x' || dim === 'y') && number$1(val)) {
18393 pan[dim] += val;
18394 }
18395 this.emit('pan viewport');
18396 break;
18397 // invalid
18398 }
18399
18400 this.notify('viewport');
18401 return this; // chaining
18402 },
18403
18404 fit: function fit(elements, padding) {
18405 var viewportState = this.getFitViewport(elements, padding);
18406 if (viewportState) {
18407 var _p = this._private;
18408 _p.zoom = viewportState.zoom;
18409 _p.pan = viewportState.pan;
18410 this.emit('pan zoom viewport');
18411 this.notify('viewport');
18412 }
18413 return this; // chaining
18414 },
18415
18416 getFitViewport: function getFitViewport(elements, padding) {
18417 if (number$1(elements) && padding === undefined) {
18418 // elements is optional
18419 padding = elements;
18420 elements = undefined;
18421 }
18422 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18423 return;
18424 }
18425 var bb;
18426 if (string(elements)) {
18427 var sel = elements;
18428 elements = this.$(sel);
18429 } else if (boundingBox(elements)) {
18430 // assume bb
18431 var bbe = elements;
18432 bb = {
18433 x1: bbe.x1,
18434 y1: bbe.y1,
18435 x2: bbe.x2,
18436 y2: bbe.y2
18437 };
18438 bb.w = bb.x2 - bb.x1;
18439 bb.h = bb.y2 - bb.y1;
18440 } else if (!elementOrCollection(elements)) {
18441 elements = this.mutableElements();
18442 }
18443 if (elementOrCollection(elements) && elements.empty()) {
18444 return;
18445 } // can't fit to nothing
18446
18447 bb = bb || elements.boundingBox();
18448 var w = this.width();
18449 var h = this.height();
18450 var zoom;
18451 padding = number$1(padding) ? padding : 0;
18452 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18453 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h);
18454
18455 // crop zoom
18456 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18457 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18458 var pan = {
18459 // now pan to middle
18460 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18461 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18462 };
18463 return {
18464 zoom: zoom,
18465 pan: pan
18466 };
18467 }
18468 return;
18469 },
18470 zoomRange: function zoomRange(min, max) {
18471 var _p = this._private;
18472 if (max == null) {
18473 var opts = min;
18474 min = opts.min;
18475 max = opts.max;
18476 }
18477 if (number$1(min) && number$1(max) && min <= max) {
18478 _p.minZoom = min;
18479 _p.maxZoom = max;
18480 } else if (number$1(min) && max === undefined && min <= _p.maxZoom) {
18481 _p.minZoom = min;
18482 } else if (number$1(max) && min === undefined && max >= _p.minZoom) {
18483 _p.maxZoom = max;
18484 }
18485 return this;
18486 },
18487 minZoom: function minZoom(zoom) {
18488 if (zoom === undefined) {
18489 return this._private.minZoom;
18490 } else {
18491 return this.zoomRange({
18492 min: zoom
18493 });
18494 }
18495 },
18496 maxZoom: function maxZoom(zoom) {
18497 if (zoom === undefined) {
18498 return this._private.maxZoom;
18499 } else {
18500 return this.zoomRange({
18501 max: zoom
18502 });
18503 }
18504 },
18505 getZoomedViewport: function getZoomedViewport(params) {
18506 var _p = this._private;
18507 var currentPan = _p.pan;
18508 var currentZoom = _p.zoom;
18509 var pos; // in rendered px
18510 var zoom;
18511 var bail = false;
18512 if (!_p.zoomingEnabled) {
18513 // zooming disabled
18514 bail = true;
18515 }
18516 if (number$1(params)) {
18517 // then set the zoom
18518 zoom = params;
18519 } else if (plainObject(params)) {
18520 // then zoom about a point
18521 zoom = params.level;
18522 if (params.position != null) {
18523 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18524 } else if (params.renderedPosition != null) {
18525 pos = params.renderedPosition;
18526 }
18527 if (pos != null && !_p.panningEnabled) {
18528 // panning disabled
18529 bail = true;
18530 }
18531 }
18532
18533 // crop zoom
18534 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18535 zoom = zoom < _p.minZoom ? _p.minZoom : zoom;
18536
18537 // can't zoom with invalid params
18538 if (bail || !number$1(zoom) || zoom === currentZoom || pos != null && (!number$1(pos.x) || !number$1(pos.y))) {
18539 return null;
18540 }
18541 if (pos != null) {
18542 // set zoom about position
18543 var pan1 = currentPan;
18544 var zoom1 = currentZoom;
18545 var zoom2 = zoom;
18546 var pan2 = {
18547 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18548 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18549 };
18550 return {
18551 zoomed: true,
18552 panned: true,
18553 zoom: zoom2,
18554 pan: pan2
18555 };
18556 } else {
18557 // just set the zoom
18558 return {
18559 zoomed: true,
18560 panned: false,
18561 zoom: zoom,
18562 pan: currentPan
18563 };
18564 }
18565 },
18566 zoom: function zoom(params) {
18567 if (params === undefined) {
18568 // get
18569 return this._private.zoom;
18570 } else {
18571 // set
18572 var vp = this.getZoomedViewport(params);
18573 var _p = this._private;
18574 if (vp == null || !vp.zoomed) {
18575 return this;
18576 }
18577 _p.zoom = vp.zoom;
18578 if (vp.panned) {
18579 _p.pan.x = vp.pan.x;
18580 _p.pan.y = vp.pan.y;
18581 }
18582 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18583 this.notify('viewport');
18584 return this; // chaining
18585 }
18586 },
18587
18588 viewport: function viewport(opts) {
18589 var _p = this._private;
18590 var zoomDefd = true;
18591 var panDefd = true;
18592 var events = []; // to trigger
18593 var zoomFailed = false;
18594 var panFailed = false;
18595 if (!opts) {
18596 return this;
18597 }
18598 if (!number$1(opts.zoom)) {
18599 zoomDefd = false;
18600 }
18601 if (!plainObject(opts.pan)) {
18602 panDefd = false;
18603 }
18604 if (!zoomDefd && !panDefd) {
18605 return this;
18606 }
18607 if (zoomDefd) {
18608 var z = opts.zoom;
18609 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18610 zoomFailed = true;
18611 } else {
18612 _p.zoom = z;
18613 events.push('zoom');
18614 }
18615 }
18616 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18617 var p = opts.pan;
18618 if (number$1(p.x)) {
18619 _p.pan.x = p.x;
18620 panFailed = false;
18621 }
18622 if (number$1(p.y)) {
18623 _p.pan.y = p.y;
18624 panFailed = false;
18625 }
18626 if (!panFailed) {
18627 events.push('pan');
18628 }
18629 }
18630 if (events.length > 0) {
18631 events.push('viewport');
18632 this.emit(events.join(' '));
18633 this.notify('viewport');
18634 }
18635 return this; // chaining
18636 },
18637
18638 center: function center(elements) {
18639 var pan = this.getCenterPan(elements);
18640 if (pan) {
18641 this._private.pan = pan;
18642 this.emit('pan viewport');
18643 this.notify('viewport');
18644 }
18645 return this; // chaining
18646 },
18647
18648 getCenterPan: function getCenterPan(elements, zoom) {
18649 if (!this._private.panningEnabled) {
18650 return;
18651 }
18652 if (string(elements)) {
18653 var selector = elements;
18654 elements = this.mutableElements().filter(selector);
18655 } else if (!elementOrCollection(elements)) {
18656 elements = this.mutableElements();
18657 }
18658 if (elements.length === 0) {
18659 return;
18660 } // can't centre pan to nothing
18661
18662 var bb = elements.boundingBox();
18663 var w = this.width();
18664 var h = this.height();
18665 zoom = zoom === undefined ? this._private.zoom : zoom;
18666 var pan = {
18667 // middle
18668 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18669 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18670 };
18671 return pan;
18672 },
18673 reset: function reset() {
18674 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18675 return this;
18676 }
18677 this.viewport({
18678 pan: {
18679 x: 0,
18680 y: 0
18681 },
18682 zoom: 1
18683 });
18684 return this; // chaining
18685 },
18686
18687 invalidateSize: function invalidateSize() {
18688 this._private.sizeCache = null;
18689 },
18690 size: function size() {
18691 var _p = this._private;
18692 var container = _p.container;
18693 var cy = this;
18694 return _p.sizeCache = _p.sizeCache || (container ? function () {
18695 var style = cy.window().getComputedStyle(container);
18696 var val = function val(name) {
18697 return parseFloat(style.getPropertyValue(name));
18698 };
18699 return {
18700 width: container.clientWidth - val('padding-left') - val('padding-right'),
18701 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18702 };
18703 }() : {
18704 // fallback if no container (not 0 b/c can be used for dividing etc)
18705 width: 1,
18706 height: 1
18707 });
18708 },
18709 width: function width() {
18710 return this.size().width;
18711 },
18712 height: function height() {
18713 return this.size().height;
18714 },
18715 extent: function extent() {
18716 var pan = this._private.pan;
18717 var zoom = this._private.zoom;
18718 var rb = this.renderedExtent();
18719 var b = {
18720 x1: (rb.x1 - pan.x) / zoom,
18721 x2: (rb.x2 - pan.x) / zoom,
18722 y1: (rb.y1 - pan.y) / zoom,
18723 y2: (rb.y2 - pan.y) / zoom
18724 };
18725 b.w = b.x2 - b.x1;
18726 b.h = b.y2 - b.y1;
18727 return b;
18728 },
18729 renderedExtent: function renderedExtent() {
18730 var width = this.width();
18731 var height = this.height();
18732 return {
18733 x1: 0,
18734 y1: 0,
18735 x2: width,
18736 y2: height,
18737 w: width,
18738 h: height
18739 };
18740 },
18741 multiClickDebounceTime: function multiClickDebounceTime(_int) {
18742 if (_int) this._private.multiClickDebounceTime = _int;else return this._private.multiClickDebounceTime;
18743 return this; // chaining
18744 }
18745 };
18746
18747 // aliases
18748 corefn$1.centre = corefn$1.center;
18749
18750 // backwards compatibility
18751 corefn$1.autolockNodes = corefn$1.autolock;
18752 corefn$1.autoungrabifyNodes = corefn$1.autoungrabify;
18753
18754 var fn = {
18755 data: define.data({
18756 field: 'data',
18757 bindingEvent: 'data',
18758 allowBinding: true,
18759 allowSetting: true,
18760 settingEvent: 'data',
18761 settingTriggersEvent: true,
18762 triggerFnName: 'trigger',
18763 allowGetting: true,
18764 updateStyle: true
18765 }),
18766 removeData: define.removeData({
18767 field: 'data',
18768 event: 'data',
18769 triggerFnName: 'trigger',
18770 triggerEvent: true,
18771 updateStyle: true
18772 }),
18773 scratch: define.data({
18774 field: 'scratch',
18775 bindingEvent: 'scratch',
18776 allowBinding: true,
18777 allowSetting: true,
18778 settingEvent: 'scratch',
18779 settingTriggersEvent: true,
18780 triggerFnName: 'trigger',
18781 allowGetting: true,
18782 updateStyle: true
18783 }),
18784 removeScratch: define.removeData({
18785 field: 'scratch',
18786 event: 'scratch',
18787 triggerFnName: 'trigger',
18788 triggerEvent: true,
18789 updateStyle: true
18790 })
18791 };
18792
18793 // aliases
18794 fn.attr = fn.data;
18795 fn.removeAttr = fn.removeData;
18796
18797 var Core = function Core(opts) {
18798 var cy = this;
18799 opts = extend({}, opts);
18800 var container = opts.container;
18801
18802 // allow for passing a wrapped jquery object
18803 // e.g. cytoscape({ container: $('#cy') })
18804 if (container && !htmlElement(container) && htmlElement(container[0])) {
18805 container = container[0];
18806 }
18807 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18808 reg = reg || {};
18809 if (reg && reg.cy) {
18810 reg.cy.destroy();
18811 reg = {}; // old instance => replace reg completely
18812 }
18813
18814 var readies = reg.readies = reg.readies || [];
18815 if (container) {
18816 container._cyreg = reg;
18817 } // make sure container assoc'd reg points to this cy
18818 reg.cy = cy;
18819 var head = _window !== undefined && container !== undefined && !opts.headless;
18820 var options = opts;
18821 options.layout = extend({
18822 name: head ? 'grid' : 'null'
18823 }, options.layout);
18824 options.renderer = extend({
18825 name: head ? 'canvas' : 'null'
18826 }, options.renderer);
18827 var defVal = function defVal(def, val, altVal) {
18828 if (val !== undefined) {
18829 return val;
18830 } else if (altVal !== undefined) {
18831 return altVal;
18832 } else {
18833 return def;
18834 }
18835 };
18836 var _p = this._private = {
18837 container: container,
18838 // html dom ele container
18839 ready: false,
18840 // whether ready has been triggered
18841 options: options,
18842 // cached options
18843 elements: new Collection(this),
18844 // elements in the graph
18845 listeners: [],
18846 // list of listeners
18847 aniEles: new Collection(this),
18848 // elements being animated
18849 data: options.data || {},
18850 // data for the core
18851 scratch: {},
18852 // scratch object for core
18853 layout: null,
18854 renderer: null,
18855 destroyed: false,
18856 // whether destroy was called
18857 notificationsEnabled: true,
18858 // whether notifications are sent to the renderer
18859 minZoom: 1e-50,
18860 maxZoom: 1e50,
18861 zoomingEnabled: defVal(true, options.zoomingEnabled),
18862 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18863 panningEnabled: defVal(true, options.panningEnabled),
18864 userPanningEnabled: defVal(true, options.userPanningEnabled),
18865 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18866 autolock: defVal(false, options.autolock, options.autolockNodes),
18867 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18868 autounselectify: defVal(false, options.autounselectify),
18869 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18870 zoom: number$1(options.zoom) ? options.zoom : 1,
18871 pan: {
18872 x: plainObject(options.pan) && number$1(options.pan.x) ? options.pan.x : 0,
18873 y: plainObject(options.pan) && number$1(options.pan.y) ? options.pan.y : 0
18874 },
18875 animation: {
18876 // object for currently-running animations
18877 current: [],
18878 queue: []
18879 },
18880 hasCompoundNodes: false,
18881 multiClickDebounceTime: defVal(250, options.multiClickDebounceTime)
18882 };
18883 this.createEmitter();
18884
18885 // set selection type
18886 this.selectionType(options.selectionType);
18887
18888 // init zoom bounds
18889 this.zoomRange({
18890 min: options.minZoom,
18891 max: options.maxZoom
18892 });
18893 var loadExtData = function loadExtData(extData, next) {
18894 var anyIsPromise = extData.some(promise);
18895 if (anyIsPromise) {
18896 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18897 } else {
18898 next(extData); // exec synchronously for convenience
18899 }
18900 };
18901
18902 // start with the default stylesheet so we have something before loading an external stylesheet
18903 if (_p.styleEnabled) {
18904 cy.setStyle([]);
18905 }
18906
18907 // create the renderer
18908 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18909 cy.initRenderer(rendererOptions);
18910 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18911 cy.notifications(false);
18912
18913 // remove old elements
18914 var oldEles = cy.mutableElements();
18915 if (oldEles.length > 0) {
18916 oldEles.remove();
18917 }
18918 if (elements != null) {
18919 if (plainObject(elements) || array(elements)) {
18920 cy.add(elements);
18921 }
18922 }
18923 cy.one('layoutready', function (e) {
18924 cy.notifications(true);
18925 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18926
18927 cy.one('load', onload);
18928 cy.emitAndNotify('load');
18929 }).one('layoutstop', function () {
18930 cy.one('done', ondone);
18931 cy.emit('done');
18932 });
18933 var layoutOpts = extend({}, cy._private.options.layout);
18934 layoutOpts.eles = cy.elements();
18935 cy.layout(layoutOpts).run();
18936 };
18937 loadExtData([options.style, options.elements], function (thens) {
18938 var initStyle = thens[0];
18939 var initEles = thens[1];
18940
18941 // init style
18942 if (_p.styleEnabled) {
18943 cy.style().append(initStyle);
18944 }
18945
18946 // initial load
18947 setElesAndLayout(initEles, function () {
18948 // onready
18949 cy.startAnimationLoop();
18950 _p.ready = true;
18951
18952 // if a ready callback is specified as an option, the bind it
18953 if (fn$6(options.ready)) {
18954 cy.on('ready', options.ready);
18955 }
18956
18957 // bind all the ready handlers registered before creating this instance
18958 for (var i = 0; i < readies.length; i++) {
18959 var fn = readies[i];
18960 cy.on('ready', fn);
18961 }
18962 if (reg) {
18963 reg.readies = [];
18964 } // 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
18965
18966 cy.emit('ready');
18967 }, options.done);
18968 });
18969 };
18970 var corefn = Core.prototype; // short alias
18971
18972 extend(corefn, {
18973 instanceString: function instanceString() {
18974 return 'core';
18975 },
18976 isReady: function isReady() {
18977 return this._private.ready;
18978 },
18979 destroyed: function destroyed() {
18980 return this._private.destroyed;
18981 },
18982 ready: function ready(fn) {
18983 if (this.isReady()) {
18984 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18985 } else {
18986 this.on('ready', fn);
18987 }
18988 return this;
18989 },
18990 destroy: function destroy() {
18991 var cy = this;
18992 if (cy.destroyed()) return;
18993 cy.stopAnimationLoop();
18994 cy.destroyRenderer();
18995 this.emit('destroy');
18996 cy._private.destroyed = true;
18997 return cy;
18998 },
18999 hasElementWithId: function hasElementWithId(id) {
19000 return this._private.elements.hasElementWithId(id);
19001 },
19002 getElementById: function getElementById(id) {
19003 return this._private.elements.getElementById(id);
19004 },
19005 hasCompoundNodes: function hasCompoundNodes() {
19006 return this._private.hasCompoundNodes;
19007 },
19008 headless: function headless() {
19009 return this._private.renderer.isHeadless();
19010 },
19011 styleEnabled: function styleEnabled() {
19012 return this._private.styleEnabled;
19013 },
19014 addToPool: function addToPool(eles) {
19015 this._private.elements.merge(eles);
19016 return this; // chaining
19017 },
19018
19019 removeFromPool: function removeFromPool(eles) {
19020 this._private.elements.unmerge(eles);
19021 return this;
19022 },
19023 container: function container() {
19024 return this._private.container || null;
19025 },
19026 window: function window() {
19027 var container = this._private.container;
19028 if (container == null) return _window;
19029 var ownerDocument = this._private.container.ownerDocument;
19030 if (ownerDocument === undefined || ownerDocument == null) {
19031 return _window;
19032 }
19033 return ownerDocument.defaultView || _window;
19034 },
19035 mount: function mount(container) {
19036 if (container == null) {
19037 return;
19038 }
19039 var cy = this;
19040 var _p = cy._private;
19041 var options = _p.options;
19042 if (!htmlElement(container) && htmlElement(container[0])) {
19043 container = container[0];
19044 }
19045 cy.stopAnimationLoop();
19046 cy.destroyRenderer();
19047 _p.container = container;
19048 _p.styleEnabled = true;
19049 cy.invalidateSize();
19050 cy.initRenderer(extend({}, options, options.renderer, {
19051 // allow custom renderer name to be re-used, otherwise use canvas
19052 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
19053 }));
19054 cy.startAnimationLoop();
19055 cy.style(options.style);
19056 cy.emit('mount');
19057 return cy;
19058 },
19059 unmount: function unmount() {
19060 var cy = this;
19061 cy.stopAnimationLoop();
19062 cy.destroyRenderer();
19063 cy.initRenderer({
19064 name: 'null'
19065 });
19066 cy.emit('unmount');
19067 return cy;
19068 },
19069 options: function options() {
19070 return copy(this._private.options);
19071 },
19072 json: function json(obj) {
19073 var cy = this;
19074 var _p = cy._private;
19075 var eles = cy.mutableElements();
19076 var getFreshRef = function getFreshRef(ele) {
19077 return cy.getElementById(ele.id());
19078 };
19079 if (plainObject(obj)) {
19080 // set
19081
19082 cy.startBatch();
19083 if (obj.elements) {
19084 var idInJson = {};
19085 var updateEles = function updateEles(jsons, gr) {
19086 var toAdd = [];
19087 var toMod = [];
19088 for (var i = 0; i < jsons.length; i++) {
19089 var json = jsons[i];
19090 if (!json.data.id) {
19091 warn('cy.json() cannot handle elements without an ID attribute');
19092 continue;
19093 }
19094 var id = '' + json.data.id; // id must be string
19095 var ele = cy.getElementById(id);
19096 idInJson[id] = true;
19097 if (ele.length !== 0) {
19098 // existing element should be updated
19099 toMod.push({
19100 ele: ele,
19101 json: json
19102 });
19103 } else {
19104 // otherwise should be added
19105 if (gr) {
19106 json.group = gr;
19107 toAdd.push(json);
19108 } else {
19109 toAdd.push(json);
19110 }
19111 }
19112 }
19113 cy.add(toAdd);
19114 for (var _i = 0; _i < toMod.length; _i++) {
19115 var _toMod$_i = toMod[_i],
19116 _ele = _toMod$_i.ele,
19117 _json = _toMod$_i.json;
19118 _ele.json(_json);
19119 }
19120 };
19121 if (array(obj.elements)) {
19122 // elements: []
19123 updateEles(obj.elements);
19124 } else {
19125 // elements: { nodes: [], edges: [] }
19126 var grs = ['nodes', 'edges'];
19127 for (var i = 0; i < grs.length; i++) {
19128 var gr = grs[i];
19129 var elements = obj.elements[gr];
19130 if (array(elements)) {
19131 updateEles(elements, gr);
19132 }
19133 }
19134 }
19135 var parentsToRemove = cy.collection();
19136 eles.filter(function (ele) {
19137 return !idInJson[ele.id()];
19138 }).forEach(function (ele) {
19139 if (ele.isParent()) {
19140 parentsToRemove.merge(ele);
19141 } else {
19142 ele.remove();
19143 }
19144 });
19145
19146 // so that children are not removed w/parent
19147 parentsToRemove.forEach(function (ele) {
19148 return ele.children().move({
19149 parent: null
19150 });
19151 });
19152
19153 // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
19154 parentsToRemove.forEach(function (ele) {
19155 return getFreshRef(ele).remove();
19156 });
19157 }
19158 if (obj.style) {
19159 cy.style(obj.style);
19160 }
19161 if (obj.zoom != null && obj.zoom !== _p.zoom) {
19162 cy.zoom(obj.zoom);
19163 }
19164 if (obj.pan) {
19165 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
19166 cy.pan(obj.pan);
19167 }
19168 }
19169 if (obj.data) {
19170 cy.data(obj.data);
19171 }
19172 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify', 'multiClickDebounceTime'];
19173 for (var _i2 = 0; _i2 < fields.length; _i2++) {
19174 var f = fields[_i2];
19175 if (obj[f] != null) {
19176 cy[f](obj[f]);
19177 }
19178 }
19179 cy.endBatch();
19180 return this; // chaining
19181 } else {
19182 // get
19183 var flat = !!obj;
19184 var json = {};
19185 if (flat) {
19186 json.elements = this.elements().map(function (ele) {
19187 return ele.json();
19188 });
19189 } else {
19190 json.elements = {};
19191 eles.forEach(function (ele) {
19192 var group = ele.group();
19193 if (!json.elements[group]) {
19194 json.elements[group] = [];
19195 }
19196 json.elements[group].push(ele.json());
19197 });
19198 }
19199 if (this._private.styleEnabled) {
19200 json.style = cy.style().json();
19201 }
19202 json.data = copy(cy.data());
19203 var options = _p.options;
19204 json.zoomingEnabled = _p.zoomingEnabled;
19205 json.userZoomingEnabled = _p.userZoomingEnabled;
19206 json.zoom = _p.zoom;
19207 json.minZoom = _p.minZoom;
19208 json.maxZoom = _p.maxZoom;
19209 json.panningEnabled = _p.panningEnabled;
19210 json.userPanningEnabled = _p.userPanningEnabled;
19211 json.pan = copy(_p.pan);
19212 json.boxSelectionEnabled = _p.boxSelectionEnabled;
19213 json.renderer = copy(options.renderer);
19214 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
19215 json.textureOnViewport = options.textureOnViewport;
19216 json.wheelSensitivity = options.wheelSensitivity;
19217 json.motionBlur = options.motionBlur;
19218 json.multiClickDebounceTime = options.multiClickDebounceTime;
19219 return json;
19220 }
19221 }
19222 });
19223 corefn.$id = corefn.getElementById;
19224 [corefn$9, corefn$8, elesfn, corefn$7, corefn$6, corefn$5, corefn$4, corefn$3, corefn$2, corefn$1, fn].forEach(function (props) {
19225 extend(corefn, props);
19226 });
19227
19228 /* eslint-disable no-unused-vars */
19229 var defaults$7 = {
19230 fit: true,
19231 // whether to fit the viewport to the graph
19232 directed: false,
19233 // whether the tree is directed downwards (or edges can point in any direction if false)
19234 padding: 30,
19235 // padding on fit
19236 circle: false,
19237 // put depths in concentric circles if true, put depths top down if false
19238 grid: false,
19239 // whether to create an even grid into which the DAG is placed (circle:false only)
19240 spacingFactor: 1.75,
19241 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
19242 boundingBox: undefined,
19243 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19244 avoidOverlap: true,
19245 // prevents node overlap, may overflow boundingBox if not enough space
19246 nodeDimensionsIncludeLabels: false,
19247 // Excludes the label when calculating node bounding boxes for the layout algorithm
19248 roots: undefined,
19249 // the roots of the trees
19250 depthSort: undefined,
19251 // a sorting function to order nodes at equal depth. e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19252 animate: false,
19253 // whether to transition the node positions
19254 animationDuration: 500,
19255 // duration of animation in ms if enabled
19256 animationEasing: undefined,
19257 // easing of animation if enabled,
19258 animateFilter: function animateFilter(node, i) {
19259 return true;
19260 },
19261 // 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
19262 ready: undefined,
19263 // callback on layoutready
19264 stop: undefined,
19265 // callback on layoutstop
19266 transform: function transform(node, position) {
19267 return position;
19268 } // transform a given node position. Useful for changing flow direction in discrete layouts
19269 };
19270
19271 var deprecatedOptionDefaults = {
19272 maximal: false,
19273 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only); setting acyclic to true sets maximal to true also
19274 acyclic: false // whether the tree is acyclic and thus a node could be shifted (due to the maximal option) multiple times without causing an infinite loop; setting to true sets maximal to true also; if you are uncertain whether a tree is acyclic, set to false to avoid potential infinite loops
19275 };
19276
19277 /* eslint-enable */
19278
19279 var getInfo = function getInfo(ele) {
19280 return ele.scratch('breadthfirst');
19281 };
19282 var setInfo = function setInfo(ele, obj) {
19283 return ele.scratch('breadthfirst', obj);
19284 };
19285 function BreadthFirstLayout(options) {
19286 this.options = extend({}, defaults$7, deprecatedOptionDefaults, options);
19287 }
19288 BreadthFirstLayout.prototype.run = function () {
19289 var params = this.options;
19290 var options = params;
19291 var cy = params.cy;
19292 var eles = options.eles;
19293 var nodes = eles.nodes().filter(function (n) {
19294 return !n.isParent();
19295 });
19296 var graph = eles;
19297 var directed = options.directed;
19298 var maximal = options.acyclic || options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code; also, setting acyclic to true sets maximal to true
19299
19300 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19301 x1: 0,
19302 y1: 0,
19303 w: cy.width(),
19304 h: cy.height()
19305 });
19306 var roots;
19307 if (elementOrCollection(options.roots)) {
19308 roots = options.roots;
19309 } else if (array(options.roots)) {
19310 var rootsArray = [];
19311 for (var i = 0; i < options.roots.length; i++) {
19312 var id = options.roots[i];
19313 var ele = cy.getElementById(id);
19314 rootsArray.push(ele);
19315 }
19316 roots = cy.collection(rootsArray);
19317 } else if (string(options.roots)) {
19318 roots = cy.$(options.roots);
19319 } else {
19320 if (directed) {
19321 roots = nodes.roots();
19322 } else {
19323 var components = eles.components();
19324 roots = cy.collection();
19325 var _loop = function _loop(_i) {
19326 var comp = components[_i];
19327 var maxDegree = comp.maxDegree(false);
19328 var compRoots = comp.filter(function (ele) {
19329 return ele.degree(false) === maxDegree;
19330 });
19331 roots = roots.add(compRoots);
19332 };
19333 for (var _i = 0; _i < components.length; _i++) {
19334 _loop(_i);
19335 }
19336 }
19337 }
19338 var depths = [];
19339 var foundByBfs = {};
19340 var addToDepth = function addToDepth(ele, d) {
19341 if (depths[d] == null) {
19342 depths[d] = [];
19343 }
19344 var i = depths[d].length;
19345 depths[d].push(ele);
19346 setInfo(ele, {
19347 index: i,
19348 depth: d
19349 });
19350 };
19351 var changeDepth = function changeDepth(ele, newDepth) {
19352 var _getInfo = getInfo(ele),
19353 depth = _getInfo.depth,
19354 index = _getInfo.index;
19355 depths[depth][index] = null;
19356 addToDepth(ele, newDepth);
19357 };
19358
19359 // find the depths of the nodes
19360 graph.bfs({
19361 roots: roots,
19362 directed: options.directed,
19363 visit: function visit(node, edge, pNode, i, depth) {
19364 var ele = node[0];
19365 var id = ele.id();
19366 addToDepth(ele, depth);
19367 foundByBfs[id] = true;
19368 }
19369 });
19370
19371 // check for nodes not found by bfs
19372 var orphanNodes = [];
19373 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19374 var _ele = nodes[_i2];
19375 if (foundByBfs[_ele.id()]) {
19376 continue;
19377 } else {
19378 orphanNodes.push(_ele);
19379 }
19380 }
19381
19382 // assign the nodes a depth and index
19383
19384 var assignDepthsAt = function assignDepthsAt(i) {
19385 var eles = depths[i];
19386 for (var j = 0; j < eles.length; j++) {
19387 var _ele2 = eles[j];
19388 if (_ele2 == null) {
19389 eles.splice(j, 1);
19390 j--;
19391 continue;
19392 }
19393 setInfo(_ele2, {
19394 depth: i,
19395 index: j
19396 });
19397 }
19398 };
19399 var assignDepths = function assignDepths() {
19400 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19401 assignDepthsAt(_i3);
19402 }
19403 };
19404 var adjustMaximally = function adjustMaximally(ele, shifted) {
19405 var eInfo = getInfo(ele);
19406 var incomers = ele.incomers().filter(function (el) {
19407 return el.isNode() && eles.has(el);
19408 });
19409 var maxDepth = -1;
19410 var id = ele.id();
19411 for (var k = 0; k < incomers.length; k++) {
19412 var incmr = incomers[k];
19413 var iInfo = getInfo(incmr);
19414 maxDepth = Math.max(maxDepth, iInfo.depth);
19415 }
19416 if (eInfo.depth <= maxDepth) {
19417 if (!options.acyclic && shifted[id]) {
19418 return null;
19419 }
19420 var newDepth = maxDepth + 1;
19421 changeDepth(ele, newDepth);
19422 shifted[id] = newDepth;
19423 return true;
19424 }
19425 return false;
19426 };
19427
19428 // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19429 if (directed && maximal) {
19430 var Q = [];
19431 var shifted = {};
19432 var enqueue = function enqueue(n) {
19433 return Q.push(n);
19434 };
19435 var dequeue = function dequeue() {
19436 return Q.shift();
19437 };
19438 nodes.forEach(function (n) {
19439 return Q.push(n);
19440 });
19441 while (Q.length > 0) {
19442 var _ele3 = dequeue();
19443 var didShift = adjustMaximally(_ele3, shifted);
19444 if (didShift) {
19445 _ele3.outgoers().filter(function (el) {
19446 return el.isNode() && eles.has(el);
19447 }).forEach(enqueue);
19448 } else if (didShift === null) {
19449 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19450 break; // exit on failure
19451 }
19452 }
19453 }
19454
19455 assignDepths(); // clear holes
19456
19457 // find min distance we need to leave between nodes
19458 var minDistance = 0;
19459 if (options.avoidOverlap) {
19460 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19461 var n = nodes[_i4];
19462 var nbb = n.layoutDimensions(options);
19463 var w = nbb.w;
19464 var h = nbb.h;
19465 minDistance = Math.max(minDistance, w, h);
19466 }
19467 }
19468
19469 // get the weighted percent for an element based on its connectivity to other levels
19470 var cachedWeightedPercent = {};
19471 var getWeightedPercent = function getWeightedPercent(ele) {
19472 if (cachedWeightedPercent[ele.id()]) {
19473 return cachedWeightedPercent[ele.id()];
19474 }
19475 var eleDepth = getInfo(ele).depth;
19476 var neighbors = ele.neighborhood();
19477 var percent = 0;
19478 var samples = 0;
19479 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19480 var neighbor = neighbors[_i5];
19481 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19482 continue;
19483 }
19484 var bf = getInfo(neighbor);
19485 if (bf == null) {
19486 continue;
19487 }
19488 var index = bf.index;
19489 var depth = bf.depth;
19490
19491 // unassigned neighbours shouldn't affect the ordering
19492 if (index == null || depth == null) {
19493 continue;
19494 }
19495 var nDepth = depths[depth].length;
19496 if (depth < eleDepth) {
19497 // only get influenced by elements above
19498 percent += index / nDepth;
19499 samples++;
19500 }
19501 }
19502 samples = Math.max(1, samples);
19503 percent = percent / samples;
19504 if (samples === 0) {
19505 // put lone nodes at the start
19506 percent = 0;
19507 }
19508 cachedWeightedPercent[ele.id()] = percent;
19509 return percent;
19510 };
19511
19512 // rearrange the indices in each depth level based on connectivity
19513
19514 var sortFn = function sortFn(a, b) {
19515 var apct = getWeightedPercent(a);
19516 var bpct = getWeightedPercent(b);
19517 var diff = apct - bpct;
19518 if (diff === 0) {
19519 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19520 } else {
19521 return diff;
19522 }
19523 };
19524 if (options.depthSort !== undefined) {
19525 sortFn = options.depthSort;
19526 }
19527
19528 // sort each level to make connected nodes closer
19529 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19530 depths[_i6].sort(sortFn);
19531 assignDepthsAt(_i6);
19532 }
19533
19534 // assign orphan nodes to a new top-level depth
19535 var orphanDepth = [];
19536 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19537 orphanDepth.push(orphanNodes[_i7]);
19538 }
19539 depths.unshift(orphanDepth);
19540 assignDepths();
19541 var biggestDepthSize = 0;
19542 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19543 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19544 }
19545 var center = {
19546 x: bb.x1 + bb.w / 2,
19547 y: bb.x1 + bb.h / 2
19548 };
19549 var maxDepthSize = depths.reduce(function (max, eles) {
19550 return Math.max(max, eles.length);
19551 }, 0);
19552 var getPosition = function getPosition(ele) {
19553 var _getInfo2 = getInfo(ele),
19554 depth = _getInfo2.depth,
19555 index = _getInfo2.index;
19556 var depthSize = depths[depth].length;
19557 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19558 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19559 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19560 radiusStepSize = Math.max(radiusStepSize, minDistance);
19561 if (!options.circle) {
19562 var epos = {
19563 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19564 y: (depth + 1) * distanceY
19565 };
19566 return epos;
19567 } else {
19568 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19569 var theta = 2 * Math.PI / depths[depth].length * index;
19570 if (depth === 0 && depths[0].length === 1) {
19571 radius = 1;
19572 }
19573 return {
19574 x: center.x + radius * Math.cos(theta),
19575 y: center.y + radius * Math.sin(theta)
19576 };
19577 }
19578 };
19579 eles.nodes().layoutPositions(this, options, getPosition);
19580 return this; // chaining
19581 };
19582
19583 var defaults$6 = {
19584 fit: true,
19585 // whether to fit the viewport to the graph
19586 padding: 30,
19587 // the padding on fit
19588 boundingBox: undefined,
19589 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19590 avoidOverlap: true,
19591 // prevents node overlap, may overflow boundingBox and radius if not enough space
19592 nodeDimensionsIncludeLabels: false,
19593 // Excludes the label when calculating node bounding boxes for the layout algorithm
19594 spacingFactor: undefined,
19595 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19596 radius: undefined,
19597 // the radius of the circle
19598 startAngle: 3 / 2 * Math.PI,
19599 // where nodes start in radians
19600 sweep: undefined,
19601 // how many radians should be between the first and last node (defaults to full circle)
19602 clockwise: true,
19603 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19604 sort: undefined,
19605 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19606 animate: false,
19607 // whether to transition the node positions
19608 animationDuration: 500,
19609 // duration of animation in ms if enabled
19610 animationEasing: undefined,
19611 // easing of animation if enabled
19612 animateFilter: function animateFilter(node, i) {
19613 return true;
19614 },
19615 // 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
19616 ready: undefined,
19617 // callback on layoutready
19618 stop: undefined,
19619 // callback on layoutstop
19620 transform: function transform(node, position) {
19621 return position;
19622 } // transform a given node position. Useful for changing flow direction in discrete layouts
19623 };
19624
19625 function CircleLayout(options) {
19626 this.options = extend({}, defaults$6, options);
19627 }
19628 CircleLayout.prototype.run = function () {
19629 var params = this.options;
19630 var options = params;
19631 var cy = params.cy;
19632 var eles = options.eles;
19633 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19634 var nodes = eles.nodes().not(':parent');
19635 if (options.sort) {
19636 nodes = nodes.sort(options.sort);
19637 }
19638 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19639 x1: 0,
19640 y1: 0,
19641 w: cy.width(),
19642 h: cy.height()
19643 });
19644 var center = {
19645 x: bb.x1 + bb.w / 2,
19646 y: bb.y1 + bb.h / 2
19647 };
19648 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19649 var dTheta = sweep / Math.max(1, nodes.length - 1);
19650 var r;
19651 var minDistance = 0;
19652 for (var i = 0; i < nodes.length; i++) {
19653 var n = nodes[i];
19654 var nbb = n.layoutDimensions(options);
19655 var w = nbb.w;
19656 var h = nbb.h;
19657 minDistance = Math.max(minDistance, w, h);
19658 }
19659 if (number$1(options.radius)) {
19660 r = options.radius;
19661 } else if (nodes.length <= 1) {
19662 r = 0;
19663 } else {
19664 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19665 }
19666
19667 // calculate the radius
19668 if (nodes.length > 1 && options.avoidOverlap) {
19669 // but only if more than one node (can't overlap)
19670 minDistance *= 1.75; // just to have some nice spacing
19671
19672 var dcos = Math.cos(dTheta) - Math.cos(0);
19673 var dsin = Math.sin(dTheta) - Math.sin(0);
19674 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19675 r = Math.max(rMin, r);
19676 }
19677 var getPos = function getPos(ele, i) {
19678 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19679 var rx = r * Math.cos(theta);
19680 var ry = r * Math.sin(theta);
19681 var pos = {
19682 x: center.x + rx,
19683 y: center.y + ry
19684 };
19685 return pos;
19686 };
19687 eles.nodes().layoutPositions(this, options, getPos);
19688 return this; // chaining
19689 };
19690
19691 var defaults$5 = {
19692 fit: true,
19693 // whether to fit the viewport to the graph
19694 padding: 30,
19695 // the padding on fit
19696 startAngle: 3 / 2 * Math.PI,
19697 // where nodes start in radians
19698 sweep: undefined,
19699 // how many radians should be between the first and last node (defaults to full circle)
19700 clockwise: true,
19701 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19702 equidistant: false,
19703 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19704 minNodeSpacing: 10,
19705 // min spacing between outside of nodes (used for radius adjustment)
19706 boundingBox: undefined,
19707 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19708 avoidOverlap: true,
19709 // prevents node overlap, may overflow boundingBox if not enough space
19710 nodeDimensionsIncludeLabels: false,
19711 // Excludes the label when calculating node bounding boxes for the layout algorithm
19712 height: undefined,
19713 // height of layout area (overrides container height)
19714 width: undefined,
19715 // width of layout area (overrides container width)
19716 spacingFactor: undefined,
19717 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19718 concentric: function concentric(node) {
19719 // returns numeric value for each node, placing higher nodes in levels towards the centre
19720 return node.degree();
19721 },
19722 levelWidth: function levelWidth(nodes) {
19723 // the variation of concentric values in each level
19724 return nodes.maxDegree() / 4;
19725 },
19726 animate: false,
19727 // whether to transition the node positions
19728 animationDuration: 500,
19729 // duration of animation in ms if enabled
19730 animationEasing: undefined,
19731 // easing of animation if enabled
19732 animateFilter: function animateFilter(node, i) {
19733 return true;
19734 },
19735 // 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
19736 ready: undefined,
19737 // callback on layoutready
19738 stop: undefined,
19739 // callback on layoutstop
19740 transform: function transform(node, position) {
19741 return position;
19742 } // transform a given node position. Useful for changing flow direction in discrete layouts
19743 };
19744
19745 function ConcentricLayout(options) {
19746 this.options = extend({}, defaults$5, options);
19747 }
19748 ConcentricLayout.prototype.run = function () {
19749 var params = this.options;
19750 var options = params;
19751 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19752 var cy = params.cy;
19753 var eles = options.eles;
19754 var nodes = eles.nodes().not(':parent');
19755 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19756 x1: 0,
19757 y1: 0,
19758 w: cy.width(),
19759 h: cy.height()
19760 });
19761 var center = {
19762 x: bb.x1 + bb.w / 2,
19763 y: bb.y1 + bb.h / 2
19764 };
19765 var nodeValues = []; // { node, value }
19766 var maxNodeSize = 0;
19767 for (var i = 0; i < nodes.length; i++) {
19768 var node = nodes[i];
19769 var value = void 0;
19770
19771 // calculate the node value
19772 value = options.concentric(node);
19773 nodeValues.push({
19774 value: value,
19775 node: node
19776 });
19777
19778 // for style mapping
19779 node._private.scratch.concentric = value;
19780 }
19781
19782 // in case we used the `concentric` in style
19783 nodes.updateStyle();
19784
19785 // calculate max size now based on potentially updated mappers
19786 for (var _i = 0; _i < nodes.length; _i++) {
19787 var _node = nodes[_i];
19788 var nbb = _node.layoutDimensions(options);
19789 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19790 }
19791
19792 // sort node values in descreasing order
19793 nodeValues.sort(function (a, b) {
19794 return b.value - a.value;
19795 });
19796 var levelWidth = options.levelWidth(nodes);
19797
19798 // put the values into levels
19799 var levels = [[]];
19800 var currentLevel = levels[0];
19801 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19802 var val = nodeValues[_i2];
19803 if (currentLevel.length > 0) {
19804 var diff = Math.abs(currentLevel[0].value - val.value);
19805 if (diff >= levelWidth) {
19806 currentLevel = [];
19807 levels.push(currentLevel);
19808 }
19809 }
19810 currentLevel.push(val);
19811 }
19812
19813 // create positions from levels
19814
19815 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19816
19817 if (!options.avoidOverlap) {
19818 // then strictly constrain to bb
19819 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19820 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19821 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19822 minDist = Math.min(minDist, rStep);
19823 }
19824
19825 // find the metrics for each level
19826 var r = 0;
19827 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19828 var level = levels[_i3];
19829 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19830 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1);
19831
19832 // calculate the radius
19833 if (level.length > 1 && options.avoidOverlap) {
19834 // but only if more than one node (can't overlap)
19835 var dcos = Math.cos(dTheta) - Math.cos(0);
19836 var dsin = Math.sin(dTheta) - Math.sin(0);
19837 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19838
19839 r = Math.max(rMin, r);
19840 }
19841 level.r = r;
19842 r += minDist;
19843 }
19844 if (options.equidistant) {
19845 var rDeltaMax = 0;
19846 var _r = 0;
19847 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19848 var _level = levels[_i4];
19849 var rDelta = _level.r - _r;
19850 rDeltaMax = Math.max(rDeltaMax, rDelta);
19851 }
19852 _r = 0;
19853 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19854 var _level2 = levels[_i5];
19855 if (_i5 === 0) {
19856 _r = _level2.r;
19857 }
19858 _level2.r = _r;
19859 _r += rDeltaMax;
19860 }
19861 }
19862
19863 // calculate the node positions
19864 var pos = {}; // id => position
19865 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19866 var _level3 = levels[_i6];
19867 var _dTheta = _level3.dTheta;
19868 var _r2 = _level3.r;
19869 for (var j = 0; j < _level3.length; j++) {
19870 var _val = _level3[j];
19871 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19872 var p = {
19873 x: center.x + _r2 * Math.cos(theta),
19874 y: center.y + _r2 * Math.sin(theta)
19875 };
19876 pos[_val.node.id()] = p;
19877 }
19878 }
19879
19880 // position the nodes
19881 eles.nodes().layoutPositions(this, options, function (ele) {
19882 var id = ele.id();
19883 return pos[id];
19884 });
19885 return this; // chaining
19886 };
19887
19888 /*
19889 The CoSE layout was written by Gerardo Huck.
19890 https://www.linkedin.com/in/gerardohuck/
19891
19892 Based on the following article:
19893 http://dl.acm.org/citation.cfm?id=1498047
19894
19895 Modifications tracked on Github.
19896 */
19897 var DEBUG;
19898
19899 /**
19900 * @brief : default layout options
19901 */
19902 var defaults$4 = {
19903 // Called on `layoutready`
19904 ready: function ready() {},
19905 // Called on `layoutstop`
19906 stop: function stop() {},
19907 // Whether to animate while running the layout
19908 // true : Animate continuously as the layout is running
19909 // false : Just show the end result
19910 // 'end' : Animate with the end result, from the initial positions to the end positions
19911 animate: true,
19912 // Easing of the animation for animate:'end'
19913 animationEasing: undefined,
19914 // The duration of the animation for animate:'end'
19915 animationDuration: undefined,
19916 // A function that determines whether the node should be animated
19917 // All nodes animated by default on animate enabled
19918 // Non-animated nodes are positioned immediately when the layout starts
19919 animateFilter: function animateFilter(node, i) {
19920 return true;
19921 },
19922 // The layout animates only after this many milliseconds for animate:true
19923 // (prevents flashing on fast runs)
19924 animationThreshold: 250,
19925 // Number of iterations between consecutive screen positions update
19926 refresh: 20,
19927 // Whether to fit the network view after when done
19928 fit: true,
19929 // Padding on fit
19930 padding: 30,
19931 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19932 boundingBox: undefined,
19933 // Excludes the label when calculating node bounding boxes for the layout algorithm
19934 nodeDimensionsIncludeLabels: false,
19935 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19936 randomize: false,
19937 // Extra spacing between components in non-compound graphs
19938 componentSpacing: 40,
19939 // Node repulsion (non overlapping) multiplier
19940 nodeRepulsion: function nodeRepulsion(node) {
19941 return 2048;
19942 },
19943 // Node repulsion (overlapping) multiplier
19944 nodeOverlap: 4,
19945 // Ideal edge (non nested) length
19946 idealEdgeLength: function idealEdgeLength(edge) {
19947 return 32;
19948 },
19949 // Divisor to compute edge forces
19950 edgeElasticity: function edgeElasticity(edge) {
19951 return 32;
19952 },
19953 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19954 nestingFactor: 1.2,
19955 // Gravity force (constant)
19956 gravity: 1,
19957 // Maximum number of iterations to perform
19958 numIter: 1000,
19959 // Initial temperature (maximum node displacement)
19960 initialTemp: 1000,
19961 // Cooling factor (how the temperature is reduced between consecutive iterations
19962 coolingFactor: 0.99,
19963 // Lower temperature threshold (below this point the layout will end)
19964 minTemp: 1.0
19965 };
19966
19967 /**
19968 * @brief : constructor
19969 * @arg options : object containing layout options
19970 */
19971 function CoseLayout(options) {
19972 this.options = extend({}, defaults$4, options);
19973 this.options.layout = this;
19974
19975 // Exclude any edge that has a source or target node that is not in the set of passed-in nodes
19976 var nodes = this.options.eles.nodes();
19977 var edges = this.options.eles.edges();
19978 var notEdges = edges.filter(function (e) {
19979 var sourceId = e.source().data('id');
19980 var targetId = e.target().data('id');
19981 var hasSource = nodes.some(function (n) {
19982 return n.data('id') === sourceId;
19983 });
19984 var hasTarget = nodes.some(function (n) {
19985 return n.data('id') === targetId;
19986 });
19987 return !hasSource || !hasTarget;
19988 });
19989 this.options.eles = this.options.eles.not(notEdges);
19990 }
19991
19992 /**
19993 * @brief : runs the layout
19994 */
19995 CoseLayout.prototype.run = function () {
19996 var options = this.options;
19997 var cy = options.cy;
19998 var layout = this;
19999 layout.stopped = false;
20000 if (options.animate === true || options.animate === false) {
20001 layout.emit({
20002 type: 'layoutstart',
20003 layout: layout
20004 });
20005 }
20006
20007 // Set DEBUG - Global variable
20008 if (true === options.debug) {
20009 DEBUG = true;
20010 } else {
20011 DEBUG = false;
20012 }
20013
20014 // Initialize layout info
20015 var layoutInfo = createLayoutInfo(cy, layout, options);
20016
20017 // Show LayoutInfo contents if debugging
20018 if (DEBUG) {
20019 printLayoutInfo(layoutInfo);
20020 }
20021
20022 // If required, randomize node positions
20023 if (options.randomize) {
20024 randomizePositions(layoutInfo);
20025 }
20026 var startTime = performanceNow();
20027 var refresh = function refresh() {
20028 refreshPositions(layoutInfo, cy, options);
20029
20030 // Fit the graph if necessary
20031 if (true === options.fit) {
20032 cy.fit(options.padding);
20033 }
20034 };
20035 var mainLoop = function mainLoop(i) {
20036 if (layout.stopped || i >= options.numIter) {
20037 // logDebug("Layout manually stopped. Stopping computation in step " + i);
20038 return false;
20039 }
20040
20041 // Do one step in the phisical simulation
20042 step(layoutInfo, options);
20043
20044 // Update temperature
20045 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor;
20046 // logDebug("New temperature: " + layoutInfo.temperature);
20047
20048 if (layoutInfo.temperature < options.minTemp) {
20049 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
20050 return false;
20051 }
20052 return true;
20053 };
20054 var done = function done() {
20055 if (options.animate === true || options.animate === false) {
20056 refresh();
20057
20058 // Layout has finished
20059 layout.one('layoutstop', options.stop);
20060 layout.emit({
20061 type: 'layoutstop',
20062 layout: layout
20063 });
20064 } else {
20065 var nodes = options.eles.nodes();
20066 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20067 nodes.layoutPositions(layout, options, getScaledPos);
20068 }
20069 };
20070 var i = 0;
20071 var loopRet = true;
20072 if (options.animate === true) {
20073 var frame = function frame() {
20074 var f = 0;
20075 while (loopRet && f < options.refresh) {
20076 loopRet = mainLoop(i);
20077 i++;
20078 f++;
20079 }
20080 if (!loopRet) {
20081 // it's done
20082 separateComponents(layoutInfo, options);
20083 done();
20084 } else {
20085 var now = performanceNow();
20086 if (now - startTime >= options.animationThreshold) {
20087 refresh();
20088 }
20089 requestAnimationFrame(frame);
20090 }
20091 };
20092 frame();
20093 } else {
20094 while (loopRet) {
20095 loopRet = mainLoop(i);
20096 i++;
20097 }
20098 separateComponents(layoutInfo, options);
20099 done();
20100 }
20101 return this; // chaining
20102 };
20103
20104 /**
20105 * @brief : called on continuous layouts to stop them before they finish
20106 */
20107 CoseLayout.prototype.stop = function () {
20108 this.stopped = true;
20109 if (this.thread) {
20110 this.thread.stop();
20111 }
20112 this.emit('layoutstop');
20113 return this; // chaining
20114 };
20115
20116 CoseLayout.prototype.destroy = function () {
20117 if (this.thread) {
20118 this.thread.stop();
20119 }
20120 return this; // chaining
20121 };
20122
20123 /**
20124 * @brief : Creates an object which is contains all the data
20125 * used in the layout process
20126 * @arg cy : cytoscape.js object
20127 * @return : layoutInfo object initialized
20128 */
20129 var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
20130 // Shortcut
20131 var edges = options.eles.edges();
20132 var nodes = options.eles.nodes();
20133 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20134 x1: 0,
20135 y1: 0,
20136 w: cy.width(),
20137 h: cy.height()
20138 });
20139 var layoutInfo = {
20140 isCompound: cy.hasCompoundNodes(),
20141 layoutNodes: [],
20142 idToIndex: {},
20143 nodeSize: nodes.size(),
20144 graphSet: [],
20145 indexToGraph: [],
20146 layoutEdges: [],
20147 edgeSize: edges.size(),
20148 temperature: options.initialTemp,
20149 clientWidth: bb.w,
20150 clientHeight: bb.h,
20151 boundingBox: bb
20152 };
20153 var components = options.eles.components();
20154 var id2cmptId = {};
20155 for (var i = 0; i < components.length; i++) {
20156 var component = components[i];
20157 for (var j = 0; j < component.length; j++) {
20158 var node = component[j];
20159 id2cmptId[node.id()] = i;
20160 }
20161 }
20162
20163 // Iterate over all nodes, creating layout nodes
20164 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20165 var n = nodes[i];
20166 var nbb = n.layoutDimensions(options);
20167 var tempNode = {};
20168 tempNode.isLocked = n.locked();
20169 tempNode.id = n.data('id');
20170 tempNode.parentId = n.data('parent');
20171 tempNode.cmptId = id2cmptId[n.id()];
20172 tempNode.children = [];
20173 tempNode.positionX = n.position('x');
20174 tempNode.positionY = n.position('y');
20175 tempNode.offsetX = 0;
20176 tempNode.offsetY = 0;
20177 tempNode.height = nbb.w;
20178 tempNode.width = nbb.h;
20179 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
20180 tempNode.minX = tempNode.positionX - tempNode.width / 2;
20181 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
20182 tempNode.minY = tempNode.positionY - tempNode.height / 2;
20183 tempNode.padLeft = parseFloat(n.style('padding'));
20184 tempNode.padRight = parseFloat(n.style('padding'));
20185 tempNode.padTop = parseFloat(n.style('padding'));
20186 tempNode.padBottom = parseFloat(n.style('padding'));
20187
20188 // forces
20189 tempNode.nodeRepulsion = fn$6(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion;
20190
20191 // Add new node
20192 layoutInfo.layoutNodes.push(tempNode);
20193 // Add entry to id-index map
20194 layoutInfo.idToIndex[tempNode.id] = i;
20195 }
20196
20197 // Inline implementation of a queue, used for traversing the graph in BFS order
20198 var queue = [];
20199 var start = 0; // Points to the start the queue
20200 var end = -1; // Points to the end of the queue
20201
20202 var tempGraph = [];
20203
20204 // Second pass to add child information and
20205 // initialize queue for hierarchical traversal
20206 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20207 var n = layoutInfo.layoutNodes[i];
20208 var p_id = n.parentId;
20209 // Check if node n has a parent node
20210 if (null != p_id) {
20211 // Add node Id to parent's list of children
20212 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
20213 } else {
20214 // If a node doesn't have a parent, then it's in the root graph
20215 queue[++end] = n.id;
20216 tempGraph.push(n.id);
20217 }
20218 }
20219
20220 // Add root graph to graphSet
20221 layoutInfo.graphSet.push(tempGraph);
20222
20223 // Traverse the graph, level by level,
20224 while (start <= end) {
20225 // Get the node to visit and remove it from queue
20226 var node_id = queue[start++];
20227 var node_ix = layoutInfo.idToIndex[node_id];
20228 var node = layoutInfo.layoutNodes[node_ix];
20229 var children = node.children;
20230 if (children.length > 0) {
20231 // Add children nodes as a new graph to graph set
20232 layoutInfo.graphSet.push(children);
20233 // Add children to que queue to be visited
20234 for (var i = 0; i < children.length; i++) {
20235 queue[++end] = children[i];
20236 }
20237 }
20238 }
20239
20240 // Create indexToGraph map
20241 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20242 var graph = layoutInfo.graphSet[i];
20243 for (var j = 0; j < graph.length; j++) {
20244 var index = layoutInfo.idToIndex[graph[j]];
20245 layoutInfo.indexToGraph[index] = i;
20246 }
20247 }
20248
20249 // Iterate over all edges, creating Layout Edges
20250 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20251 var e = edges[i];
20252 var tempEdge = {};
20253 tempEdge.id = e.data('id');
20254 tempEdge.sourceId = e.data('source');
20255 tempEdge.targetId = e.data('target');
20256
20257 // Compute ideal length
20258 var idealLength = fn$6(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
20259 var elasticity = fn$6(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity;
20260
20261 // Check if it's an inter graph edge
20262 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
20263 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
20264 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
20265 var targetGraph = layoutInfo.indexToGraph[targetIx];
20266 if (sourceGraph != targetGraph) {
20267 // Find lowest common graph ancestor
20268 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo);
20269
20270 // Compute sum of node depths, relative to lca graph
20271 var lcaGraph = layoutInfo.graphSet[lca];
20272 var depth = 0;
20273
20274 // Source depth
20275 var tempNode = layoutInfo.layoutNodes[sourceIx];
20276 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20277 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20278 depth++;
20279 }
20280
20281 // Target depth
20282 tempNode = layoutInfo.layoutNodes[targetIx];
20283 while (-1 === lcaGraph.indexOf(tempNode.id)) {
20284 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
20285 depth++;
20286 }
20287
20288 // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20289 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20290 // ". Depth: " + depth);
20291
20292 // Update idealLength
20293 idealLength *= depth * options.nestingFactor;
20294 }
20295 tempEdge.idealLength = idealLength;
20296 tempEdge.elasticity = elasticity;
20297 layoutInfo.layoutEdges.push(tempEdge);
20298 }
20299
20300 // Finally, return layoutInfo object
20301 return layoutInfo;
20302 };
20303
20304 /**
20305 * @brief : This function finds the index of the lowest common
20306 * graph ancestor between 2 nodes in the subtree
20307 * (from the graph hierarchy induced tree) whose
20308 * root is graphIx
20309 *
20310 * @arg node1: node1's ID
20311 * @arg node2: node2's ID
20312 * @arg layoutInfo: layoutInfo object
20313 *
20314 */
20315 var findLCA = function findLCA(node1, node2, layoutInfo) {
20316 // Find their common ancester, starting from the root graph
20317 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20318 if (2 > res.count) {
20319 // If aux function couldn't find the common ancester,
20320 // then it is the root graph
20321 return 0;
20322 } else {
20323 return res.graph;
20324 }
20325 };
20326
20327 /**
20328 * @brief : Auxiliary function used for LCA computation
20329 *
20330 * @arg node1 : node1's ID
20331 * @arg node2 : node2's ID
20332 * @arg graphIx : subgraph index
20333 * @arg layoutInfo : layoutInfo object
20334 *
20335 * @return : object of the form {count: X, graph: Y}, where:
20336 * X is the number of ancestors (max: 2) found in
20337 * graphIx (and it's subgraphs),
20338 * Y is the graph index of the lowest graph containing
20339 * all X nodes
20340 */
20341 var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20342 var graph = layoutInfo.graphSet[graphIx];
20343 // If both nodes belongs to graphIx
20344 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20345 return {
20346 count: 2,
20347 graph: graphIx
20348 };
20349 }
20350
20351 // Make recursive calls for all subgraphs
20352 var c = 0;
20353 for (var i = 0; i < graph.length; i++) {
20354 var nodeId = graph[i];
20355 var nodeIx = layoutInfo.idToIndex[nodeId];
20356 var children = layoutInfo.layoutNodes[nodeIx].children;
20357
20358 // If the node has no child, skip it
20359 if (0 === children.length) {
20360 continue;
20361 }
20362 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20363 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20364 if (0 === result.count) {
20365 // Neither node1 nor node2 are present in this subgraph
20366 continue;
20367 } else if (1 === result.count) {
20368 // One of (node1, node2) is present in this subgraph
20369 c++;
20370 if (2 === c) {
20371 // We've already found both nodes, no need to keep searching
20372 break;
20373 }
20374 } else {
20375 // Both nodes are present in this subgraph
20376 return result;
20377 }
20378 }
20379 return {
20380 count: c,
20381 graph: graphIx
20382 };
20383 };
20384
20385 /**
20386 * @brief: printsLayoutInfo into js console
20387 * Only used for debbuging
20388 */
20389var printLayoutInfo;
20390
20391 /**
20392 * @brief : Randomizes the position of all nodes
20393 */
20394 var randomizePositions = function randomizePositions(layoutInfo, cy) {
20395 var width = layoutInfo.clientWidth;
20396 var height = layoutInfo.clientHeight;
20397 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20398 var n = layoutInfo.layoutNodes[i];
20399
20400 // No need to randomize compound nodes or locked nodes
20401 if (0 === n.children.length && !n.isLocked) {
20402 n.positionX = Math.random() * width;
20403 n.positionY = Math.random() * height;
20404 }
20405 }
20406 };
20407 var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20408 var bb = layoutInfo.boundingBox;
20409 var coseBB = {
20410 x1: Infinity,
20411 x2: -Infinity,
20412 y1: Infinity,
20413 y2: -Infinity
20414 };
20415 if (options.boundingBox) {
20416 nodes.forEach(function (node) {
20417 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20418 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20419 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20420 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20421 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20422 });
20423 coseBB.w = coseBB.x2 - coseBB.x1;
20424 coseBB.h = coseBB.y2 - coseBB.y1;
20425 }
20426 return function (ele, i) {
20427 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20428 if (options.boundingBox) {
20429 // then add extra bounding box constraint
20430 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20431 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20432 return {
20433 x: bb.x1 + pctX * bb.w,
20434 y: bb.y1 + pctY * bb.h
20435 };
20436 } else {
20437 return {
20438 x: lnode.positionX,
20439 y: lnode.positionY
20440 };
20441 }
20442 };
20443 };
20444
20445 /**
20446 * @brief : Updates the positions of nodes in the network
20447 * @arg layoutInfo : LayoutInfo object
20448 * @arg cy : Cytoscape object
20449 * @arg options : Layout options
20450 */
20451 var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20452 // var s = 'Refreshing positions';
20453 // logDebug(s);
20454
20455 var layout = options.layout;
20456 var nodes = options.eles.nodes();
20457 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20458 nodes.positions(getScaledPos);
20459
20460 // Trigger layoutReady only on first call
20461 if (true !== layoutInfo.ready) {
20462 // s = 'Triggering layoutready';
20463 // logDebug(s);
20464 layoutInfo.ready = true;
20465 layout.one('layoutready', options.ready);
20466 layout.emit({
20467 type: 'layoutready',
20468 layout: this
20469 });
20470 }
20471 };
20472
20473 /**
20474 * @brief : Logs a debug message in JS console, if DEBUG is ON
20475 */
20476 // var logDebug = function(text) {
20477 // if (DEBUG) {
20478 // console.debug(text);
20479 // }
20480 // };
20481
20482 /**
20483 * @brief : Performs one iteration of the physical simulation
20484 * @arg layoutInfo : LayoutInfo object already initialized
20485 * @arg cy : Cytoscape object
20486 * @arg options : Layout options
20487 */
20488 var step = function step(layoutInfo, options, _step) {
20489 // var s = "\n\n###############################";
20490 // s += "\nSTEP: " + step;
20491 // s += "\n###############################\n";
20492 // logDebug(s);
20493
20494 // Calculate node repulsions
20495 calculateNodeForces(layoutInfo, options);
20496 // Calculate edge forces
20497 calculateEdgeForces(layoutInfo);
20498 // Calculate gravity forces
20499 calculateGravityForces(layoutInfo, options);
20500 // Propagate forces from parent to child
20501 propagateForces(layoutInfo);
20502 // Update positions based on calculated forces
20503 updatePositions(layoutInfo);
20504 };
20505
20506 /**
20507 * @brief : Computes the node repulsion forces
20508 */
20509 var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20510 // Go through each of the graphs in graphSet
20511 // Nodes only repel each other if they belong to the same graph
20512 // var s = 'calculateNodeForces';
20513 // logDebug(s);
20514 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20515 var graph = layoutInfo.graphSet[i];
20516 var numNodes = graph.length;
20517
20518 // s = "Set: " + graph.toString();
20519 // logDebug(s);
20520
20521 // Now get all the pairs of nodes
20522 // Only get each pair once, (A, B) = (B, A)
20523 for (var j = 0; j < numNodes; j++) {
20524 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20525 for (var k = j + 1; k < numNodes; k++) {
20526 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20527 nodeRepulsion(node1, node2, layoutInfo, options);
20528 }
20529 }
20530 }
20531 };
20532 var randomDistance = function randomDistance(max) {
20533 return -max + 2 * max * Math.random();
20534 };
20535
20536 /**
20537 * @brief : Compute the node repulsion forces between a pair of nodes
20538 */
20539 var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20540 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20541
20542 var cmptId1 = node1.cmptId;
20543 var cmptId2 = node2.cmptId;
20544 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20545 return;
20546 }
20547
20548 // Get direction of line connecting both node centers
20549 var directionX = node2.positionX - node1.positionX;
20550 var directionY = node2.positionY - node1.positionY;
20551 var maxRandDist = 1;
20552 // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20553
20554 // If both centers are the same, apply a random force
20555 if (0 === directionX && 0 === directionY) {
20556 directionX = randomDistance(maxRandDist);
20557 directionY = randomDistance(maxRandDist);
20558 }
20559 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20560 if (overlap > 0) {
20561 // s += "\nNodes DO overlap.";
20562 // s += "\nOverlap: " + overlap;
20563 // If nodes overlap, repulsion force is proportional
20564 // to the overlap
20565 var force = options.nodeOverlap * overlap;
20566
20567 // Compute the module and components of the force vector
20568 var distance = Math.sqrt(directionX * directionX + directionY * directionY);
20569 // s += "\nDistance: " + distance;
20570 var forceX = force * directionX / distance;
20571 var forceY = force * directionY / distance;
20572 } else {
20573 // s += "\nNodes do NOT overlap.";
20574 // If there's no overlap, force is inversely proportional
20575 // to squared distance
20576
20577 // Get clipping points for both nodes
20578 var point1 = findClippingPoint(node1, directionX, directionY);
20579 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY);
20580
20581 // Use clipping points to compute distance
20582 var distanceX = point2.x - point1.x;
20583 var distanceY = point2.y - point1.y;
20584 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20585 var distance = Math.sqrt(distanceSqr);
20586 // s += "\nDistance: " + distance;
20587
20588 // Compute the module and components of the force vector
20589 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20590 var forceX = force * distanceX / distance;
20591 var forceY = force * distanceY / distance;
20592 }
20593
20594 // Apply force
20595 if (!node1.isLocked) {
20596 node1.offsetX -= forceX;
20597 node1.offsetY -= forceY;
20598 }
20599 if (!node2.isLocked) {
20600 node2.offsetX += forceX;
20601 node2.offsetY += forceY;
20602 }
20603
20604 // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20605 // logDebug(s);
20606
20607 return;
20608 };
20609
20610 /**
20611 * @brief : Determines whether two nodes overlap or not
20612 * @return : Amount of overlapping (0 => no overlap)
20613 */
20614 var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20615 if (dX > 0) {
20616 var overlapX = node1.maxX - node2.minX;
20617 } else {
20618 var overlapX = node2.maxX - node1.minX;
20619 }
20620 if (dY > 0) {
20621 var overlapY = node1.maxY - node2.minY;
20622 } else {
20623 var overlapY = node2.maxY - node1.minY;
20624 }
20625 if (overlapX >= 0 && overlapY >= 0) {
20626 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20627 } else {
20628 return 0;
20629 }
20630 };
20631
20632 /**
20633 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20634 * the rectangular bounding box of it's source/target node
20635 */
20636 var findClippingPoint = function findClippingPoint(node, dX, dY) {
20637 // Shorcuts
20638 var X = node.positionX;
20639 var Y = node.positionY;
20640 var H = node.height || 1;
20641 var W = node.width || 1;
20642 var dirSlope = dY / dX;
20643 var nodeSlope = H / W;
20644
20645 // var s = 'Computing clipping point of node ' + node.id +
20646 // " . Height: " + H + ", Width: " + W +
20647 // "\nDirection " + dX + ", " + dY;
20648 //
20649 // Compute intersection
20650 var res = {};
20651
20652 // Case: Vertical direction (up)
20653 if (0 === dX && 0 < dY) {
20654 res.x = X;
20655 // s += "\nUp direction";
20656 res.y = Y + H / 2;
20657 return res;
20658 }
20659
20660 // Case: Vertical direction (down)
20661 if (0 === dX && 0 > dY) {
20662 res.x = X;
20663 res.y = Y + H / 2;
20664 // s += "\nDown direction";
20665
20666 return res;
20667 }
20668
20669 // Case: Intersects the right border
20670 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20671 res.x = X + W / 2;
20672 res.y = Y + W * dY / 2 / dX;
20673 // s += "\nRightborder";
20674
20675 return res;
20676 }
20677
20678 // Case: Intersects the left border
20679 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20680 res.x = X - W / 2;
20681 res.y = Y - W * dY / 2 / dX;
20682 // s += "\nLeftborder";
20683
20684 return res;
20685 }
20686
20687 // Case: Intersects the top border
20688 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20689 res.x = X + H * dX / 2 / dY;
20690 res.y = Y + H / 2;
20691 // s += "\nTop border";
20692
20693 return res;
20694 }
20695
20696 // Case: Intersects the bottom border
20697 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20698 res.x = X - H * dX / 2 / dY;
20699 res.y = Y - H / 2;
20700 // s += "\nBottom border";
20701
20702 return res;
20703 }
20704
20705 // s += "\nClipping point found at " + res.x + ", " + res.y;
20706 // logDebug(s);
20707 return res;
20708 };
20709
20710 /**
20711 * @brief : Calculates all edge forces
20712 */
20713 var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20714 // Iterate over all edges
20715 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20716 // Get edge, source & target nodes
20717 var edge = layoutInfo.layoutEdges[i];
20718 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20719 var source = layoutInfo.layoutNodes[sourceIx];
20720 var targetIx = layoutInfo.idToIndex[edge.targetId];
20721 var target = layoutInfo.layoutNodes[targetIx];
20722
20723 // Get direction of line connecting both node centers
20724 var directionX = target.positionX - source.positionX;
20725 var directionY = target.positionY - source.positionY;
20726
20727 // If both centers are the same, do nothing.
20728 // A random force has already been applied as node repulsion
20729 if (0 === directionX && 0 === directionY) {
20730 continue;
20731 }
20732
20733 // Get clipping points for both nodes
20734 var point1 = findClippingPoint(source, directionX, directionY);
20735 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20736 var lx = point2.x - point1.x;
20737 var ly = point2.y - point1.y;
20738 var l = Math.sqrt(lx * lx + ly * ly);
20739 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20740 if (0 !== l) {
20741 var forceX = force * lx / l;
20742 var forceY = force * ly / l;
20743 } else {
20744 var forceX = 0;
20745 var forceY = 0;
20746 }
20747
20748 // Add this force to target and source nodes
20749 if (!source.isLocked) {
20750 source.offsetX += forceX;
20751 source.offsetY += forceY;
20752 }
20753 if (!target.isLocked) {
20754 target.offsetX -= forceX;
20755 target.offsetY -= forceY;
20756 }
20757
20758 // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20759 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20760 // logDebug(s);
20761 }
20762 };
20763
20764 /**
20765 * @brief : Computes gravity forces for all nodes
20766 */
20767 var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20768 if (options.gravity === 0) {
20769 return;
20770 }
20771 var distThreshold = 1;
20772
20773 // var s = 'calculateGravityForces';
20774 // logDebug(s);
20775 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20776 var graph = layoutInfo.graphSet[i];
20777 var numNodes = graph.length;
20778
20779 // s = "Set: " + graph.toString();
20780 // logDebug(s);
20781
20782 // Compute graph center
20783 if (0 === i) {
20784 var centerX = layoutInfo.clientHeight / 2;
20785 var centerY = layoutInfo.clientWidth / 2;
20786 } else {
20787 // Get Parent node for this graph, and use its position as center
20788 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20789 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20790 var centerX = parent.positionX;
20791 var centerY = parent.positionY;
20792 }
20793 // s = "Center found at: " + centerX + ", " + centerY;
20794 // logDebug(s);
20795
20796 // Apply force to all nodes in graph
20797 for (var j = 0; j < numNodes; j++) {
20798 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20799 // s = "Node: " + node.id;
20800
20801 if (node.isLocked) {
20802 continue;
20803 }
20804 var dx = centerX - node.positionX;
20805 var dy = centerY - node.positionY;
20806 var d = Math.sqrt(dx * dx + dy * dy);
20807 if (d > distThreshold) {
20808 var fx = options.gravity * dx / d;
20809 var fy = options.gravity * dy / d;
20810 node.offsetX += fx;
20811 node.offsetY += fy;
20812 // s += ": Applied force: " + fx + ", " + fy;
20813 }
20814 // logDebug(s);
20815 }
20816 }
20817 };
20818
20819 /**
20820 * @brief : This function propagates the existing offsets from
20821 * parent nodes to its descendents.
20822 * @arg layoutInfo : layoutInfo Object
20823 * @arg cy : cytoscape Object
20824 * @arg options : Layout options
20825 */
20826 var propagateForces = function propagateForces(layoutInfo, options) {
20827 // Inline implementation of a queue, used for traversing the graph in BFS order
20828 var queue = [];
20829 var start = 0; // Points to the start the queue
20830 var end = -1; // Points to the end of the queue
20831
20832 // logDebug('propagateForces');
20833
20834 // Start by visiting the nodes in the root graph
20835 queue.push.apply(queue, layoutInfo.graphSet[0]);
20836 end += layoutInfo.graphSet[0].length;
20837
20838 // Traverse the graph, level by level,
20839 while (start <= end) {
20840 // Get the node to visit and remove it from queue
20841 var nodeId = queue[start++];
20842 var nodeIndex = layoutInfo.idToIndex[nodeId];
20843 var node = layoutInfo.layoutNodes[nodeIndex];
20844 var children = node.children;
20845
20846 // We only need to process the node if it's compound
20847 if (0 < children.length && !node.isLocked) {
20848 var offX = node.offsetX;
20849 var offY = node.offsetY;
20850
20851 // var s = "Propagating offset from parent node : " + node.id +
20852 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20853 // s += "\n Children: " + children.toString();
20854 // logDebug(s);
20855
20856 for (var i = 0; i < children.length; i++) {
20857 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]];
20858 // Propagate offset
20859 childNode.offsetX += offX;
20860 childNode.offsetY += offY;
20861 // Add children to queue to be visited
20862 queue[++end] = children[i];
20863 }
20864
20865 // Reset parent offsets
20866 node.offsetX = 0;
20867 node.offsetY = 0;
20868 }
20869 }
20870 };
20871
20872 /**
20873 * @brief : Updates the layout model positions, based on
20874 * the accumulated forces
20875 */
20876 var updatePositions = function updatePositions(layoutInfo, options) {
20877 // var s = 'Updating positions';
20878 // logDebug(s);
20879
20880 // Reset boundaries for compound nodes
20881 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20882 var n = layoutInfo.layoutNodes[i];
20883 if (0 < n.children.length) {
20884 // logDebug("Resetting boundaries of compound node: " + n.id);
20885 n.maxX = undefined;
20886 n.minX = undefined;
20887 n.maxY = undefined;
20888 n.minY = undefined;
20889 }
20890 }
20891 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20892 var n = layoutInfo.layoutNodes[i];
20893 if (0 < n.children.length || n.isLocked) {
20894 // No need to set compound or locked node position
20895 // logDebug("Skipping position update of node: " + n.id);
20896 continue;
20897 }
20898 // s = "Node: " + n.id + " Previous position: (" +
20899 // n.positionX + ", " + n.positionY + ").";
20900
20901 // Limit displacement in order to improve stability
20902 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20903 n.positionX += tempForce.x;
20904 n.positionY += tempForce.y;
20905 n.offsetX = 0;
20906 n.offsetY = 0;
20907 n.minX = n.positionX - n.width;
20908 n.maxX = n.positionX + n.width;
20909 n.minY = n.positionY - n.height;
20910 n.maxY = n.positionY + n.height;
20911 // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20912 // logDebug(s);
20913
20914 // Update ancestry boudaries
20915 updateAncestryBoundaries(n, layoutInfo);
20916 }
20917
20918 // Update size, position of compund nodes
20919 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20920 var n = layoutInfo.layoutNodes[i];
20921 if (0 < n.children.length && !n.isLocked) {
20922 n.positionX = (n.maxX + n.minX) / 2;
20923 n.positionY = (n.maxY + n.minY) / 2;
20924 n.width = n.maxX - n.minX;
20925 n.height = n.maxY - n.minY;
20926 // s = "Updating position, size of compound node " + n.id;
20927 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20928 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20929 // logDebug(s);
20930 }
20931 }
20932 };
20933
20934 /**
20935 * @brief : Limits a force (forceX, forceY) to be not
20936 * greater (in modulo) than max.
20937 8 Preserves force direction.
20938 */
20939 var limitForce = function limitForce(forceX, forceY, max) {
20940 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20941 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20942 if (force > max) {
20943 var res = {
20944 x: max * forceX / force,
20945 y: max * forceY / force
20946 };
20947 } else {
20948 var res = {
20949 x: forceX,
20950 y: forceY
20951 };
20952 }
20953
20954 // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20955 // logDebug(s);
20956
20957 return res;
20958 };
20959
20960 /**
20961 * @brief : Function used for keeping track of compound node
20962 * sizes, since they should bound all their subnodes.
20963 */
20964 var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20965 // var s = "Propagating new position/size of node " + node.id;
20966 var parentId = node.parentId;
20967 if (null == parentId) {
20968 // If there's no parent, we are done
20969 // s += ". No parent node.";
20970 // logDebug(s);
20971 return;
20972 }
20973
20974 // Get Parent Node
20975 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20976 var flag = false;
20977
20978 // MaxX
20979 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20980 p.maxX = node.maxX + p.padRight;
20981 flag = true;
20982 // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20983 }
20984
20985 // MinX
20986 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20987 p.minX = node.minX - p.padLeft;
20988 flag = true;
20989 // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20990 }
20991
20992 // MaxY
20993 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20994 p.maxY = node.maxY + p.padBottom;
20995 flag = true;
20996 // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20997 }
20998
20999 // MinY
21000 if (null == p.minY || node.minY - p.padTop < p.minY) {
21001 p.minY = node.minY - p.padTop;
21002 flag = true;
21003 // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
21004 }
21005
21006 // If updated boundaries, propagate changes upward
21007 if (flag) {
21008 // logDebug(s);
21009 return updateAncestryBoundaries(p, layoutInfo);
21010 }
21011
21012 // s += ". No changes in boundaries/position of parent node " + p.id;
21013 // logDebug(s);
21014 return;
21015 };
21016 var separateComponents = function separateComponents(layoutInfo, options) {
21017 var nodes = layoutInfo.layoutNodes;
21018 var components = [];
21019 for (var i = 0; i < nodes.length; i++) {
21020 var node = nodes[i];
21021 var cid = node.cmptId;
21022 var component = components[cid] = components[cid] || [];
21023 component.push(node);
21024 }
21025 var totalA = 0;
21026 for (var i = 0; i < components.length; i++) {
21027 var c = components[i];
21028 if (!c) {
21029 continue;
21030 }
21031 c.x1 = Infinity;
21032 c.x2 = -Infinity;
21033 c.y1 = Infinity;
21034 c.y2 = -Infinity;
21035 for (var j = 0; j < c.length; j++) {
21036 var n = c[j];
21037 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
21038 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
21039 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
21040 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
21041 }
21042 c.w = c.x2 - c.x1;
21043 c.h = c.y2 - c.y1;
21044 totalA += c.w * c.h;
21045 }
21046 components.sort(function (c1, c2) {
21047 return c2.w * c2.h - c1.w * c1.h;
21048 });
21049 var x = 0;
21050 var y = 0;
21051 var usedW = 0;
21052 var rowH = 0;
21053 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
21054 for (var i = 0; i < components.length; i++) {
21055 var c = components[i];
21056 if (!c) {
21057 continue;
21058 }
21059 for (var j = 0; j < c.length; j++) {
21060 var n = c[j];
21061 if (!n.isLocked) {
21062 n.positionX += x - c.x1;
21063 n.positionY += y - c.y1;
21064 }
21065 }
21066 x += c.w + options.componentSpacing;
21067 usedW += c.w + options.componentSpacing;
21068 rowH = Math.max(rowH, c.h);
21069 if (usedW > maxRowW) {
21070 y += rowH + options.componentSpacing;
21071 x = 0;
21072 usedW = 0;
21073 rowH = 0;
21074 }
21075 }
21076 };
21077
21078 var defaults$3 = {
21079 fit: true,
21080 // whether to fit the viewport to the graph
21081 padding: 30,
21082 // padding used on fit
21083 boundingBox: undefined,
21084 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21085 avoidOverlap: true,
21086 // prevents node overlap, may overflow boundingBox if not enough space
21087 avoidOverlapPadding: 10,
21088 // extra spacing around nodes when avoidOverlap: true
21089 nodeDimensionsIncludeLabels: false,
21090 // Excludes the label when calculating node bounding boxes for the layout algorithm
21091 spacingFactor: undefined,
21092 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21093 condense: false,
21094 // uses all available space on false, uses minimal space on true
21095 rows: undefined,
21096 // force num of rows in the grid
21097 cols: undefined,
21098 // force num of columns in the grid
21099 position: function position(node) {},
21100 // returns { row, col } for element
21101 sort: undefined,
21102 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
21103 animate: false,
21104 // whether to transition the node positions
21105 animationDuration: 500,
21106 // duration of animation in ms if enabled
21107 animationEasing: undefined,
21108 // easing of animation if enabled
21109 animateFilter: function animateFilter(node, i) {
21110 return true;
21111 },
21112 // 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
21113 ready: undefined,
21114 // callback on layoutready
21115 stop: undefined,
21116 // callback on layoutstop
21117 transform: function transform(node, position) {
21118 return position;
21119 } // transform a given node position. Useful for changing flow direction in discrete layouts
21120 };
21121
21122 function GridLayout(options) {
21123 this.options = extend({}, defaults$3, options);
21124 }
21125 GridLayout.prototype.run = function () {
21126 var params = this.options;
21127 var options = params;
21128 var cy = params.cy;
21129 var eles = options.eles;
21130 var nodes = eles.nodes().not(':parent');
21131 if (options.sort) {
21132 nodes = nodes.sort(options.sort);
21133 }
21134 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21135 x1: 0,
21136 y1: 0,
21137 w: cy.width(),
21138 h: cy.height()
21139 });
21140 if (bb.h === 0 || bb.w === 0) {
21141 eles.nodes().layoutPositions(this, options, function (ele) {
21142 return {
21143 x: bb.x1,
21144 y: bb.y1
21145 };
21146 });
21147 } else {
21148 // width/height * splits^2 = cells where splits is number of times to split width
21149 var cells = nodes.size();
21150 var splits = Math.sqrt(cells * bb.h / bb.w);
21151 var rows = Math.round(splits);
21152 var cols = Math.round(bb.w / bb.h * splits);
21153 var small = function small(val) {
21154 if (val == null) {
21155 return Math.min(rows, cols);
21156 } else {
21157 var min = Math.min(rows, cols);
21158 if (min == rows) {
21159 rows = val;
21160 } else {
21161 cols = val;
21162 }
21163 }
21164 };
21165 var large = function large(val) {
21166 if (val == null) {
21167 return Math.max(rows, cols);
21168 } else {
21169 var max = Math.max(rows, cols);
21170 if (max == rows) {
21171 rows = val;
21172 } else {
21173 cols = val;
21174 }
21175 }
21176 };
21177 var oRows = options.rows;
21178 var oCols = options.cols != null ? options.cols : options.columns;
21179
21180 // if rows or columns were set in options, use those values
21181 if (oRows != null && oCols != null) {
21182 rows = oRows;
21183 cols = oCols;
21184 } else if (oRows != null && oCols == null) {
21185 rows = oRows;
21186 cols = Math.ceil(cells / rows);
21187 } else if (oRows == null && oCols != null) {
21188 cols = oCols;
21189 rows = Math.ceil(cells / cols);
21190 }
21191
21192 // otherwise use the automatic values and adjust accordingly
21193
21194 // if rounding was up, see if we can reduce rows or columns
21195 else if (cols * rows > cells) {
21196 var sm = small();
21197 var lg = large();
21198
21199 // reducing the small side takes away the most cells, so try it first
21200 if ((sm - 1) * lg >= cells) {
21201 small(sm - 1);
21202 } else if ((lg - 1) * sm >= cells) {
21203 large(lg - 1);
21204 }
21205 } else {
21206 // if rounding was too low, add rows or columns
21207 while (cols * rows < cells) {
21208 var _sm = small();
21209 var _lg = large();
21210
21211 // try to add to larger side first (adds less in multiplication)
21212 if ((_lg + 1) * _sm >= cells) {
21213 large(_lg + 1);
21214 } else {
21215 small(_sm + 1);
21216 }
21217 }
21218 }
21219 var cellWidth = bb.w / cols;
21220 var cellHeight = bb.h / rows;
21221 if (options.condense) {
21222 cellWidth = 0;
21223 cellHeight = 0;
21224 }
21225 if (options.avoidOverlap) {
21226 for (var i = 0; i < nodes.length; i++) {
21227 var node = nodes[i];
21228 var pos = node._private.position;
21229 if (pos.x == null || pos.y == null) {
21230 // for bb
21231 pos.x = 0;
21232 pos.y = 0;
21233 }
21234 var nbb = node.layoutDimensions(options);
21235 var p = options.avoidOverlapPadding;
21236 var w = nbb.w + p;
21237 var h = nbb.h + p;
21238 cellWidth = Math.max(cellWidth, w);
21239 cellHeight = Math.max(cellHeight, h);
21240 }
21241 }
21242 var cellUsed = {}; // e.g. 'c-0-2' => true
21243
21244 var used = function used(row, col) {
21245 return cellUsed['c-' + row + '-' + col] ? true : false;
21246 };
21247 var use = function use(row, col) {
21248 cellUsed['c-' + row + '-' + col] = true;
21249 };
21250
21251 // to keep track of current cell position
21252 var row = 0;
21253 var col = 0;
21254 var moveToNextCell = function moveToNextCell() {
21255 col++;
21256 if (col >= cols) {
21257 col = 0;
21258 row++;
21259 }
21260 };
21261
21262 // get a cache of all the manual positions
21263 var id2manPos = {};
21264 for (var _i = 0; _i < nodes.length; _i++) {
21265 var _node = nodes[_i];
21266 var rcPos = options.position(_node);
21267 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21268 // must have at least row or col def'd
21269 var _pos = {
21270 row: rcPos.row,
21271 col: rcPos.col
21272 };
21273 if (_pos.col === undefined) {
21274 // find unused col
21275 _pos.col = 0;
21276 while (used(_pos.row, _pos.col)) {
21277 _pos.col++;
21278 }
21279 } else if (_pos.row === undefined) {
21280 // find unused row
21281 _pos.row = 0;
21282 while (used(_pos.row, _pos.col)) {
21283 _pos.row++;
21284 }
21285 }
21286 id2manPos[_node.id()] = _pos;
21287 use(_pos.row, _pos.col);
21288 }
21289 }
21290 var getPos = function getPos(element, i) {
21291 var x, y;
21292 if (element.locked() || element.isParent()) {
21293 return false;
21294 }
21295
21296 // see if we have a manual position set
21297 var rcPos = id2manPos[element.id()];
21298 if (rcPos) {
21299 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21300 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21301 } else {
21302 // otherwise set automatically
21303
21304 while (used(row, col)) {
21305 moveToNextCell();
21306 }
21307 x = col * cellWidth + cellWidth / 2 + bb.x1;
21308 y = row * cellHeight + cellHeight / 2 + bb.y1;
21309 use(row, col);
21310 moveToNextCell();
21311 }
21312 return {
21313 x: x,
21314 y: y
21315 };
21316 };
21317 nodes.layoutPositions(this, options, getPos);
21318 }
21319 return this; // chaining
21320 };
21321
21322 // default layout options
21323 var defaults$2 = {
21324 ready: function ready() {},
21325 // on layoutready
21326 stop: function stop() {} // on layoutstop
21327 };
21328
21329 // constructor
21330 // options : object containing layout options
21331 function NullLayout(options) {
21332 this.options = extend({}, defaults$2, options);
21333 }
21334
21335 // runs the layout
21336 NullLayout.prototype.run = function () {
21337 var options = this.options;
21338 var eles = options.eles; // elements to consider in the layout
21339 var layout = this;
21340
21341 // cy is automatically populated for us in the constructor
21342 // (disable eslint for next line as this serves as example layout code to external developers)
21343 // eslint-disable-next-line no-unused-vars
21344 options.cy;
21345 layout.emit('layoutstart');
21346
21347 // puts all nodes at (0, 0)
21348 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21349 eles.nodes().positions(function () {
21350 return {
21351 x: 0,
21352 y: 0
21353 };
21354 });
21355
21356 // trigger layoutready when each node has had its position set at least once
21357 layout.one('layoutready', options.ready);
21358 layout.emit('layoutready');
21359
21360 // trigger layoutstop when the layout stops (e.g. finishes)
21361 layout.one('layoutstop', options.stop);
21362 layout.emit('layoutstop');
21363 return this; // chaining
21364 };
21365
21366 // called on continuous layouts to stop them before they finish
21367 NullLayout.prototype.stop = function () {
21368 return this; // chaining
21369 };
21370
21371 var defaults$1 = {
21372 positions: undefined,
21373 // map of (node id) => (position obj); or function(node){ return somPos; }
21374 zoom: undefined,
21375 // the zoom level to set (prob want fit = false if set)
21376 pan: undefined,
21377 // the pan level to set (prob want fit = false if set)
21378 fit: true,
21379 // whether to fit to viewport
21380 padding: 30,
21381 // padding on fit
21382 spacingFactor: undefined,
21383 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
21384 animate: false,
21385 // whether to transition the node positions
21386 animationDuration: 500,
21387 // duration of animation in ms if enabled
21388 animationEasing: undefined,
21389 // easing of animation if enabled
21390 animateFilter: function animateFilter(node, i) {
21391 return true;
21392 },
21393 // 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
21394 ready: undefined,
21395 // callback on layoutready
21396 stop: undefined,
21397 // callback on layoutstop
21398 transform: function transform(node, position) {
21399 return position;
21400 } // transform a given node position. Useful for changing flow direction in discrete layouts
21401 };
21402
21403 function PresetLayout(options) {
21404 this.options = extend({}, defaults$1, options);
21405 }
21406 PresetLayout.prototype.run = function () {
21407 var options = this.options;
21408 var eles = options.eles;
21409 var nodes = eles.nodes();
21410 var posIsFn = fn$6(options.positions);
21411 function getPosition(node) {
21412 if (options.positions == null) {
21413 return copyPosition(node.position());
21414 }
21415 if (posIsFn) {
21416 return options.positions(node);
21417 }
21418 var pos = options.positions[node._private.data.id];
21419 if (pos == null) {
21420 return null;
21421 }
21422 return pos;
21423 }
21424 nodes.layoutPositions(this, options, function (node, i) {
21425 var position = getPosition(node);
21426 if (node.locked() || position == null) {
21427 return false;
21428 }
21429 return position;
21430 });
21431 return this; // chaining
21432 };
21433
21434 var defaults = {
21435 fit: true,
21436 // whether to fit to viewport
21437 padding: 30,
21438 // fit padding
21439 boundingBox: undefined,
21440 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21441 animate: false,
21442 // whether to transition the node positions
21443 animationDuration: 500,
21444 // duration of animation in ms if enabled
21445 animationEasing: undefined,
21446 // easing of animation if enabled
21447 animateFilter: function animateFilter(node, i) {
21448 return true;
21449 },
21450 // 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
21451 ready: undefined,
21452 // callback on layoutready
21453 stop: undefined,
21454 // callback on layoutstop
21455 transform: function transform(node, position) {
21456 return position;
21457 } // transform a given node position. Useful for changing flow direction in discrete layouts
21458 };
21459
21460 function RandomLayout(options) {
21461 this.options = extend({}, defaults, options);
21462 }
21463 RandomLayout.prototype.run = function () {
21464 var options = this.options;
21465 var cy = options.cy;
21466 var eles = options.eles;
21467 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21468 x1: 0,
21469 y1: 0,
21470 w: cy.width(),
21471 h: cy.height()
21472 });
21473 var getPos = function getPos(node, i) {
21474 return {
21475 x: bb.x1 + Math.round(Math.random() * bb.w),
21476 y: bb.y1 + Math.round(Math.random() * bb.h)
21477 };
21478 };
21479 eles.nodes().layoutPositions(this, options, getPos);
21480 return this; // chaining
21481 };
21482
21483 var layout = [{
21484 name: 'breadthfirst',
21485 impl: BreadthFirstLayout
21486 }, {
21487 name: 'circle',
21488 impl: CircleLayout
21489 }, {
21490 name: 'concentric',
21491 impl: ConcentricLayout
21492 }, {
21493 name: 'cose',
21494 impl: CoseLayout
21495 }, {
21496 name: 'grid',
21497 impl: GridLayout
21498 }, {
21499 name: 'null',
21500 impl: NullLayout
21501 }, {
21502 name: 'preset',
21503 impl: PresetLayout
21504 }, {
21505 name: 'random',
21506 impl: RandomLayout
21507 }];
21508
21509 function NullRenderer(options) {
21510 this.options = options;
21511 this.notifications = 0; // for testing
21512 }
21513
21514 var noop = function noop() {};
21515 var throwImgErr = function throwImgErr() {
21516 throw new Error('A headless instance can not render images');
21517 };
21518 NullRenderer.prototype = {
21519 recalculateRenderedStyle: noop,
21520 notify: function notify() {
21521 this.notifications++;
21522 },
21523 init: noop,
21524 isHeadless: function isHeadless() {
21525 return true;
21526 },
21527 png: throwImgErr,
21528 jpg: throwImgErr
21529 };
21530
21531 var BRp$f = {};
21532 BRp$f.arrowShapeWidth = 0.3;
21533 BRp$f.registerArrowShapes = function () {
21534 var arrowShapes = this.arrowShapes = {};
21535 var renderer = this;
21536
21537 // Contract for arrow shapes:
21538 // 0, 0 is arrow tip
21539 // (0, 1) is direction towards node
21540 // (1, 0) is right
21541 //
21542 // functional api:
21543 // collide: check x, y in shape
21544 // roughCollide: called before collide, no false negatives
21545 // draw: draw
21546 // spacing: dist(arrowTip, nodeBoundary)
21547 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21548
21549 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21550 var x1 = translation.x - size / 2 - padding;
21551 var x2 = translation.x + size / 2 + padding;
21552 var y1 = translation.y - size / 2 - padding;
21553 var y2 = translation.y + size / 2 + padding;
21554 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21555 return inside;
21556 };
21557 var transform = function transform(x, y, size, angle, translation) {
21558 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21559 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21560 var xScaled = xRotated * size;
21561 var yScaled = yRotated * size;
21562 var xTranslated = xScaled + translation.x;
21563 var yTranslated = yScaled + translation.y;
21564 return {
21565 x: xTranslated,
21566 y: yTranslated
21567 };
21568 };
21569 var transformPoints = function transformPoints(pts, size, angle, translation) {
21570 var retPts = [];
21571 for (var i = 0; i < pts.length; i += 2) {
21572 var x = pts[i];
21573 var y = pts[i + 1];
21574 retPts.push(transform(x, y, size, angle, translation));
21575 }
21576 return retPts;
21577 };
21578 var pointsToArr = function pointsToArr(pts) {
21579 var ret = [];
21580 for (var i = 0; i < pts.length; i++) {
21581 var p = pts[i];
21582 ret.push(p.x, p.y);
21583 }
21584 return ret;
21585 };
21586 var standardGap = function standardGap(edge) {
21587 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21588 };
21589 var defineArrowShape = function defineArrowShape(name, defn) {
21590 if (string(defn)) {
21591 defn = arrowShapes[defn];
21592 }
21593 arrowShapes[name] = extend({
21594 name: name,
21595 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21596 collide: function collide(x, y, size, angle, translation, padding) {
21597 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21598 var inside = pointInsidePolygonPoints(x, y, points);
21599 return inside;
21600 },
21601 roughCollide: bbCollide,
21602 draw: function draw(context, size, angle, translation) {
21603 var points = transformPoints(this.points, size, angle, translation);
21604 renderer.arrowShapeImpl('polygon')(context, points);
21605 },
21606 spacing: function spacing(edge) {
21607 return 0;
21608 },
21609 gap: standardGap
21610 }, defn);
21611 };
21612 defineArrowShape('none', {
21613 collide: falsify,
21614 roughCollide: falsify,
21615 draw: noop$1,
21616 spacing: zeroify,
21617 gap: zeroify
21618 });
21619 defineArrowShape('triangle', {
21620 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21621 });
21622 defineArrowShape('arrow', 'triangle');
21623 defineArrowShape('triangle-backcurve', {
21624 points: arrowShapes['triangle'].points,
21625 controlPoint: [0, -0.15],
21626 roughCollide: bbCollide,
21627 draw: function draw(context, size, angle, translation, edgeWidth) {
21628 var ptsTrans = transformPoints(this.points, size, angle, translation);
21629 var ctrlPt = this.controlPoint;
21630 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21631 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21632 },
21633 gap: function gap(edge) {
21634 return standardGap(edge) * 0.8;
21635 }
21636 });
21637 defineArrowShape('triangle-tee', {
21638 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21639 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21640 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21641 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21642 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21643 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21644 return inside;
21645 },
21646 draw: function draw(context, size, angle, translation, edgeWidth) {
21647 var triPts = transformPoints(this.points, size, angle, translation);
21648 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21649 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21650 }
21651 });
21652 defineArrowShape('circle-triangle', {
21653 radius: 0.15,
21654 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21655 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21656 var t = translation;
21657 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21658 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21659 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21660 },
21661 draw: function draw(context, size, angle, translation, edgeWidth) {
21662 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21663 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21664 },
21665 spacing: function spacing(edge) {
21666 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21667 }
21668 });
21669 defineArrowShape('triangle-cross', {
21670 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21671 baseCrossLinePts: [-0.15, -0.4,
21672 // first half of the rectangle
21673 -0.15, -0.4, 0.15, -0.4,
21674 // second half of the rectangle
21675 0.15, -0.4],
21676 crossLinePts: function crossLinePts(size, edgeWidth) {
21677 // shift points so that the distance between the cross points matches edge width
21678 var p = this.baseCrossLinePts.slice();
21679 var shiftFactor = edgeWidth / size;
21680 var y0 = 3;
21681 var y1 = 5;
21682 p[y0] = p[y0] - shiftFactor;
21683 p[y1] = p[y1] - shiftFactor;
21684 return p;
21685 },
21686 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21687 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21688 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21689 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21690 return inside;
21691 },
21692 draw: function draw(context, size, angle, translation, edgeWidth) {
21693 var triPts = transformPoints(this.points, size, angle, translation);
21694 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21695 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21696 }
21697 });
21698 defineArrowShape('vee', {
21699 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21700 gap: function gap(edge) {
21701 return standardGap(edge) * 0.525;
21702 }
21703 });
21704 defineArrowShape('circle', {
21705 radius: 0.15,
21706 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21707 var t = translation;
21708 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21709 return inside;
21710 },
21711 draw: function draw(context, size, angle, translation, edgeWidth) {
21712 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21713 },
21714 spacing: function spacing(edge) {
21715 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21716 }
21717 });
21718 defineArrowShape('tee', {
21719 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21720 spacing: function spacing(edge) {
21721 return 1;
21722 },
21723 gap: function gap(edge) {
21724 return 1;
21725 }
21726 });
21727 defineArrowShape('square', {
21728 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21729 });
21730 defineArrowShape('diamond', {
21731 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21732 gap: function gap(edge) {
21733 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21734 }
21735 });
21736 defineArrowShape('chevron', {
21737 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21738 gap: function gap(edge) {
21739 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21740 }
21741 });
21742 };
21743
21744 var BRp$e = {};
21745
21746 // Project mouse
21747 BRp$e.projectIntoViewport = function (clientX, clientY) {
21748 var cy = this.cy;
21749 var offsets = this.findContainerClientCoords();
21750 var offsetLeft = offsets[0];
21751 var offsetTop = offsets[1];
21752 var scale = offsets[4];
21753 var pan = cy.pan();
21754 var zoom = cy.zoom();
21755 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21756 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21757 return [x, y];
21758 };
21759 BRp$e.findContainerClientCoords = function () {
21760 if (this.containerBB) {
21761 return this.containerBB;
21762 }
21763 var container = this.container;
21764 var rect = container.getBoundingClientRect();
21765 var style = this.cy.window().getComputedStyle(container);
21766 var styleValue = function styleValue(name) {
21767 return parseFloat(style.getPropertyValue(name));
21768 };
21769 var padding = {
21770 left: styleValue('padding-left'),
21771 right: styleValue('padding-right'),
21772 top: styleValue('padding-top'),
21773 bottom: styleValue('padding-bottom')
21774 };
21775 var border = {
21776 left: styleValue('border-left-width'),
21777 right: styleValue('border-right-width'),
21778 top: styleValue('border-top-width'),
21779 bottom: styleValue('border-bottom-width')
21780 };
21781 var clientWidth = container.clientWidth;
21782 var clientHeight = container.clientHeight;
21783 var paddingHor = padding.left + padding.right;
21784 var paddingVer = padding.top + padding.bottom;
21785 var borderHor = border.left + border.right;
21786 var scale = rect.width / (clientWidth + borderHor);
21787 var unscaledW = clientWidth - paddingHor;
21788 var unscaledH = clientHeight - paddingVer;
21789 var left = rect.left + padding.left + border.left;
21790 var top = rect.top + padding.top + border.top;
21791 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21792 };
21793 BRp$e.invalidateContainerClientCoordsCache = function () {
21794 this.containerBB = null;
21795 };
21796 BRp$e.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21797 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21798 };
21799 BRp$e.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21800 var self = this;
21801 var r = this;
21802 var eles = r.getCachedZSortedEles();
21803 var near = []; // 1 node max, 1 edge max
21804 var zoom = r.cy.zoom();
21805 var hasCompounds = r.cy.hasCompoundNodes();
21806 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21807 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21808 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21809 var minSqDist = Infinity;
21810 var nearEdge;
21811 var nearNode;
21812 if (interactiveElementsOnly) {
21813 eles = eles.interactive;
21814 }
21815 function addEle(ele, sqDist) {
21816 if (ele.isNode()) {
21817 if (nearNode) {
21818 return; // can't replace node
21819 } else {
21820 nearNode = ele;
21821 near.push(ele);
21822 }
21823 }
21824 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21825 if (nearEdge) {
21826 // then replace existing edge
21827 // can replace only if same z-index
21828 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) {
21829 for (var i = 0; i < near.length; i++) {
21830 if (near[i].isEdge()) {
21831 near[i] = ele;
21832 nearEdge = ele;
21833 minSqDist = sqDist != null ? sqDist : minSqDist;
21834 break;
21835 }
21836 }
21837 }
21838 } else {
21839 near.push(ele);
21840 nearEdge = ele;
21841 minSqDist = sqDist != null ? sqDist : minSqDist;
21842 }
21843 }
21844 }
21845 function checkNode(node) {
21846 var width = node.outerWidth() + 2 * nodeThreshold;
21847 var height = node.outerHeight() + 2 * nodeThreshold;
21848 var hw = width / 2;
21849 var hh = height / 2;
21850 var pos = node.position();
21851 var cornerRadius = node.pstyle('corner-radius').value === 'auto' ? 'auto' : node.pstyle('corner-radius').pfValue;
21852 var rs = node._private.rscratch;
21853 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21854 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21855 ) {
21856 var shape = r.nodeShapes[self.getNodeShape(node)];
21857 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y, cornerRadius, rs)) {
21858 addEle(node, 0);
21859 return true;
21860 }
21861 }
21862 }
21863 function checkEdge(edge) {
21864 var _p = edge._private;
21865 var rs = _p.rscratch;
21866 var styleWidth = edge.pstyle('width').pfValue;
21867 var scale = edge.pstyle('arrow-scale').value;
21868 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21869 var widthSq = width * width;
21870 var width2 = width * 2;
21871 var src = _p.source;
21872 var tgt = _p.target;
21873 var sqDist;
21874 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21875 var pts = rs.allpts;
21876 for (var i = 0; i + 3 < pts.length; i += 2) {
21877 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]))) {
21878 addEle(edge, sqDist);
21879 return true;
21880 }
21881 }
21882 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21883 var pts = rs.allpts;
21884 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21885 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]))) {
21886 addEle(edge, sqDist);
21887 return true;
21888 }
21889 }
21890 }
21891
21892 // if we're close to the edge but didn't hit it, maybe we hit its arrows
21893
21894 var src = src || _p.source;
21895 var tgt = tgt || _p.target;
21896 var arSize = self.getArrowWidth(styleWidth, scale);
21897 var arrows = [{
21898 name: 'source',
21899 x: rs.arrowStartX,
21900 y: rs.arrowStartY,
21901 angle: rs.srcArrowAngle
21902 }, {
21903 name: 'target',
21904 x: rs.arrowEndX,
21905 y: rs.arrowEndY,
21906 angle: rs.tgtArrowAngle
21907 }, {
21908 name: 'mid-source',
21909 x: rs.midX,
21910 y: rs.midY,
21911 angle: rs.midsrcArrowAngle
21912 }, {
21913 name: 'mid-target',
21914 x: rs.midX,
21915 y: rs.midY,
21916 angle: rs.midtgtArrowAngle
21917 }];
21918 for (var i = 0; i < arrows.length; i++) {
21919 var ar = arrows[i];
21920 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21921 var edgeWidth = edge.pstyle('width').pfValue;
21922 if (shape.roughCollide(x, y, arSize, ar.angle, {
21923 x: ar.x,
21924 y: ar.y
21925 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21926 x: ar.x,
21927 y: ar.y
21928 }, edgeWidth, edgeThreshold)) {
21929 addEle(edge);
21930 return true;
21931 }
21932 }
21933
21934 // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21935 if (hasCompounds && near.length > 0) {
21936 checkNode(src);
21937 checkNode(tgt);
21938 }
21939 }
21940 function preprop(obj, name, pre) {
21941 return getPrefixedProperty(obj, name, pre);
21942 }
21943 function checkLabel(ele, prefix) {
21944 var _p = ele._private;
21945 var th = labelThreshold;
21946 var prefixDash;
21947 if (prefix) {
21948 prefixDash = prefix + '-';
21949 } else {
21950 prefixDash = '';
21951 }
21952 ele.boundingBox();
21953 var bb = _p.labelBounds[prefix || 'main'];
21954 var text = ele.pstyle(prefixDash + 'label').value;
21955 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21956 if (!eventsEnabled || !text) {
21957 return;
21958 }
21959 var lx = preprop(_p.rscratch, 'labelX', prefix);
21960 var ly = preprop(_p.rscratch, 'labelY', prefix);
21961 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21962 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21963 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21964 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21965 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21966 var ly1 = bb.y1 - th - oy;
21967 var ly2 = bb.y2 + th - oy;
21968 if (theta) {
21969 var cos = Math.cos(theta);
21970 var sin = Math.sin(theta);
21971 var rotate = function rotate(x, y) {
21972 x = x - lx;
21973 y = y - ly;
21974 return {
21975 x: x * cos - y * sin + lx,
21976 y: x * sin + y * cos + ly
21977 };
21978 };
21979 var px1y1 = rotate(lx1, ly1);
21980 var px1y2 = rotate(lx1, ly2);
21981 var px2y1 = rotate(lx2, ly1);
21982 var px2y2 = rotate(lx2, ly2);
21983 var points = [
21984 // with the margin added after the rotation is applied
21985 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21986 if (pointInsidePolygonPoints(x, y, points)) {
21987 addEle(ele);
21988 return true;
21989 }
21990 } else {
21991 // do a cheaper bb check
21992 if (inBoundingBox(bb, x, y)) {
21993 addEle(ele);
21994 return true;
21995 }
21996 }
21997 }
21998 for (var i = eles.length - 1; i >= 0; i--) {
21999 // reverse order for precedence
22000 var ele = eles[i];
22001 if (ele.isNode()) {
22002 checkNode(ele) || checkLabel(ele);
22003 } else {
22004 // then edge
22005 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
22006 }
22007 }
22008 return near;
22009 };
22010
22011 // 'Give me everything from this box'
22012 BRp$e.getAllInBox = function (x1, y1, x2, y2) {
22013 var eles = this.getCachedZSortedEles().interactive;
22014 var box = [];
22015 var x1c = Math.min(x1, x2);
22016 var x2c = Math.max(x1, x2);
22017 var y1c = Math.min(y1, y2);
22018 var y2c = Math.max(y1, y2);
22019 x1 = x1c;
22020 x2 = x2c;
22021 y1 = y1c;
22022 y2 = y2c;
22023 var boxBb = makeBoundingBox({
22024 x1: x1,
22025 y1: y1,
22026 x2: x2,
22027 y2: y2
22028 });
22029 for (var e = 0; e < eles.length; e++) {
22030 var ele = eles[e];
22031 if (ele.isNode()) {
22032 var node = ele;
22033 var nodeBb = node.boundingBox({
22034 includeNodes: true,
22035 includeEdges: false,
22036 includeLabels: false
22037 });
22038 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
22039 box.push(node);
22040 }
22041 } else {
22042 var edge = ele;
22043 var _p = edge._private;
22044 var rs = _p.rscratch;
22045 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
22046 continue;
22047 }
22048 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
22049 continue;
22050 }
22051 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
22052 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
22053 var allInside = true;
22054 for (var i = 0; i < pts.length; i++) {
22055 if (!pointInBoundingBox(boxBb, pts[i])) {
22056 allInside = false;
22057 break;
22058 }
22059 }
22060 if (allInside) {
22061 box.push(edge);
22062 }
22063 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
22064 box.push(edge);
22065 }
22066 }
22067 }
22068 return box;
22069 };
22070
22071 var BRp$d = {};
22072 BRp$d.calculateArrowAngles = function (edge) {
22073 var rs = edge._private.rscratch;
22074 var isHaystack = rs.edgeType === 'haystack';
22075 var isBezier = rs.edgeType === 'bezier';
22076 var isMultibezier = rs.edgeType === 'multibezier';
22077 var isSegments = rs.edgeType === 'segments';
22078 var isCompound = rs.edgeType === 'compound';
22079 var isSelf = rs.edgeType === 'self';
22080
22081 // Displacement gives direction for arrowhead orientation
22082 var dispX, dispY;
22083 var startX, startY, endX, endY, midX, midY;
22084 if (isHaystack) {
22085 startX = rs.haystackPts[0];
22086 startY = rs.haystackPts[1];
22087 endX = rs.haystackPts[2];
22088 endY = rs.haystackPts[3];
22089 } else {
22090 startX = rs.arrowStartX;
22091 startY = rs.arrowStartY;
22092 endX = rs.arrowEndX;
22093 endY = rs.arrowEndY;
22094 }
22095 midX = rs.midX;
22096 midY = rs.midY;
22097
22098 // source
22099 //
22100
22101 if (isSegments) {
22102 dispX = startX - rs.segpts[0];
22103 dispY = startY - rs.segpts[1];
22104 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22105 var pts = rs.allpts;
22106 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
22107 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
22108 dispX = startX - bX;
22109 dispY = startY - bY;
22110 } else {
22111 dispX = startX - midX;
22112 dispY = startY - midY;
22113 }
22114 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY);
22115
22116 // mid target
22117 //
22118
22119 var midX = rs.midX;
22120 var midY = rs.midY;
22121 if (isHaystack) {
22122 midX = (startX + endX) / 2;
22123 midY = (startY + endY) / 2;
22124 }
22125 dispX = endX - startX;
22126 dispY = endY - startY;
22127 if (isSegments) {
22128 var pts = rs.allpts;
22129 if (pts.length / 2 % 2 === 0) {
22130 var i2 = pts.length / 2;
22131 var i1 = i2 - 2;
22132 dispX = pts[i2] - pts[i1];
22133 dispY = pts[i2 + 1] - pts[i1 + 1];
22134 } else if (rs.isRound) {
22135 dispX = rs.midVector[1];
22136 dispY = -rs.midVector[0];
22137 } else {
22138 var i2 = pts.length / 2 - 1;
22139 var i1 = i2 - 2;
22140 dispX = pts[i2] - pts[i1];
22141 dispY = pts[i2 + 1] - pts[i1 + 1];
22142 }
22143 } else if (isMultibezier || isCompound || isSelf) {
22144 var pts = rs.allpts;
22145 var cpts = rs.ctrlpts;
22146 var bp0x, bp0y;
22147 var bp1x, bp1y;
22148 if (cpts.length / 2 % 2 === 0) {
22149 var p0 = pts.length / 2 - 1; // startpt
22150 var ic = p0 + 2;
22151 var p1 = ic + 2;
22152 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
22153 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
22154 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
22155 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
22156 } else {
22157 var ic = pts.length / 2 - 1; // ctrpt
22158 var p0 = ic - 2; // startpt
22159 var p1 = ic + 2; // endpt
22160
22161 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
22162 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
22163 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
22164 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
22165 }
22166 dispX = bp1x - bp0x;
22167 dispY = bp1y - bp0y;
22168 }
22169 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
22170 rs.midDispX = dispX;
22171 rs.midDispY = dispY;
22172
22173 // mid source
22174 //
22175
22176 dispX *= -1;
22177 dispY *= -1;
22178 if (isSegments) {
22179 var pts = rs.allpts;
22180 if (pts.length / 2 % 2 === 0) ; else if (!rs.isRound) {
22181 var i2 = pts.length / 2 - 1;
22182 var i3 = i2 + 2;
22183 dispX = -(pts[i3] - pts[i2]);
22184 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22185 }
22186 }
22187 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY);
22188
22189 // target
22190 //
22191
22192 if (isSegments) {
22193 dispX = endX - rs.segpts[rs.segpts.length - 2];
22194 dispY = endY - rs.segpts[rs.segpts.length - 1];
22195 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22196 var pts = rs.allpts;
22197 var l = pts.length;
22198 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22199 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22200 dispX = endX - bX;
22201 dispY = endY - bY;
22202 } else {
22203 dispX = endX - midX;
22204 dispY = endY - midY;
22205 }
22206 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22207 };
22208 BRp$d.getArrowWidth = BRp$d.getArrowHeight = function (edgeWidth, scale) {
22209 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22210 var cachedVal = cache[edgeWidth + ', ' + scale];
22211 if (cachedVal) {
22212 return cachedVal;
22213 }
22214 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22215 cache[edgeWidth + ', ' + scale] = cachedVal;
22216 return cachedVal;
22217 };
22218
22219 /**
22220 * Explained by Blindman67 at https://stackoverflow.com/a/44856925/11028828
22221 */
22222
22223 // Declare reused variable to avoid reallocating variables every time the function is called
22224 var x,
22225 y,
22226 v1 = {},
22227 v2 = {},
22228 sinA,
22229 sinA90,
22230 radDirection,
22231 drawDirection,
22232 angle,
22233 halfAngle,
22234 cRadius,
22235 lenOut,
22236 radius,
22237 limit;
22238 var startX, startY, stopX, stopY;
22239 var lastPoint;
22240
22241 // convert 2 points into vector form, polar form, and normalised
22242 var asVec = function asVec(p, pp, v) {
22243 v.x = pp.x - p.x;
22244 v.y = pp.y - p.y;
22245 v.len = Math.sqrt(v.x * v.x + v.y * v.y);
22246 v.nx = v.x / v.len;
22247 v.ny = v.y / v.len;
22248 v.ang = Math.atan2(v.ny, v.nx);
22249 };
22250 var invertVec = function invertVec(originalV, invertedV) {
22251 invertedV.x = originalV.x * -1;
22252 invertedV.y = originalV.y * -1;
22253 invertedV.nx = originalV.nx * -1;
22254 invertedV.ny = originalV.ny * -1;
22255 invertedV.ang = originalV.ang > 0 ? -(Math.PI - originalV.ang) : Math.PI + originalV.ang;
22256 };
22257 var calcCornerArc = function calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius) {
22258 //-----------------------------------------
22259 // Part 1
22260 previousPoint !== lastPoint ? asVec(currentPoint, previousPoint, v1) : invertVec(v2, v1); // Avoid recalculating vec if it is the invert of the last one calculated
22261 asVec(currentPoint, nextPoint, v2);
22262 sinA = v1.nx * v2.ny - v1.ny * v2.nx;
22263 sinA90 = v1.nx * v2.nx - v1.ny * -v2.ny;
22264 angle = Math.asin(Math.max(-1, Math.min(1, sinA)));
22265 if (Math.abs(angle) < 1e-6) {
22266 x = currentPoint.x;
22267 y = currentPoint.y;
22268 cRadius = radius = 0;
22269 return;
22270 }
22271 //-----------------------------------------
22272 radDirection = 1;
22273 drawDirection = false;
22274 if (sinA90 < 0) {
22275 if (angle < 0) {
22276 angle = Math.PI + angle;
22277 } else {
22278 angle = Math.PI - angle;
22279 radDirection = -1;
22280 drawDirection = true;
22281 }
22282 } else {
22283 if (angle > 0) {
22284 radDirection = -1;
22285 drawDirection = true;
22286 }
22287 }
22288 if (currentPoint.radius !== undefined) {
22289 radius = currentPoint.radius;
22290 } else {
22291 radius = radiusMax;
22292 }
22293 //-----------------------------------------
22294 // Part 2
22295 halfAngle = angle / 2;
22296 //-----------------------------------------
22297
22298 limit = Math.min(v1.len / 2, v2.len / 2);
22299 if (isArcRadius) {
22300 //-----------------------------------------
22301 // Part 3
22302 lenOut = Math.abs(Math.cos(halfAngle) * radius / Math.sin(halfAngle));
22303
22304 //-----------------------------------------
22305 // Special part A
22306 if (lenOut > limit) {
22307 lenOut = limit;
22308 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
22309 } else {
22310 cRadius = radius;
22311 }
22312 } else {
22313 lenOut = Math.min(limit, radius);
22314 cRadius = Math.abs(lenOut * Math.sin(halfAngle) / Math.cos(halfAngle));
22315 }
22316 //-----------------------------------------
22317
22318 //-----------------------------------------
22319 // Part 4
22320 stopX = currentPoint.x + v2.nx * lenOut;
22321 stopY = currentPoint.y + v2.ny * lenOut;
22322 //-----------------------------------------
22323 // Part 5
22324 x = stopX - v2.ny * cRadius * radDirection;
22325 y = stopY + v2.nx * cRadius * radDirection;
22326 //-----------------------------------------
22327 // Additional Part : calculate start point E
22328 startX = currentPoint.x + v1.nx * lenOut;
22329 startY = currentPoint.y + v1.ny * lenOut;
22330
22331 // Save last point to avoid recalculating vector when not needed
22332 lastPoint = currentPoint;
22333 };
22334
22335 /**
22336 * Draw corner provided by {@link getRoundCorner}
22337 *
22338 * @param ctx :CanvasRenderingContext2D
22339 * @param roundCorner {{cx:number, cy:number, radius:number, endAngle: number, startAngle: number, counterClockwise: boolean}}
22340 */
22341 function drawPreparedRoundCorner(ctx, roundCorner) {
22342 if (roundCorner.radius === 0) ctx.lineTo(roundCorner.cx, roundCorner.cy);else ctx.arc(roundCorner.cx, roundCorner.cy, roundCorner.radius, roundCorner.startAngle, roundCorner.endAngle, roundCorner.counterClockwise);
22343 }
22344
22345 /**
22346 * Get round corner from a point and its previous and next neighbours in a path
22347 *
22348 * @param previousPoint {{x: number, y:number, radius: number?}}
22349 * @param currentPoint {{x: number, y:number, radius: number?}}
22350 * @param nextPoint {{x: number, y:number, radius: number?}}
22351 * @param radiusMax :number
22352 * @param isArcRadius :boolean
22353 * @return {{
22354 * cx:number, cy:number, radius:number,
22355 * startX:number, startY:number,
22356 * stopX:number, stopY: number,
22357 * endAngle: number, startAngle: number, counterClockwise: boolean
22358 * }}
22359 */
22360 function getRoundCorner(previousPoint, currentPoint, nextPoint, radiusMax) {
22361 var isArcRadius = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
22362 if (radiusMax === 0 || currentPoint.radius === 0) return {
22363 cx: currentPoint.x,
22364 cy: currentPoint.y,
22365 radius: 0,
22366 startX: currentPoint.x,
22367 startY: currentPoint.y,
22368 stopX: currentPoint.x,
22369 stopY: currentPoint.y,
22370 startAngle: undefined,
22371 endAngle: undefined,
22372 counterClockwise: undefined
22373 };
22374 calcCornerArc(previousPoint, currentPoint, nextPoint, radiusMax, isArcRadius);
22375 return {
22376 cx: x,
22377 cy: y,
22378 radius: cRadius,
22379 startX: startX,
22380 startY: startY,
22381 stopX: stopX,
22382 stopY: stopY,
22383 startAngle: v1.ang + Math.PI / 2 * radDirection,
22384 endAngle: v2.ang - Math.PI / 2 * radDirection,
22385 counterClockwise: drawDirection
22386 };
22387 }
22388
22389 var BRp$c = {};
22390 BRp$c.findMidptPtsEtc = function (edge, pairInfo) {
22391 var posPts = pairInfo.posPts,
22392 intersectionPts = pairInfo.intersectionPts,
22393 vectorNormInverse = pairInfo.vectorNormInverse;
22394 var midptPts;
22395
22396 // n.b. assumes all edges in bezier bundle have same endpoints specified
22397 var srcManEndpt = edge.pstyle('source-endpoint');
22398 var tgtManEndpt = edge.pstyle('target-endpoint');
22399 var haveManualEndPts = srcManEndpt.units != null && tgtManEndpt.units != null;
22400 var recalcVectorNormInverse = function recalcVectorNormInverse(x1, y1, x2, y2) {
22401 var dy = y2 - y1;
22402 var dx = x2 - x1;
22403 var l = Math.sqrt(dx * dx + dy * dy);
22404 return {
22405 x: -dy / l,
22406 y: dx / l
22407 };
22408 };
22409 var edgeDistances = edge.pstyle('edge-distances').value;
22410 switch (edgeDistances) {
22411 case 'node-position':
22412 midptPts = posPts;
22413 break;
22414 case 'intersection':
22415 midptPts = intersectionPts;
22416 break;
22417 case 'endpoints':
22418 {
22419 if (haveManualEndPts) {
22420 var _this$manualEndptToPx = this.manualEndptToPx(edge.source()[0], srcManEndpt),
22421 _this$manualEndptToPx2 = _slicedToArray(_this$manualEndptToPx, 2),
22422 x1 = _this$manualEndptToPx2[0],
22423 y1 = _this$manualEndptToPx2[1];
22424 var _this$manualEndptToPx3 = this.manualEndptToPx(edge.target()[0], tgtManEndpt),
22425 _this$manualEndptToPx4 = _slicedToArray(_this$manualEndptToPx3, 2),
22426 x2 = _this$manualEndptToPx4[0],
22427 y2 = _this$manualEndptToPx4[1];
22428 var endPts = {
22429 x1: x1,
22430 y1: y1,
22431 x2: x2,
22432 y2: y2
22433 };
22434 vectorNormInverse = recalcVectorNormInverse(x1, y1, x2, y2);
22435 midptPts = endPts;
22436 } else {
22437 warn("Edge ".concat(edge.id(), " has edge-distances:endpoints specified without manual endpoints specified via source-endpoint and target-endpoint. Falling back on edge-distances:intersection (default)."));
22438 midptPts = intersectionPts; // back to default
22439 }
22440
22441 break;
22442 }
22443 }
22444 return {
22445 midptPts: midptPts,
22446 vectorNormInverse: vectorNormInverse
22447 };
22448 };
22449 BRp$c.findHaystackPoints = function (edges) {
22450 for (var i = 0; i < edges.length; i++) {
22451 var edge = edges[i];
22452 var _p = edge._private;
22453 var rs = _p.rscratch;
22454 if (!rs.haystack) {
22455 var angle = Math.random() * 2 * Math.PI;
22456 rs.source = {
22457 x: Math.cos(angle),
22458 y: Math.sin(angle)
22459 };
22460 angle = Math.random() * 2 * Math.PI;
22461 rs.target = {
22462 x: Math.cos(angle),
22463 y: Math.sin(angle)
22464 };
22465 }
22466 var src = _p.source;
22467 var tgt = _p.target;
22468 var srcPos = src.position();
22469 var tgtPos = tgt.position();
22470 var srcW = src.width();
22471 var tgtW = tgt.width();
22472 var srcH = src.height();
22473 var tgtH = tgt.height();
22474 var radius = edge.pstyle('haystack-radius').value;
22475 var halfRadius = radius / 2; // b/c have to half width/height
22476
22477 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];
22478 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22479 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2;
22480
22481 // always override as haystack in case set to different type previously
22482 rs.edgeType = 'haystack';
22483 rs.haystack = true;
22484 this.storeEdgeProjections(edge);
22485 this.calculateArrowAngles(edge);
22486 this.recalculateEdgeLabelProjections(edge);
22487 this.calculateLabelAngles(edge);
22488 }
22489 };
22490 BRp$c.findSegmentsPoints = function (edge, pairInfo) {
22491 // Segments (multiple straight lines)
22492
22493 var rs = edge._private.rscratch;
22494 var segmentWs = edge.pstyle('segment-weights');
22495 var segmentDs = edge.pstyle('segment-distances');
22496 var segmentRs = edge.pstyle('segment-radii');
22497 var segmentTs = edge.pstyle('radius-type');
22498 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22499 var lastRadius = segmentRs.pfValue[segmentRs.pfValue.length - 1];
22500 var lastRadiusType = segmentTs.pfValue[segmentTs.pfValue.length - 1];
22501 rs.edgeType = 'segments';
22502 rs.segpts = [];
22503 rs.radii = [];
22504 rs.isArcRadius = [];
22505 for (var s = 0; s < segmentsN; s++) {
22506 var w = segmentWs.pfValue[s];
22507 var d = segmentDs.pfValue[s];
22508 var w1 = 1 - w;
22509 var w2 = w;
22510 var _this$findMidptPtsEtc = this.findMidptPtsEtc(edge, pairInfo),
22511 midptPts = _this$findMidptPtsEtc.midptPts,
22512 vectorNormInverse = _this$findMidptPtsEtc.vectorNormInverse;
22513 var adjustedMidpt = {
22514 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22515 y: midptPts.y1 * w1 + midptPts.y2 * w2
22516 };
22517 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22518 rs.radii.push(segmentRs.pfValue[s] !== undefined ? segmentRs.pfValue[s] : lastRadius);
22519 rs.isArcRadius.push((segmentTs.pfValue[s] !== undefined ? segmentTs.pfValue[s] : lastRadiusType) === 'arc-radius');
22520 }
22521 };
22522 BRp$c.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22523 // Self-edge
22524
22525 var rs = edge._private.rscratch;
22526 var dirCounts = pairInfo.dirCounts,
22527 srcPos = pairInfo.srcPos;
22528 var ctrlptDists = edge.pstyle('control-point-distances');
22529 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22530 var loopDir = edge.pstyle('loop-direction').pfValue;
22531 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22532 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22533 rs.edgeType = 'self';
22534 var j = i;
22535 var loopDist = stepSize;
22536 if (edgeIsUnbundled) {
22537 j = 0;
22538 loopDist = ctrlptDist;
22539 }
22540 var loopAngle = loopDir - Math.PI / 2;
22541 var outAngle = loopAngle - loopSwp / 2;
22542 var inAngle = loopAngle + loopSwp / 2;
22543
22544 // increase by step size for overlapping loops, keyed on direction and sweep values
22545 var dc = String(loopDir + '_' + loopSwp);
22546 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22547 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)];
22548 };
22549 BRp$c.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22550 // Compound edge
22551
22552 var rs = edge._private.rscratch;
22553 rs.edgeType = 'compound';
22554 var srcPos = pairInfo.srcPos,
22555 tgtPos = pairInfo.tgtPos,
22556 srcW = pairInfo.srcW,
22557 srcH = pairInfo.srcH,
22558 tgtW = pairInfo.tgtW,
22559 tgtH = pairInfo.tgtH;
22560 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22561 var ctrlptDists = edge.pstyle('control-point-distances');
22562 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22563 var j = i;
22564 var loopDist = stepSize;
22565 if (edgeIsUnbundled) {
22566 j = 0;
22567 loopDist = ctrlptDist;
22568 }
22569 var loopW = 50;
22570 var loopaPos = {
22571 x: srcPos.x - srcW / 2,
22572 y: srcPos.y - srcH / 2
22573 };
22574 var loopbPos = {
22575 x: tgtPos.x - tgtW / 2,
22576 y: tgtPos.y - tgtH / 2
22577 };
22578 var loopPos = {
22579 x: Math.min(loopaPos.x, loopbPos.x),
22580 y: Math.min(loopaPos.y, loopbPos.y)
22581 };
22582
22583 // avoids cases with impossible beziers
22584 var minCompoundStretch = 0.5;
22585 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22586 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22587 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];
22588 };
22589 BRp$c.findStraightEdgePoints = function (edge) {
22590 // Straight edge within bundle
22591
22592 edge._private.rscratch.edgeType = 'straight';
22593 };
22594 BRp$c.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22595 var rs = edge._private.rscratch;
22596 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22597 var ctrlptDists = edge.pstyle('control-point-distances');
22598 var ctrlptWs = edge.pstyle('control-point-weights');
22599 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22600 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22601 var ctrlptWeight = ctrlptWs.value[0];
22602
22603 // (Multi)bezier
22604
22605 var multi = edgeIsUnbundled;
22606 rs.edgeType = multi ? 'multibezier' : 'bezier';
22607 rs.ctrlpts = [];
22608 for (var b = 0; b < bezierN; b++) {
22609 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22610 var manctrlptDist = void 0;
22611 var sign = signum(normctrlptDist);
22612 if (multi) {
22613 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22614 ctrlptWeight = ctrlptWs.value[b];
22615 }
22616 if (edgeIsUnbundled) {
22617 // multi or single unbundled
22618 manctrlptDist = ctrlptDist;
22619 } else {
22620 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22621 }
22622 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22623 var w1 = 1 - ctrlptWeight;
22624 var w2 = ctrlptWeight;
22625 var _this$findMidptPtsEtc2 = this.findMidptPtsEtc(edge, pairInfo),
22626 midptPts = _this$findMidptPtsEtc2.midptPts,
22627 vectorNormInverse = _this$findMidptPtsEtc2.vectorNormInverse;
22628 var adjustedMidpt = {
22629 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22630 y: midptPts.y1 * w1 + midptPts.y2 * w2
22631 };
22632 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22633 }
22634 };
22635 BRp$c.findTaxiPoints = function (edge, pairInfo) {
22636 // Taxicab geometry with two turns maximum
22637
22638 var rs = edge._private.rscratch;
22639 rs.edgeType = 'segments';
22640 var VERTICAL = 'vertical';
22641 var HORIZONTAL = 'horizontal';
22642 var LEFTWARD = 'leftward';
22643 var RIGHTWARD = 'rightward';
22644 var DOWNWARD = 'downward';
22645 var UPWARD = 'upward';
22646 var AUTO = 'auto';
22647 var posPts = pairInfo.posPts,
22648 srcW = pairInfo.srcW,
22649 srcH = pairInfo.srcH,
22650 tgtW = pairInfo.tgtW,
22651 tgtH = pairInfo.tgtH;
22652 var edgeDistances = edge.pstyle('edge-distances').value;
22653 var dIncludesNodeBody = edgeDistances !== 'node-position';
22654 var taxiDir = edge.pstyle('taxi-direction').value;
22655 var rawTaxiDir = taxiDir; // unprocessed value
22656 var taxiTurn = edge.pstyle('taxi-turn');
22657 var turnIsPercent = taxiTurn.units === '%';
22658 var taxiTurnPfVal = taxiTurn.pfValue;
22659 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22660 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22661 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22662 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22663 var pdx = posPts.x2 - posPts.x1;
22664 var pdy = posPts.y2 - posPts.y1;
22665
22666 // take away the effective w/h from the magnitude of the delta value
22667 var subDWH = function subDWH(dxy, dwh) {
22668 if (dxy > 0) {
22669 return Math.max(dxy - dwh, 0);
22670 } else {
22671 return Math.min(dxy + dwh, 0);
22672 }
22673 };
22674 var dx = subDWH(pdx, dw);
22675 var dy = subDWH(pdy, dh);
22676 var isExplicitDir = false;
22677 if (rawTaxiDir === AUTO) {
22678 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22679 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22680 taxiDir = VERTICAL;
22681 isExplicitDir = true;
22682 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22683 taxiDir = HORIZONTAL;
22684 isExplicitDir = true;
22685 }
22686 var isVert = taxiDir === VERTICAL;
22687 var l = isVert ? dy : dx;
22688 var pl = isVert ? pdy : pdx;
22689 var sgnL = signum(pl);
22690 var forcedDir = false;
22691 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22692 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22693 sgnL *= -1;
22694 l = sgnL * Math.abs(l);
22695 forcedDir = true;
22696 }
22697 var d;
22698 if (turnIsPercent) {
22699 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22700 d = p * l;
22701 } else {
22702 var k = taxiTurnPfVal < 0 ? l : 0;
22703 d = k + taxiTurnPfVal * sgnL;
22704 }
22705 var getIsTooClose = function getIsTooClose(d) {
22706 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22707 };
22708 var isTooCloseSrc = getIsTooClose(d);
22709 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22710 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22711 if (isTooClose && !forcedDir) {
22712 // non-ideal routing
22713 if (isVert) {
22714 // vertical fallbacks
22715 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22716 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22717 if (lShapeInsideSrc) {
22718 // horizontal Z-shape (direction not respected)
22719 var x = (posPts.x1 + posPts.x2) / 2;
22720 var y1 = posPts.y1,
22721 y2 = posPts.y2;
22722 rs.segpts = [x, y1, x, y2];
22723 } else if (lShapeInsideTgt) {
22724 // vertical Z-shape (distance not respected)
22725 var y = (posPts.y1 + posPts.y2) / 2;
22726 var x1 = posPts.x1,
22727 x2 = posPts.x2;
22728 rs.segpts = [x1, y, x2, y];
22729 } else {
22730 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22731 rs.segpts = [posPts.x1, posPts.y2];
22732 }
22733 } else {
22734 // horizontal fallbacks
22735 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22736 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22737 if (_lShapeInsideSrc) {
22738 // vertical Z-shape (direction not respected)
22739 var _y = (posPts.y1 + posPts.y2) / 2;
22740 var _x = posPts.x1,
22741 _x2 = posPts.x2;
22742 rs.segpts = [_x, _y, _x2, _y];
22743 } else if (_lShapeInsideTgt) {
22744 // horizontal Z-shape (turn distance not respected)
22745 var _x3 = (posPts.x1 + posPts.x2) / 2;
22746 var _y2 = posPts.y1,
22747 _y3 = posPts.y2;
22748 rs.segpts = [_x3, _y2, _x3, _y3];
22749 } else {
22750 // L-shape (turn distance not respected, but works well for tree siblings)
22751 rs.segpts = [posPts.x2, posPts.y1];
22752 }
22753 }
22754 } else {
22755 // ideal routing
22756 if (isVert) {
22757 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22758 var _x4 = posPts.x1,
22759 _x5 = posPts.x2;
22760 rs.segpts = [_x4, _y4, _x5, _y4];
22761 } else {
22762 // horizontal
22763 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22764 var _y5 = posPts.y1,
22765 _y6 = posPts.y2;
22766 rs.segpts = [_x6, _y5, _x6, _y6];
22767 }
22768 }
22769 if (rs.isRound) {
22770 var radius = edge.pstyle('taxi-radius').value;
22771 var isArcRadius = edge.pstyle('radius-type').value[0] === 'arc-radius';
22772 rs.radii = new Array(rs.segpts.length / 2).fill(radius);
22773 rs.isArcRadius = new Array(rs.segpts.length / 2).fill(isArcRadius);
22774 }
22775 };
22776 BRp$c.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22777 var rs = edge._private.rscratch;
22778
22779 // can only correct beziers for now...
22780 if (rs.edgeType === 'bezier') {
22781 var srcPos = pairInfo.srcPos,
22782 tgtPos = pairInfo.tgtPos,
22783 srcW = pairInfo.srcW,
22784 srcH = pairInfo.srcH,
22785 tgtW = pairInfo.tgtW,
22786 tgtH = pairInfo.tgtH,
22787 srcShape = pairInfo.srcShape,
22788 tgtShape = pairInfo.tgtShape,
22789 srcCornerRadius = pairInfo.srcCornerRadius,
22790 tgtCornerRadius = pairInfo.tgtCornerRadius,
22791 srcRs = pairInfo.srcRs,
22792 tgtRs = pairInfo.tgtRs;
22793 var badStart = !number$1(rs.startX) || !number$1(rs.startY);
22794 var badAStart = !number$1(rs.arrowStartX) || !number$1(rs.arrowStartY);
22795 var badEnd = !number$1(rs.endX) || !number$1(rs.endY);
22796 var badAEnd = !number$1(rs.arrowEndX) || !number$1(rs.arrowEndY);
22797 var minCpADistFactor = 3;
22798 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22799 var minCpADist = minCpADistFactor * arrowW;
22800 var startACpDist = dist({
22801 x: rs.ctrlpts[0],
22802 y: rs.ctrlpts[1]
22803 }, {
22804 x: rs.startX,
22805 y: rs.startY
22806 });
22807 var closeStartACp = startACpDist < minCpADist;
22808 var endACpDist = dist({
22809 x: rs.ctrlpts[0],
22810 y: rs.ctrlpts[1]
22811 }, {
22812 x: rs.endX,
22813 y: rs.endY
22814 });
22815 var closeEndACp = endACpDist < minCpADist;
22816 var overlapping = false;
22817 if (badStart || badAStart || closeStartACp) {
22818 overlapping = true;
22819
22820 // project control point along line from src centre to outside the src shape
22821 // (otherwise intersection will yield nothing)
22822 var cpD = {
22823 // delta
22824 x: rs.ctrlpts[0] - srcPos.x,
22825 y: rs.ctrlpts[1] - srcPos.y
22826 };
22827 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22828 var cpM = {
22829 // normalised delta
22830 x: cpD.x / cpL,
22831 y: cpD.y / cpL
22832 };
22833 var radius = Math.max(srcW, srcH);
22834 var cpProj = {
22835 // *2 radius guarantees outside shape
22836 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22837 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22838 };
22839 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0, srcCornerRadius, srcRs);
22840 if (closeStartACp) {
22841 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22842 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22843 } else {
22844 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22845 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22846 }
22847 }
22848 if (badEnd || badAEnd || closeEndACp) {
22849 overlapping = true;
22850
22851 // project control point along line from tgt centre to outside the tgt shape
22852 // (otherwise intersection will yield nothing)
22853 var _cpD = {
22854 // delta
22855 x: rs.ctrlpts[0] - tgtPos.x,
22856 y: rs.ctrlpts[1] - tgtPos.y
22857 };
22858 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22859 var _cpM = {
22860 // normalised delta
22861 x: _cpD.x / _cpL,
22862 y: _cpD.y / _cpL
22863 };
22864 var _radius = Math.max(srcW, srcH);
22865 var _cpProj = {
22866 // *2 radius guarantees outside shape
22867 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22868 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22869 };
22870 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0, tgtCornerRadius, tgtRs);
22871 if (closeEndACp) {
22872 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22873 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22874 } else {
22875 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22876 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22877 }
22878 }
22879 if (overlapping) {
22880 // recalc endpts
22881 this.findEndpoints(edge);
22882 }
22883 }
22884 };
22885 BRp$c.storeAllpts = function (edge) {
22886 var rs = edge._private.rscratch;
22887 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22888 rs.allpts = [];
22889 rs.allpts.push(rs.startX, rs.startY);
22890 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22891 // ctrl pt itself
22892 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]);
22893
22894 // the midpt between ctrlpts as intermediate destination pts
22895 if (b + 3 < rs.ctrlpts.length) {
22896 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22897 }
22898 }
22899 rs.allpts.push(rs.endX, rs.endY);
22900 var m, mt;
22901 if (rs.ctrlpts.length / 2 % 2 === 0) {
22902 m = rs.allpts.length / 2 - 1;
22903 rs.midX = rs.allpts[m];
22904 rs.midY = rs.allpts[m + 1];
22905 } else {
22906 m = rs.allpts.length / 2 - 3;
22907 mt = 0.5;
22908 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22909 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22910 }
22911 } else if (rs.edgeType === 'straight') {
22912 // need to calc these after endpts
22913 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY];
22914
22915 // default midpt for labels etc
22916 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22917 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22918 } else if (rs.edgeType === 'segments') {
22919 rs.allpts = [];
22920 rs.allpts.push(rs.startX, rs.startY);
22921 rs.allpts.push.apply(rs.allpts, rs.segpts);
22922 rs.allpts.push(rs.endX, rs.endY);
22923 if (rs.isRound) {
22924 rs.roundCorners = [];
22925 for (var i = 2; i + 3 < rs.allpts.length; i += 2) {
22926 var radius = rs.radii[i / 2 - 1];
22927 var isArcRadius = rs.isArcRadius[i / 2 - 1];
22928 rs.roundCorners.push(getRoundCorner({
22929 x: rs.allpts[i - 2],
22930 y: rs.allpts[i - 1]
22931 }, {
22932 x: rs.allpts[i],
22933 y: rs.allpts[i + 1],
22934 radius: radius
22935 }, {
22936 x: rs.allpts[i + 2],
22937 y: rs.allpts[i + 3]
22938 }, radius, isArcRadius));
22939 }
22940 }
22941 if (rs.segpts.length % 4 === 0) {
22942 var i2 = rs.segpts.length / 2;
22943 var i1 = i2 - 2;
22944 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22945 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22946 } else {
22947 var _i = rs.segpts.length / 2 - 1;
22948 if (!rs.isRound) {
22949 rs.midX = rs.segpts[_i];
22950 rs.midY = rs.segpts[_i + 1];
22951 } else {
22952 var point = {
22953 x: rs.segpts[_i],
22954 y: rs.segpts[_i + 1]
22955 };
22956 var corner = rs.roundCorners[_i / 2];
22957 var v = [point.x - corner.cx, point.y - corner.cy];
22958 var factor = corner.radius / Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2));
22959 v = v.map(function (c) {
22960 return c * factor;
22961 });
22962 rs.midX = corner.cx + v[0];
22963 rs.midY = corner.cy + v[1];
22964 rs.midVector = v;
22965 }
22966 }
22967 }
22968 };
22969 BRp$c.checkForInvalidEdgeWarning = function (edge) {
22970 var rs = edge[0]._private.rscratch;
22971 if (rs.nodesOverlap || number$1(rs.startX) && number$1(rs.startY) && number$1(rs.endX) && number$1(rs.endY)) {
22972 rs.loggedErr = false;
22973 } else {
22974 if (!rs.loggedErr) {
22975 rs.loggedErr = true;
22976 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.');
22977 }
22978 }
22979 };
22980 BRp$c.findEdgeControlPoints = function (edges) {
22981 var _this = this;
22982 if (!edges || edges.length === 0) {
22983 return;
22984 }
22985 var r = this;
22986 var cy = r.cy;
22987 var hasCompounds = cy.hasCompoundNodes();
22988 var hashTable = {
22989 map: new Map$2(),
22990 get: function get(pairId) {
22991 var map2 = this.map.get(pairId[0]);
22992 if (map2 != null) {
22993 return map2.get(pairId[1]);
22994 } else {
22995 return null;
22996 }
22997 },
22998 set: function set(pairId, val) {
22999 var map2 = this.map.get(pairId[0]);
23000 if (map2 == null) {
23001 map2 = new Map$2();
23002 this.map.set(pairId[0], map2);
23003 }
23004 map2.set(pairId[1], val);
23005 }
23006 };
23007 var pairIds = [];
23008 var haystackEdges = [];
23009
23010 // create a table of edge (src, tgt) => list of edges between them
23011 for (var i = 0; i < edges.length; i++) {
23012 var edge = edges[i];
23013 var _p = edge._private;
23014 var curveStyle = edge.pstyle('curve-style').value;
23015
23016 // ignore edges who are not to be displayed
23017 // they shouldn't take up space
23018 if (edge.removed() || !edge.takesUpSpace()) {
23019 continue;
23020 }
23021 if (curveStyle === 'haystack') {
23022 haystackEdges.push(edge);
23023 continue;
23024 }
23025 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle.endsWith('segments') || curveStyle === 'straight' || curveStyle === 'straight-triangle' || curveStyle.endsWith('taxi');
23026 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
23027 var src = _p.source;
23028 var tgt = _p.target;
23029 var srcIndex = src.poolIndex();
23030 var tgtIndex = tgt.poolIndex();
23031 var pairId = [srcIndex, tgtIndex].sort();
23032 var tableEntry = hashTable.get(pairId);
23033 if (tableEntry == null) {
23034 tableEntry = {
23035 eles: []
23036 };
23037 hashTable.set(pairId, tableEntry);
23038 pairIds.push(pairId);
23039 }
23040 tableEntry.eles.push(edge);
23041 if (edgeIsUnbundled) {
23042 tableEntry.hasUnbundled = true;
23043 }
23044 if (edgeIsBezier) {
23045 tableEntry.hasBezier = true;
23046 }
23047 }
23048
23049 // for each pair (src, tgt), create the ctrl pts
23050 // Nested for loop is OK; total number of iterations for both loops = edgeCount
23051 var _loop = function _loop(p) {
23052 var pairId = pairIds[p];
23053 var pairInfo = hashTable.get(pairId);
23054 var swappedpairInfo = void 0;
23055 if (!pairInfo.hasUnbundled) {
23056 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
23057 return e.isBundledBezier();
23058 });
23059 clearArray(pairInfo.eles);
23060 pllEdges.forEach(function (edge) {
23061 return pairInfo.eles.push(edge);
23062 });
23063
23064 // for each pair id, the edges should be sorted by index
23065 pairInfo.eles.sort(function (edge1, edge2) {
23066 return edge1.poolIndex() - edge2.poolIndex();
23067 });
23068 }
23069 var firstEdge = pairInfo.eles[0];
23070 var src = firstEdge.source();
23071 var tgt = firstEdge.target();
23072
23073 // make sure src/tgt distinction is consistent w.r.t. pairId
23074 if (src.poolIndex() > tgt.poolIndex()) {
23075 var temp = src;
23076 src = tgt;
23077 tgt = temp;
23078 }
23079 var srcPos = pairInfo.srcPos = src.position();
23080 var tgtPos = pairInfo.tgtPos = tgt.position();
23081 var srcW = pairInfo.srcW = src.outerWidth();
23082 var srcH = pairInfo.srcH = src.outerHeight();
23083 var tgtW = pairInfo.tgtW = tgt.outerWidth();
23084 var tgtH = pairInfo.tgtH = tgt.outerHeight();
23085 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
23086 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
23087 var srcCornerRadius = pairInfo.srcCornerRadius = src.pstyle('corner-radius').value === 'auto' ? 'auto' : src.pstyle('corner-radius').pfValue;
23088 var tgtCornerRadius = pairInfo.tgtCornerRadius = tgt.pstyle('corner-radius').value === 'auto' ? 'auto' : tgt.pstyle('corner-radius').pfValue;
23089 var tgtRs = pairInfo.tgtRs = tgt._private.rscratch;
23090 var srcRs = pairInfo.srcRs = src._private.rscratch;
23091 pairInfo.dirCounts = {
23092 'north': 0,
23093 'west': 0,
23094 'south': 0,
23095 'east': 0,
23096 'northwest': 0,
23097 'southwest': 0,
23098 'northeast': 0,
23099 'southeast': 0
23100 };
23101 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
23102 var _edge = pairInfo.eles[_i2];
23103 var rs = _edge[0]._private.rscratch;
23104 var _curveStyle = _edge.pstyle('curve-style').value;
23105 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle.endsWith('segments') || _curveStyle.endsWith('taxi');
23106
23107 // whether the normalised pair order is the reverse of the edge's src-tgt order
23108 var edgeIsSwapped = !src.same(_edge.source());
23109 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
23110 pairInfo.calculatedIntersection = true;
23111
23112 // pt outside src shape to calc distance/displacement from src to tgt
23113 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0, srcCornerRadius, srcRs);
23114 var srcIntn = pairInfo.srcIntn = srcOutside;
23115
23116 // pt outside tgt shape to calc distance/displacement from src to tgt
23117 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0, tgtCornerRadius, tgtRs);
23118 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
23119 var intersectionPts = pairInfo.intersectionPts = {
23120 x1: srcOutside[0],
23121 x2: tgtOutside[0],
23122 y1: srcOutside[1],
23123 y2: tgtOutside[1]
23124 };
23125 var posPts = pairInfo.posPts = {
23126 x1: srcPos.x,
23127 x2: tgtPos.x,
23128 y1: srcPos.y,
23129 y2: tgtPos.y
23130 };
23131 var dy = tgtOutside[1] - srcOutside[1];
23132 var dx = tgtOutside[0] - srcOutside[0];
23133 var l = Math.sqrt(dx * dx + dy * dy);
23134 var vector = pairInfo.vector = {
23135 x: dx,
23136 y: dy
23137 };
23138 var vectorNorm = pairInfo.vectorNorm = {
23139 x: vector.x / l,
23140 y: vector.y / l
23141 };
23142 var vectorNormInverse = {
23143 x: -vectorNorm.y,
23144 y: vectorNorm.x
23145 };
23146
23147 // if node shapes overlap, then no ctrl pts to draw
23148 pairInfo.nodesOverlap = !number$1(l) || tgtShape.checkPoint(srcOutside[0], srcOutside[1], 0, tgtW, tgtH, tgtPos.x, tgtPos.y, tgtCornerRadius, tgtRs) || srcShape.checkPoint(tgtOutside[0], tgtOutside[1], 0, srcW, srcH, srcPos.x, srcPos.y, srcCornerRadius, srcRs);
23149 pairInfo.vectorNormInverse = vectorNormInverse;
23150 swappedpairInfo = {
23151 nodesOverlap: pairInfo.nodesOverlap,
23152 dirCounts: pairInfo.dirCounts,
23153 calculatedIntersection: true,
23154 hasBezier: pairInfo.hasBezier,
23155 hasUnbundled: pairInfo.hasUnbundled,
23156 eles: pairInfo.eles,
23157 srcPos: tgtPos,
23158 tgtPos: srcPos,
23159 srcW: tgtW,
23160 srcH: tgtH,
23161 tgtW: srcW,
23162 tgtH: srcH,
23163 srcIntn: tgtIntn,
23164 tgtIntn: srcIntn,
23165 srcShape: tgtShape,
23166 tgtShape: srcShape,
23167 posPts: {
23168 x1: posPts.x2,
23169 y1: posPts.y2,
23170 x2: posPts.x1,
23171 y2: posPts.y1
23172 },
23173 intersectionPts: {
23174 x1: intersectionPts.x2,
23175 y1: intersectionPts.y2,
23176 x2: intersectionPts.x1,
23177 y2: intersectionPts.y1
23178 },
23179 vector: {
23180 x: -vector.x,
23181 y: -vector.y
23182 },
23183 vectorNorm: {
23184 x: -vectorNorm.x,
23185 y: -vectorNorm.y
23186 },
23187 vectorNormInverse: {
23188 x: -vectorNormInverse.x,
23189 y: -vectorNormInverse.y
23190 }
23191 };
23192 }
23193 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
23194 rs.nodesOverlap = passedPairInfo.nodesOverlap;
23195 rs.srcIntn = passedPairInfo.srcIntn;
23196 rs.tgtIntn = passedPairInfo.tgtIntn;
23197 rs.isRound = _curveStyle.startsWith('round');
23198 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
23199 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23200 } else if (src === tgt) {
23201 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
23202 } else if (_curveStyle.endsWith('segments')) {
23203 _this.findSegmentsPoints(_edge, passedPairInfo);
23204 } else if (_curveStyle.endsWith('taxi')) {
23205 _this.findTaxiPoints(_edge, passedPairInfo);
23206 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
23207 _this.findStraightEdgePoints(_edge);
23208 } else {
23209 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
23210 }
23211 _this.findEndpoints(_edge);
23212 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
23213 _this.checkForInvalidEdgeWarning(_edge);
23214 _this.storeAllpts(_edge);
23215 _this.storeEdgeProjections(_edge);
23216 _this.calculateArrowAngles(_edge);
23217 _this.recalculateEdgeLabelProjections(_edge);
23218 _this.calculateLabelAngles(_edge);
23219 } // for pair edges
23220 };
23221 for (var p = 0; p < pairIds.length; p++) {
23222 _loop(p);
23223 } // for pair ids
23224
23225 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
23226 this.findHaystackPoints(haystackEdges);
23227 };
23228 function getPts(pts) {
23229 var retPts = [];
23230 if (pts == null) {
23231 return;
23232 }
23233 for (var i = 0; i < pts.length; i += 2) {
23234 var x = pts[i];
23235 var y = pts[i + 1];
23236 retPts.push({
23237 x: x,
23238 y: y
23239 });
23240 }
23241 return retPts;
23242 }
23243 BRp$c.getSegmentPoints = function (edge) {
23244 var rs = edge[0]._private.rscratch;
23245 var type = rs.edgeType;
23246 if (type === 'segments') {
23247 this.recalculateRenderedStyle(edge);
23248 return getPts(rs.segpts);
23249 }
23250 };
23251 BRp$c.getControlPoints = function (edge) {
23252 var rs = edge[0]._private.rscratch;
23253 var type = rs.edgeType;
23254 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
23255 this.recalculateRenderedStyle(edge);
23256 return getPts(rs.ctrlpts);
23257 }
23258 };
23259 BRp$c.getEdgeMidpoint = function (edge) {
23260 var rs = edge[0]._private.rscratch;
23261 this.recalculateRenderedStyle(edge);
23262 return {
23263 x: rs.midX,
23264 y: rs.midY
23265 };
23266 };
23267
23268 var BRp$b = {};
23269 BRp$b.manualEndptToPx = function (node, prop) {
23270 var r = this;
23271 var npos = node.position();
23272 var w = node.outerWidth();
23273 var h = node.outerHeight();
23274 var rs = node._private.rscratch;
23275 if (prop.value.length === 2) {
23276 var p = [prop.pfValue[0], prop.pfValue[1]];
23277 if (prop.units[0] === '%') {
23278 p[0] = p[0] * w;
23279 }
23280 if (prop.units[1] === '%') {
23281 p[1] = p[1] * h;
23282 }
23283 p[0] += npos.x;
23284 p[1] += npos.y;
23285 return p;
23286 } else {
23287 var angle = prop.pfValue[0];
23288 angle = -Math.PI / 2 + angle; // start at 12 o'clock
23289
23290 var l = 2 * Math.max(w, h);
23291 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
23292 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0, node.pstyle('corner-radius').value === 'auto' ? 'auto' : node.pstyle('corner-radius').pfValue, rs);
23293 }
23294 };
23295 BRp$b.findEndpoints = function (edge) {
23296 var r = this;
23297 var intersect;
23298 var source = edge.source()[0];
23299 var target = edge.target()[0];
23300 var srcPos = source.position();
23301 var tgtPos = target.position();
23302 var tgtArShape = edge.pstyle('target-arrow-shape').value;
23303 var srcArShape = edge.pstyle('source-arrow-shape').value;
23304 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
23305 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
23306 var srcRs = source._private.rscratch;
23307 var tgtRs = target._private.rscratch;
23308 var curveStyle = edge.pstyle('curve-style').value;
23309 var rs = edge._private.rscratch;
23310 var et = rs.edgeType;
23311 var taxi = curveStyle === 'taxi';
23312 var self = et === 'self' || et === 'compound';
23313 var bezier = et === 'bezier' || et === 'multibezier' || self;
23314 var multi = et !== 'bezier';
23315 var lines = et === 'straight' || et === 'segments';
23316 var segments = et === 'segments';
23317 var hasEndpts = bezier || multi || lines;
23318 var overrideEndpts = self || taxi;
23319 var srcManEndpt = edge.pstyle('source-endpoint');
23320 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
23321 var srcCornerRadius = source.pstyle('corner-radius').value === 'auto' ? 'auto' : source.pstyle('corner-radius').pfValue;
23322 var tgtManEndpt = edge.pstyle('target-endpoint');
23323 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
23324 var tgtCornerRadius = target.pstyle('corner-radius').value === 'auto' ? 'auto' : target.pstyle('corner-radius').pfValue;
23325 rs.srcManEndpt = srcManEndpt;
23326 rs.tgtManEndpt = tgtManEndpt;
23327 var p1; // last known point of edge on target side
23328 var p2; // last known point of edge on source side
23329
23330 var p1_i; // point to intersect with target shape
23331 var p2_i; // point to intersect with source shape
23332
23333 if (bezier) {
23334 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
23335 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
23336 p1 = cpEnd;
23337 p2 = cpStart;
23338 } else if (lines) {
23339 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
23340 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
23341 p1 = tgtArrowFromPt;
23342 p2 = srcArrowFromPt;
23343 }
23344 if (tgtManEndptVal === 'inside-to-node') {
23345 intersect = [tgtPos.x, tgtPos.y];
23346 } else if (tgtManEndpt.units) {
23347 intersect = this.manualEndptToPx(target, tgtManEndpt);
23348 } else if (tgtManEndptVal === 'outside-to-line') {
23349 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
23350 } else {
23351 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
23352 p1_i = p1;
23353 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
23354 p1_i = [srcPos.x, srcPos.y];
23355 }
23356 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0, tgtCornerRadius, tgtRs);
23357 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
23358 var trs = target._private.rscratch;
23359 var lw = trs.labelWidth;
23360 var lh = trs.labelHeight;
23361 var lx = trs.labelX;
23362 var ly = trs.labelY;
23363 var lw2 = lw / 2;
23364 var lh2 = lh / 2;
23365 var va = target.pstyle('text-valign').value;
23366 if (va === 'top') {
23367 ly -= lh2;
23368 } else if (va === 'bottom') {
23369 ly += lh2;
23370 }
23371 var ha = target.pstyle('text-halign').value;
23372 if (ha === 'left') {
23373 lx -= lw2;
23374 } else if (ha === 'right') {
23375 lx += lw2;
23376 }
23377 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);
23378 if (labelIntersect.length > 0) {
23379 var refPt = srcPos;
23380 var intSqdist = sqdist(refPt, array2point(intersect));
23381 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23382 var minSqDist = intSqdist;
23383 if (labIntSqdist < intSqdist) {
23384 intersect = labelIntersect;
23385 minSqDist = labIntSqdist;
23386 }
23387 if (labelIntersect.length > 2) {
23388 var labInt2SqDist = sqdist(refPt, {
23389 x: labelIntersect[2],
23390 y: labelIntersect[3]
23391 });
23392 if (labInt2SqDist < minSqDist) {
23393 intersect = [labelIntersect[2], labelIntersect[3]];
23394 }
23395 }
23396 }
23397 }
23398 }
23399 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23400 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23401 rs.endX = edgeEnd[0];
23402 rs.endY = edgeEnd[1];
23403 rs.arrowEndX = arrowEnd[0];
23404 rs.arrowEndY = arrowEnd[1];
23405 if (srcManEndptVal === 'inside-to-node') {
23406 intersect = [srcPos.x, srcPos.y];
23407 } else if (srcManEndpt.units) {
23408 intersect = this.manualEndptToPx(source, srcManEndpt);
23409 } else if (srcManEndptVal === 'outside-to-line') {
23410 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23411 } else {
23412 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23413 p2_i = p2;
23414 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23415 p2_i = [tgtPos.x, tgtPos.y];
23416 }
23417 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0, srcCornerRadius, srcRs);
23418 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23419 var srs = source._private.rscratch;
23420 var _lw = srs.labelWidth;
23421 var _lh = srs.labelHeight;
23422 var _lx = srs.labelX;
23423 var _ly = srs.labelY;
23424 var _lw2 = _lw / 2;
23425 var _lh2 = _lh / 2;
23426 var _va = source.pstyle('text-valign').value;
23427 if (_va === 'top') {
23428 _ly -= _lh2;
23429 } else if (_va === 'bottom') {
23430 _ly += _lh2;
23431 }
23432 var _ha = source.pstyle('text-halign').value;
23433 if (_ha === 'left') {
23434 _lx -= _lw2;
23435 } else if (_ha === 'right') {
23436 _lx += _lw2;
23437 }
23438 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);
23439 if (_labelIntersect.length > 0) {
23440 var _refPt = tgtPos;
23441 var _intSqdist = sqdist(_refPt, array2point(intersect));
23442 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23443 var _minSqDist = _intSqdist;
23444 if (_labIntSqdist < _intSqdist) {
23445 intersect = [_labelIntersect[0], _labelIntersect[1]];
23446 _minSqDist = _labIntSqdist;
23447 }
23448 if (_labelIntersect.length > 2) {
23449 var _labInt2SqDist = sqdist(_refPt, {
23450 x: _labelIntersect[2],
23451 y: _labelIntersect[3]
23452 });
23453 if (_labInt2SqDist < _minSqDist) {
23454 intersect = [_labelIntersect[2], _labelIntersect[3]];
23455 }
23456 }
23457 }
23458 }
23459 }
23460 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23461 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23462 rs.startX = edgeStart[0];
23463 rs.startY = edgeStart[1];
23464 rs.arrowStartX = arrowStart[0];
23465 rs.arrowStartY = arrowStart[1];
23466 if (hasEndpts) {
23467 if (!number$1(rs.startX) || !number$1(rs.startY) || !number$1(rs.endX) || !number$1(rs.endY)) {
23468 rs.badLine = true;
23469 } else {
23470 rs.badLine = false;
23471 }
23472 }
23473 };
23474 BRp$b.getSourceEndpoint = function (edge) {
23475 var rs = edge[0]._private.rscratch;
23476 this.recalculateRenderedStyle(edge);
23477 switch (rs.edgeType) {
23478 case 'haystack':
23479 return {
23480 x: rs.haystackPts[0],
23481 y: rs.haystackPts[1]
23482 };
23483 default:
23484 return {
23485 x: rs.arrowStartX,
23486 y: rs.arrowStartY
23487 };
23488 }
23489 };
23490 BRp$b.getTargetEndpoint = function (edge) {
23491 var rs = edge[0]._private.rscratch;
23492 this.recalculateRenderedStyle(edge);
23493 switch (rs.edgeType) {
23494 case 'haystack':
23495 return {
23496 x: rs.haystackPts[2],
23497 y: rs.haystackPts[3]
23498 };
23499 default:
23500 return {
23501 x: rs.arrowEndX,
23502 y: rs.arrowEndY
23503 };
23504 }
23505 };
23506
23507 var BRp$a = {};
23508 function pushBezierPts(r, edge, pts) {
23509 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23510 return qbezierAt(p1, p2, p3, t);
23511 };
23512 var _p = edge._private;
23513 var bpts = _p.rstyle.bezierPts;
23514 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23515 var p = r.bezierProjPcts[i];
23516 bpts.push({
23517 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23518 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23519 });
23520 }
23521 }
23522 BRp$a.storeEdgeProjections = function (edge) {
23523 var _p = edge._private;
23524 var rs = _p.rscratch;
23525 var et = rs.edgeType;
23526
23527 // clear the cached points state
23528 _p.rstyle.bezierPts = null;
23529 _p.rstyle.linePts = null;
23530 _p.rstyle.haystackPts = null;
23531 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23532 _p.rstyle.bezierPts = [];
23533 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23534 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23535 }
23536 } else if (et === 'segments') {
23537 var lpts = _p.rstyle.linePts = [];
23538 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23539 lpts.push({
23540 x: rs.allpts[i],
23541 y: rs.allpts[i + 1]
23542 });
23543 }
23544 } else if (et === 'haystack') {
23545 var hpts = rs.haystackPts;
23546 _p.rstyle.haystackPts = [{
23547 x: hpts[0],
23548 y: hpts[1]
23549 }, {
23550 x: hpts[2],
23551 y: hpts[3]
23552 }];
23553 }
23554 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23555 };
23556 BRp$a.recalculateEdgeProjections = function (edges) {
23557 this.findEdgeControlPoints(edges);
23558 };
23559
23560 var BRp$9 = {};
23561 BRp$9.recalculateNodeLabelProjection = function (node) {
23562 var content = node.pstyle('label').strValue;
23563 if (emptyString(content)) {
23564 return;
23565 }
23566 var textX, textY;
23567 var _p = node._private;
23568 var nodeWidth = node.width();
23569 var nodeHeight = node.height();
23570 var padding = node.padding();
23571 var nodePos = node.position();
23572 var textHalign = node.pstyle('text-halign').strValue;
23573 var textValign = node.pstyle('text-valign').strValue;
23574 var rs = _p.rscratch;
23575 var rstyle = _p.rstyle;
23576 switch (textHalign) {
23577 case 'left':
23578 textX = nodePos.x - nodeWidth / 2 - padding;
23579 break;
23580 case 'right':
23581 textX = nodePos.x + nodeWidth / 2 + padding;
23582 break;
23583 default:
23584 // e.g. center
23585 textX = nodePos.x;
23586 }
23587 switch (textValign) {
23588 case 'top':
23589 textY = nodePos.y - nodeHeight / 2 - padding;
23590 break;
23591 case 'bottom':
23592 textY = nodePos.y + nodeHeight / 2 + padding;
23593 break;
23594 default:
23595 // e.g. middle
23596 textY = nodePos.y;
23597 }
23598 rs.labelX = textX;
23599 rs.labelY = textY;
23600 rstyle.labelX = textX;
23601 rstyle.labelY = textY;
23602 this.calculateLabelAngles(node);
23603 this.applyLabelDimensions(node);
23604 };
23605 var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23606 var angle = Math.atan(dy / dx);
23607 if (dx === 0 && angle < 0) {
23608 angle = angle * -1;
23609 }
23610 return angle;
23611 };
23612 var lineAngle = function lineAngle(p0, p1) {
23613 var dx = p1.x - p0.x;
23614 var dy = p1.y - p0.y;
23615 return lineAngleFromDelta(dx, dy);
23616 };
23617 var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23618 var t0 = bound(0, t - 0.001, 1);
23619 var t1 = bound(0, t + 0.001, 1);
23620 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23621 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23622 return lineAngle(lp0, lp1);
23623 };
23624 BRp$9.recalculateEdgeLabelProjections = function (edge) {
23625 var p;
23626 var _p = edge._private;
23627 var rs = _p.rscratch;
23628 var r = this;
23629 var content = {
23630 mid: edge.pstyle('label').strValue,
23631 source: edge.pstyle('source-label').strValue,
23632 target: edge.pstyle('target-label').strValue
23633 };
23634 if (content.mid || content.source || content.target) ; else {
23635 return; // no labels => no calcs
23636 }
23637
23638 // add center point to style so bounding box calculations can use it
23639 //
23640 p = {
23641 x: rs.midX,
23642 y: rs.midY
23643 };
23644 var setRs = function setRs(propName, prefix, value) {
23645 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23646 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23647 };
23648 setRs('labelX', null, p.x);
23649 setRs('labelY', null, p.y);
23650 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23651 setRs('labelAutoAngle', null, midAngle);
23652 var createControlPointInfo = function createControlPointInfo() {
23653 if (createControlPointInfo.cache) {
23654 return createControlPointInfo.cache;
23655 } // use cache so only 1x per edge
23656
23657 var ctrlpts = [];
23658
23659 // store each ctrlpt info init
23660 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23661 var p0 = {
23662 x: rs.allpts[i],
23663 y: rs.allpts[i + 1]
23664 };
23665 var p1 = {
23666 x: rs.allpts[i + 2],
23667 y: rs.allpts[i + 3]
23668 }; // ctrlpt
23669 var p2 = {
23670 x: rs.allpts[i + 4],
23671 y: rs.allpts[i + 5]
23672 };
23673 ctrlpts.push({
23674 p0: p0,
23675 p1: p1,
23676 p2: p2,
23677 startDist: 0,
23678 length: 0,
23679 segments: []
23680 });
23681 }
23682 var bpts = _p.rstyle.bezierPts;
23683 var nProjs = r.bezierProjPcts.length;
23684 function addSegment(cp, p0, p1, t0, t1) {
23685 var length = dist(p0, p1);
23686 var prevSegment = cp.segments[cp.segments.length - 1];
23687 var segment = {
23688 p0: p0,
23689 p1: p1,
23690 t0: t0,
23691 t1: t1,
23692 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23693 length: length
23694 };
23695 cp.segments.push(segment);
23696 cp.length += length;
23697 }
23698
23699 // update each ctrlpt with segment info
23700 for (var _i = 0; _i < ctrlpts.length; _i++) {
23701 var cp = ctrlpts[_i];
23702 var prevCp = ctrlpts[_i - 1];
23703 if (prevCp) {
23704 cp.startDist = prevCp.startDist + prevCp.length;
23705 }
23706 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23707
23708 for (var j = 0; j < nProjs - 1; j++) {
23709 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23710 }
23711 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23712 }
23713
23714 return createControlPointInfo.cache = ctrlpts;
23715 };
23716 var calculateEndProjection = function calculateEndProjection(prefix) {
23717 var angle;
23718 var isSrc = prefix === 'source';
23719 if (!content[prefix]) {
23720 return;
23721 }
23722 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23723 switch (rs.edgeType) {
23724 case 'self':
23725 case 'compound':
23726 case 'bezier':
23727 case 'multibezier':
23728 {
23729 var cps = createControlPointInfo();
23730 var selected;
23731 var startDist = 0;
23732 var totalDist = 0;
23733
23734 // find the segment we're on
23735 for (var i = 0; i < cps.length; i++) {
23736 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23737 for (var j = 0; j < _cp.segments.length; j++) {
23738 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23739 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23740 startDist = totalDist;
23741 totalDist += _seg.length;
23742 if (totalDist >= offset || lastSeg) {
23743 selected = {
23744 cp: _cp,
23745 segment: _seg
23746 };
23747 break;
23748 }
23749 }
23750 if (selected) {
23751 break;
23752 }
23753 }
23754 var cp = selected.cp;
23755 var seg = selected.segment;
23756 var tSegment = (offset - startDist) / seg.length;
23757 var segDt = seg.t1 - seg.t0;
23758 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23759 t = bound(0, t, 1);
23760 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23761 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23762 break;
23763 }
23764 case 'straight':
23765 case 'segments':
23766 case 'haystack':
23767 {
23768 var d = 0,
23769 di,
23770 d0;
23771 var p0, p1;
23772 var l = rs.allpts.length;
23773 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23774 if (isSrc) {
23775 p0 = {
23776 x: rs.allpts[_i2],
23777 y: rs.allpts[_i2 + 1]
23778 };
23779 p1 = {
23780 x: rs.allpts[_i2 + 2],
23781 y: rs.allpts[_i2 + 3]
23782 };
23783 } else {
23784 p0 = {
23785 x: rs.allpts[l - 2 - _i2],
23786 y: rs.allpts[l - 1 - _i2]
23787 };
23788 p1 = {
23789 x: rs.allpts[l - 4 - _i2],
23790 y: rs.allpts[l - 3 - _i2]
23791 };
23792 }
23793 di = dist(p0, p1);
23794 d0 = d;
23795 d += di;
23796 if (d >= offset) {
23797 break;
23798 }
23799 }
23800 var pD = offset - d0;
23801 var _t = pD / di;
23802 _t = bound(0, _t, 1);
23803 p = lineAt(p0, p1, _t);
23804 angle = lineAngle(p0, p1);
23805 break;
23806 }
23807 }
23808 setRs('labelX', prefix, p.x);
23809 setRs('labelY', prefix, p.y);
23810 setRs('labelAutoAngle', prefix, angle);
23811 };
23812 calculateEndProjection('source');
23813 calculateEndProjection('target');
23814 this.applyLabelDimensions(edge);
23815 };
23816 BRp$9.applyLabelDimensions = function (ele) {
23817 this.applyPrefixedLabelDimensions(ele);
23818 if (ele.isEdge()) {
23819 this.applyPrefixedLabelDimensions(ele, 'source');
23820 this.applyPrefixedLabelDimensions(ele, 'target');
23821 }
23822 };
23823 BRp$9.applyPrefixedLabelDimensions = function (ele, prefix) {
23824 var _p = ele._private;
23825 var text = this.getLabelText(ele, prefix);
23826 var labelDims = this.calculateLabelDimensions(ele, text);
23827 var lineHeight = ele.pstyle('line-height').pfValue;
23828 var textWrap = ele.pstyle('text-wrap').strValue;
23829 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23830 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23831 var normPerLineHeight = labelDims.height / numLines;
23832 var labelLineHeight = normPerLineHeight * lineHeight;
23833 var width = labelDims.width;
23834 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23835 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23836 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23837 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23838 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23839 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23840 };
23841 BRp$9.getLabelText = function (ele, prefix) {
23842 var _p = ele._private;
23843 var pfd = prefix ? prefix + '-' : '';
23844 var text = ele.pstyle(pfd + 'label').strValue;
23845 var textTransform = ele.pstyle('text-transform').value;
23846 var rscratch = function rscratch(propName, value) {
23847 if (value) {
23848 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23849 return value;
23850 } else {
23851 return getPrefixedProperty(_p.rscratch, propName, prefix);
23852 }
23853 };
23854
23855 // for empty text, skip all processing
23856 if (!text) {
23857 return '';
23858 }
23859 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23860 text = text.toUpperCase();
23861 } else if (textTransform == 'lowercase') {
23862 text = text.toLowerCase();
23863 }
23864 var wrapStyle = ele.pstyle('text-wrap').value;
23865 if (wrapStyle === 'wrap') {
23866 var labelKey = rscratch('labelKey');
23867
23868 // save recalc if the label is the same as before
23869 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23870 return rscratch('labelWrapCachedText');
23871 }
23872 var zwsp = "\u200B";
23873 var lines = text.split('\n');
23874 var maxW = ele.pstyle('text-max-width').pfValue;
23875 var overflow = ele.pstyle('text-overflow-wrap').value;
23876 var overflowAny = overflow === 'anywhere';
23877 var wrappedLines = [];
23878 var separatorRegex = /[\s\u200b]+|$/g; // Include end of string to add last word
23879
23880 for (var l = 0; l < lines.length; l++) {
23881 var line = lines[l];
23882 var lineDims = this.calculateLabelDimensions(ele, line);
23883 var lineW = lineDims.width;
23884 if (overflowAny) {
23885 var processedLine = line.split('').join(zwsp);
23886 line = processedLine;
23887 }
23888 if (lineW > maxW) {
23889 // line is too long
23890 var separatorMatches = line.matchAll(separatorRegex);
23891 var subline = '';
23892 var previousIndex = 0;
23893 // Add fake match
23894 var _iterator = _createForOfIteratorHelper(separatorMatches),
23895 _step;
23896 try {
23897 for (_iterator.s(); !(_step = _iterator.n()).done;) {
23898 var separatorMatch = _step.value;
23899 var wordSeparator = separatorMatch[0];
23900 var word = line.substring(previousIndex, separatorMatch.index);
23901 previousIndex = separatorMatch.index + wordSeparator.length;
23902 var testLine = subline.length === 0 ? word : subline + word + wordSeparator;
23903 var testDims = this.calculateLabelDimensions(ele, testLine);
23904 var testW = testDims.width;
23905 if (testW <= maxW) {
23906 // word fits on current line
23907 subline += word + wordSeparator;
23908 } else {
23909 // word starts new line
23910 if (subline) {
23911 wrappedLines.push(subline);
23912 }
23913 subline = word + wordSeparator;
23914 }
23915 }
23916
23917 // if there's remaining text, put it in a wrapped line
23918 } catch (err) {
23919 _iterator.e(err);
23920 } finally {
23921 _iterator.f();
23922 }
23923 if (!subline.match(/^[\s\u200b]+$/)) {
23924 wrappedLines.push(subline);
23925 }
23926 } else {
23927 // line is already short enough
23928 wrappedLines.push(line);
23929 }
23930 } // for
23931
23932 rscratch('labelWrapCachedLines', wrappedLines);
23933 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23934 rscratch('labelWrapKey', labelKey);
23935 } else if (wrapStyle === 'ellipsis') {
23936 var _maxW = ele.pstyle('text-max-width').pfValue;
23937 var ellipsized = '';
23938 var ellipsis = "\u2026";
23939 var incLastCh = false;
23940 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23941 // the label already fits
23942 return text;
23943 }
23944 for (var i = 0; i < text.length; i++) {
23945 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23946 if (widthWithNextCh > _maxW) {
23947 break;
23948 }
23949 ellipsized += text[i];
23950 if (i === text.length - 1) {
23951 incLastCh = true;
23952 }
23953 }
23954 if (!incLastCh) {
23955 ellipsized += ellipsis;
23956 }
23957 return ellipsized;
23958 } // if ellipsize
23959
23960 return text;
23961 };
23962 BRp$9.getLabelJustification = function (ele) {
23963 var justification = ele.pstyle('text-justification').strValue;
23964 var textHalign = ele.pstyle('text-halign').strValue;
23965 if (justification === 'auto') {
23966 if (ele.isNode()) {
23967 switch (textHalign) {
23968 case 'left':
23969 return 'right';
23970 case 'right':
23971 return 'left';
23972 default:
23973 return 'center';
23974 }
23975 } else {
23976 return 'center';
23977 }
23978 } else {
23979 return justification;
23980 }
23981 };
23982 BRp$9.calculateLabelDimensions = function (ele, text) {
23983 var r = this;
23984 var containerWindow = r.cy.window();
23985 var document = containerWindow.document;
23986 var cacheKey = hashString(text, ele._private.labelDimsKey);
23987 var cache = r.labelDimCache || (r.labelDimCache = []);
23988 var existingVal = cache[cacheKey];
23989 if (existingVal != null) {
23990 return existingVal;
23991 }
23992 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23993 var fStyle = ele.pstyle('font-style').strValue;
23994 var size = ele.pstyle('font-size').pfValue;
23995 var family = ele.pstyle('font-family').strValue;
23996 var weight = ele.pstyle('font-weight').strValue;
23997 var canvas = this.labelCalcCanvas;
23998 var c2d = this.labelCalcCanvasContext;
23999 if (!canvas) {
24000 canvas = this.labelCalcCanvas = document.createElement('canvas');
24001 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
24002 var ds = canvas.style;
24003 ds.position = 'absolute';
24004 ds.left = '-9999px';
24005 ds.top = '-9999px';
24006 ds.zIndex = '-1';
24007 ds.visibility = 'hidden';
24008 ds.pointerEvents = 'none';
24009 }
24010 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
24011 var width = 0;
24012 var height = 0;
24013 var lines = text.split('\n');
24014 for (var i = 0; i < lines.length; i++) {
24015 var line = lines[i];
24016 var metrics = c2d.measureText(line);
24017 var w = Math.ceil(metrics.width);
24018 var h = size;
24019 width = Math.max(w, width);
24020 height += h;
24021 }
24022 width += padding;
24023 height += padding;
24024 return cache[cacheKey] = {
24025 width: width,
24026 height: height
24027 };
24028 };
24029 BRp$9.calculateLabelAngle = function (ele, prefix) {
24030 var _p = ele._private;
24031 var rs = _p.rscratch;
24032 var isEdge = ele.isEdge();
24033 var prefixDash = prefix ? prefix + '-' : '';
24034 var rot = ele.pstyle(prefixDash + 'text-rotation');
24035 var rotStr = rot.strValue;
24036 if (rotStr === 'none') {
24037 return 0;
24038 } else if (isEdge && rotStr === 'autorotate') {
24039 return rs.labelAutoAngle;
24040 } else if (rotStr === 'autorotate') {
24041 return 0;
24042 } else {
24043 return rot.pfValue;
24044 }
24045 };
24046 BRp$9.calculateLabelAngles = function (ele) {
24047 var r = this;
24048 var isEdge = ele.isEdge();
24049 var _p = ele._private;
24050 var rs = _p.rscratch;
24051 rs.labelAngle = r.calculateLabelAngle(ele);
24052 if (isEdge) {
24053 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
24054 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
24055 }
24056 };
24057
24058 var BRp$8 = {};
24059 var TOO_SMALL_CUT_RECT = 28;
24060 var warnedCutRect = false;
24061 BRp$8.getNodeShape = function (node) {
24062 var r = this;
24063 var shape = node.pstyle('shape').value;
24064 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
24065 if (!warnedCutRect) {
24066 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
24067 warnedCutRect = true;
24068 }
24069 return 'rectangle';
24070 }
24071 if (node.isParent()) {
24072 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
24073 return shape;
24074 } else {
24075 return 'rectangle';
24076 }
24077 }
24078 if (shape === 'polygon') {
24079 var points = node.pstyle('shape-polygon-points').value;
24080 return r.nodeShapes.makePolygon(points).name;
24081 }
24082 return shape;
24083 };
24084
24085 var BRp$7 = {};
24086 BRp$7.registerCalculationListeners = function () {
24087 var cy = this.cy;
24088 var elesToUpdate = cy.collection();
24089 var r = this;
24090 var enqueue = function enqueue(eles) {
24091 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
24092 elesToUpdate.merge(eles);
24093 if (dirtyStyleCaches) {
24094 for (var i = 0; i < eles.length; i++) {
24095 var ele = eles[i];
24096 var _p = ele._private;
24097 var rstyle = _p.rstyle;
24098 rstyle.clean = false;
24099 rstyle.cleanConnected = false;
24100 }
24101 }
24102 };
24103 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
24104 var ele = e.target;
24105 enqueue(ele);
24106 }).on('style.* background.*', function onDirtyStyle(e) {
24107 var ele = e.target;
24108 enqueue(ele, false);
24109 });
24110 var updateEleCalcs = function updateEleCalcs(willDraw) {
24111 if (willDraw) {
24112 var fns = r.onUpdateEleCalcsFns;
24113
24114 // because we need to have up-to-date style (e.g. stylesheet mappers)
24115 // before calculating rendered style (and pstyle might not be called yet)
24116 elesToUpdate.cleanStyle();
24117 for (var i = 0; i < elesToUpdate.length; i++) {
24118 var ele = elesToUpdate[i];
24119 var rstyle = ele._private.rstyle;
24120 if (ele.isNode() && !rstyle.cleanConnected) {
24121 enqueue(ele.connectedEdges());
24122 rstyle.cleanConnected = true;
24123 }
24124 }
24125 if (fns) {
24126 for (var _i = 0; _i < fns.length; _i++) {
24127 var fn = fns[_i];
24128 fn(willDraw, elesToUpdate);
24129 }
24130 }
24131 r.recalculateRenderedStyle(elesToUpdate);
24132 elesToUpdate = cy.collection();
24133 }
24134 };
24135 r.flushRenderedStyleQueue = function () {
24136 updateEleCalcs(true);
24137 };
24138 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
24139 };
24140 BRp$7.onUpdateEleCalcs = function (fn) {
24141 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
24142 fns.push(fn);
24143 };
24144 BRp$7.recalculateRenderedStyle = function (eles, useCache) {
24145 var isCleanConnected = function isCleanConnected(ele) {
24146 return ele._private.rstyle.cleanConnected;
24147 };
24148 var edges = [];
24149 var nodes = [];
24150
24151 // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
24152 if (this.destroyed) {
24153 return;
24154 }
24155
24156 // use cache by default for perf
24157 if (useCache === undefined) {
24158 useCache = true;
24159 }
24160 for (var i = 0; i < eles.length; i++) {
24161 var ele = eles[i];
24162 var _p = ele._private;
24163 var rstyle = _p.rstyle;
24164
24165 // an edge may be implicitly dirty b/c of one of its connected nodes
24166 // (and a request for recalc may come in between frames)
24167 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
24168 rstyle.clean = false;
24169 }
24170
24171 // only update if dirty and in graph
24172 if (useCache && rstyle.clean || ele.removed()) {
24173 continue;
24174 }
24175
24176 // only update if not display: none
24177 if (ele.pstyle('display').value === 'none') {
24178 continue;
24179 }
24180 if (_p.group === 'nodes') {
24181 nodes.push(ele);
24182 } else {
24183 // edges
24184 edges.push(ele);
24185 }
24186 rstyle.clean = true;
24187 }
24188
24189 // update node data from projections
24190 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
24191 var _ele = nodes[_i2];
24192 var _p2 = _ele._private;
24193 var _rstyle = _p2.rstyle;
24194 var pos = _ele.position();
24195 this.recalculateNodeLabelProjection(_ele);
24196 _rstyle.nodeX = pos.x;
24197 _rstyle.nodeY = pos.y;
24198 _rstyle.nodeW = _ele.pstyle('width').pfValue;
24199 _rstyle.nodeH = _ele.pstyle('height').pfValue;
24200 }
24201 this.recalculateEdgeProjections(edges);
24202
24203 // update edge data from projections
24204 for (var _i3 = 0; _i3 < edges.length; _i3++) {
24205 var _ele2 = edges[_i3];
24206 var _p3 = _ele2._private;
24207 var _rstyle2 = _p3.rstyle;
24208 var rs = _p3.rscratch;
24209
24210 // update rstyle positions
24211 _rstyle2.srcX = rs.arrowStartX;
24212 _rstyle2.srcY = rs.arrowStartY;
24213 _rstyle2.tgtX = rs.arrowEndX;
24214 _rstyle2.tgtY = rs.arrowEndY;
24215 _rstyle2.midX = rs.midX;
24216 _rstyle2.midY = rs.midY;
24217 _rstyle2.labelAngle = rs.labelAngle;
24218 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
24219 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
24220 }
24221 };
24222
24223 var BRp$6 = {};
24224 BRp$6.updateCachedGrabbedEles = function () {
24225 var eles = this.cachedZSortedEles;
24226 if (!eles) {
24227 // just let this be recalculated on the next z sort tick
24228 return;
24229 }
24230 eles.drag = [];
24231 eles.nondrag = [];
24232 var grabTargets = [];
24233 for (var i = 0; i < eles.length; i++) {
24234 var ele = eles[i];
24235 var rs = ele._private.rscratch;
24236 if (ele.grabbed() && !ele.isParent()) {
24237 grabTargets.push(ele);
24238 } else if (rs.inDragLayer) {
24239 eles.drag.push(ele);
24240 } else {
24241 eles.nondrag.push(ele);
24242 }
24243 }
24244
24245 // put the grab target nodes last so it's on top of its neighbourhood
24246 for (var i = 0; i < grabTargets.length; i++) {
24247 var ele = grabTargets[i];
24248 eles.drag.push(ele);
24249 }
24250 };
24251 BRp$6.invalidateCachedZSortedEles = function () {
24252 this.cachedZSortedEles = null;
24253 };
24254 BRp$6.getCachedZSortedEles = function (forceRecalc) {
24255 if (forceRecalc || !this.cachedZSortedEles) {
24256 var eles = this.cy.mutableElements().toArray();
24257 eles.sort(zIndexSort);
24258 eles.interactive = eles.filter(function (ele) {
24259 return ele.interactive();
24260 });
24261 this.cachedZSortedEles = eles;
24262 this.updateCachedGrabbedEles();
24263 } else {
24264 eles = this.cachedZSortedEles;
24265 }
24266 return eles;
24267 };
24268
24269 var BRp$5 = {};
24270 [BRp$e, BRp$d, BRp$c, BRp$b, BRp$a, BRp$9, BRp$8, BRp$7, BRp$6].forEach(function (props) {
24271 extend(BRp$5, props);
24272 });
24273
24274 var BRp$4 = {};
24275 BRp$4.getCachedImage = function (url, crossOrigin, onLoad) {
24276 var r = this;
24277 var imageCache = r.imageCache = r.imageCache || {};
24278 var cache = imageCache[url];
24279 if (cache) {
24280 if (!cache.image.complete) {
24281 cache.image.addEventListener('load', onLoad);
24282 }
24283 return cache.image;
24284 } else {
24285 cache = imageCache[url] = imageCache[url] || {};
24286 var image = cache.image = new Image(); // eslint-disable-line no-undef
24287
24288 image.addEventListener('load', onLoad);
24289 image.addEventListener('error', function () {
24290 image.error = true;
24291 });
24292
24293 // #1582 safari doesn't load data uris with crossOrigin properly
24294 // https://bugs.webkit.org/show_bug.cgi?id=123978
24295 var dataUriPrefix = 'data:';
24296 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24297 if (!isDataUri) {
24298 // if crossorigin is 'null'(stringified), then manually set it to null
24299 crossOrigin = crossOrigin === 'null' ? null : crossOrigin;
24300 image.crossOrigin = crossOrigin; // prevent tainted canvas
24301 }
24302
24303 image.src = url;
24304 return image;
24305 }
24306 };
24307
24308 var BRp$3 = {};
24309
24310 /* global document, ResizeObserver, MutationObserver */
24311
24312 BRp$3.registerBinding = function (target, event, handler, useCapture) {
24313 // eslint-disable-line no-unused-vars
24314 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24315 var b = this.binder(target);
24316 return b.on.apply(b, args);
24317 };
24318 BRp$3.binder = function (tgt) {
24319 var r = this;
24320 var containerWindow = r.cy.window();
24321 var tgtIsDom = tgt === containerWindow || tgt === containerWindow.document || tgt === containerWindow.document.body || domElement(tgt);
24322 if (r.supportsPassiveEvents == null) {
24323 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24324 var supportsPassive = false;
24325 try {
24326 var opts = Object.defineProperty({}, 'passive', {
24327 get: function get() {
24328 supportsPassive = true;
24329 return true;
24330 }
24331 });
24332 containerWindow.addEventListener('test', null, opts);
24333 } catch (err) {
24334 // not supported
24335 }
24336 r.supportsPassiveEvents = supportsPassive;
24337 }
24338 var on = function on(event, handler, useCapture) {
24339 var args = Array.prototype.slice.call(arguments);
24340 if (tgtIsDom && r.supportsPassiveEvents) {
24341 // replace useCapture w/ opts obj
24342 args[2] = {
24343 capture: useCapture != null ? useCapture : false,
24344 passive: false,
24345 once: false
24346 };
24347 }
24348 r.bindings.push({
24349 target: tgt,
24350 args: args
24351 });
24352 (tgt.addEventListener || tgt.on).apply(tgt, args);
24353 return this;
24354 };
24355 return {
24356 on: on,
24357 addEventListener: on,
24358 addListener: on,
24359 bind: on
24360 };
24361 };
24362 BRp$3.nodeIsDraggable = function (node) {
24363 return node && node.isNode() && !node.locked() && node.grabbable();
24364 };
24365 BRp$3.nodeIsGrabbable = function (node) {
24366 return this.nodeIsDraggable(node) && node.interactive();
24367 };
24368 BRp$3.load = function () {
24369 var r = this;
24370 var containerWindow = r.cy.window();
24371 var isSelected = function isSelected(ele) {
24372 return ele.selected();
24373 };
24374 var triggerEvents = function triggerEvents(target, names, e, position) {
24375 if (target == null) {
24376 target = r.cy;
24377 }
24378 for (var i = 0; i < names.length; i++) {
24379 var name = names[i];
24380 target.emit({
24381 originalEvent: e,
24382 type: name,
24383 position: position
24384 });
24385 }
24386 };
24387 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24388 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24389 };
24390
24391 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24392 var allowPassthrough = true;
24393 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24394 // a grabbable compound node below the ele => no passthrough panning
24395 for (var i = 0; downs && i < downs.length; i++) {
24396 var down = downs[i];
24397
24398 //if any parent node in event hierarchy isn't pannable, reject passthrough
24399 if (down.isNode() && down.isParent() && !down.pannable()) {
24400 allowPassthrough = false;
24401 break;
24402 }
24403 }
24404 } else {
24405 allowPassthrough = true;
24406 }
24407 return allowPassthrough;
24408 };
24409 var setGrabbed = function setGrabbed(ele) {
24410 ele[0]._private.grabbed = true;
24411 };
24412 var setFreed = function setFreed(ele) {
24413 ele[0]._private.grabbed = false;
24414 };
24415 var setInDragLayer = function setInDragLayer(ele) {
24416 ele[0]._private.rscratch.inDragLayer = true;
24417 };
24418 var setOutDragLayer = function setOutDragLayer(ele) {
24419 ele[0]._private.rscratch.inDragLayer = false;
24420 };
24421 var setGrabTarget = function setGrabTarget(ele) {
24422 ele[0]._private.rscratch.isGrabTarget = true;
24423 };
24424 var removeGrabTarget = function removeGrabTarget(ele) {
24425 ele[0]._private.rscratch.isGrabTarget = false;
24426 };
24427 var addToDragList = function addToDragList(ele, opts) {
24428 var list = opts.addToList;
24429 var listHasEle = list.has(ele);
24430 if (!listHasEle && ele.grabbable() && !ele.locked()) {
24431 list.merge(ele);
24432 setGrabbed(ele);
24433 }
24434 };
24435
24436 // helper function to determine which child nodes and inner edges
24437 // of a compound node to be dragged as well as the grabbed and selected nodes
24438 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24439 if (!node.cy().hasCompoundNodes()) {
24440 return;
24441 }
24442 if (opts.inDragLayer == null && opts.addToList == null) {
24443 return;
24444 } // nothing to do
24445
24446 var innerNodes = node.descendants();
24447 if (opts.inDragLayer) {
24448 innerNodes.forEach(setInDragLayer);
24449 innerNodes.connectedEdges().forEach(setInDragLayer);
24450 }
24451 if (opts.addToList) {
24452 addToDragList(innerNodes, opts);
24453 }
24454 };
24455
24456 // adds the given nodes and its neighbourhood to the drag layer
24457 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24458 opts = opts || {};
24459 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24460 if (opts.inDragLayer) {
24461 nodes.forEach(setInDragLayer);
24462 nodes.neighborhood().stdFilter(function (ele) {
24463 return !hasCompoundNodes || ele.isEdge();
24464 }).forEach(setInDragLayer);
24465 }
24466 if (opts.addToList) {
24467 nodes.forEach(function (ele) {
24468 addToDragList(ele, opts);
24469 });
24470 }
24471 addDescendantsToDrag(nodes, opts); // always add to drag
24472
24473 // also add nodes and edges related to the topmost ancestor
24474 updateAncestorsInDragLayer(nodes, {
24475 inDragLayer: opts.inDragLayer
24476 });
24477 r.updateCachedGrabbedEles();
24478 };
24479 var addNodeToDrag = addNodesToDrag;
24480 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24481 if (!grabbedEles) {
24482 return;
24483 }
24484
24485 // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24486 r.getCachedZSortedEles().forEach(function (ele) {
24487 setFreed(ele);
24488 setOutDragLayer(ele);
24489 removeGrabTarget(ele);
24490 });
24491 r.updateCachedGrabbedEles();
24492 };
24493
24494 // helper function to determine which ancestor nodes and edges should go
24495 // to the drag layer (or should be removed from drag layer).
24496 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24497 if (opts.inDragLayer == null && opts.addToList == null) {
24498 return;
24499 } // nothing to do
24500
24501 if (!node.cy().hasCompoundNodes()) {
24502 return;
24503 }
24504
24505 // find top-level parent
24506 var parent = node.ancestors().orphans();
24507
24508 // no parent node: no nodes to add to the drag layer
24509 if (parent.same(node)) {
24510 return;
24511 }
24512 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24513 var edges = nodes.connectedEdges();
24514 if (opts.inDragLayer) {
24515 edges.forEach(setInDragLayer);
24516 nodes.forEach(setInDragLayer);
24517 }
24518 if (opts.addToList) {
24519 nodes.forEach(function (ele) {
24520 addToDragList(ele, opts);
24521 });
24522 }
24523 };
24524 var blurActiveDomElement = function blurActiveDomElement() {
24525 if (document.activeElement != null && document.activeElement.blur != null) {
24526 document.activeElement.blur();
24527 }
24528 };
24529 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24530 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined';
24531
24532 // watch for when the cy container is removed from the dom
24533 if (haveMutationsApi) {
24534 r.removeObserver = new MutationObserver(function (mutns) {
24535 // eslint-disable-line no-undef
24536 for (var i = 0; i < mutns.length; i++) {
24537 var mutn = mutns[i];
24538 var rNodes = mutn.removedNodes;
24539 if (rNodes) {
24540 for (var j = 0; j < rNodes.length; j++) {
24541 var rNode = rNodes[j];
24542 if (rNode === r.container) {
24543 r.destroy();
24544 break;
24545 }
24546 }
24547 }
24548 }
24549 });
24550 if (r.container.parentNode) {
24551 r.removeObserver.observe(r.container.parentNode, {
24552 childList: true
24553 });
24554 }
24555 } else {
24556 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24557 // eslint-disable-line no-unused-vars
24558 r.destroy();
24559 });
24560 }
24561 var onResize = debounce_1(function () {
24562 r.cy.resize();
24563 }, 100);
24564 if (haveMutationsApi) {
24565 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24566
24567 r.styleObserver.observe(r.container, {
24568 attributes: true
24569 });
24570 }
24571
24572 // auto resize
24573 r.registerBinding(containerWindow, 'resize', onResize); // eslint-disable-line no-undef
24574
24575 if (haveResizeObserverApi) {
24576 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24577
24578 r.resizeObserver.observe(r.container);
24579 }
24580 var forEachUp = function forEachUp(domEle, fn) {
24581 while (domEle != null) {
24582 fn(domEle);
24583 domEle = domEle.parentNode;
24584 }
24585 };
24586 var invalidateCoords = function invalidateCoords() {
24587 r.invalidateContainerClientCoordsCache();
24588 };
24589 forEachUp(r.container, function (domEle) {
24590 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24591 r.registerBinding(domEle, 'animationend', invalidateCoords);
24592 r.registerBinding(domEle, 'scroll', invalidateCoords);
24593 });
24594
24595 // stop right click menu from appearing on cy
24596 r.registerBinding(r.container, 'contextmenu', function (e) {
24597 e.preventDefault();
24598 });
24599 var inBoxSelection = function inBoxSelection() {
24600 return r.selection[4] !== 0;
24601 };
24602 var eventInContainer = function eventInContainer(e) {
24603 // save cycles if mouse events aren't to be captured
24604 var containerPageCoords = r.findContainerClientCoords();
24605 var x = containerPageCoords[0];
24606 var y = containerPageCoords[1];
24607 var width = containerPageCoords[2];
24608 var height = containerPageCoords[3];
24609 var positions = e.touches ? e.touches : [e];
24610 var atLeastOnePosInside = false;
24611 for (var i = 0; i < positions.length; i++) {
24612 var p = positions[i];
24613 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24614 atLeastOnePosInside = true;
24615 break;
24616 }
24617 }
24618 if (!atLeastOnePosInside) {
24619 return false;
24620 }
24621 var container = r.container;
24622 var target = e.target;
24623 var tParent = target.parentNode;
24624 var containerIsTarget = false;
24625 while (tParent) {
24626 if (tParent === container) {
24627 containerIsTarget = true;
24628 break;
24629 }
24630 tParent = tParent.parentNode;
24631 }
24632 if (!containerIsTarget) {
24633 return false;
24634 } // if target is outisde cy container, then this event is not for us
24635
24636 return true;
24637 };
24638
24639 // Primary key
24640 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24641 if (!eventInContainer(e)) {
24642 return;
24643 }
24644
24645 // during left mouse button gestures, ignore other buttons
24646 if (r.hoverData.which === 1 && e.which !== 1) {
24647 return;
24648 }
24649 e.preventDefault();
24650 blurActiveDomElement();
24651 r.hoverData.capture = true;
24652 r.hoverData.which = e.which;
24653 var cy = r.cy;
24654 var gpos = [e.clientX, e.clientY];
24655 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24656 var select = r.selection;
24657 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24658 var near = nears[0];
24659 var draggedElements = r.dragData.possibleDragElements;
24660 r.hoverData.mdownPos = pos;
24661 r.hoverData.mdownGPos = gpos;
24662 var checkForTaphold = function checkForTaphold() {
24663 r.hoverData.tapholdCancelled = false;
24664 clearTimeout(r.hoverData.tapholdTimeout);
24665 r.hoverData.tapholdTimeout = setTimeout(function () {
24666 if (r.hoverData.tapholdCancelled) {
24667 return;
24668 } else {
24669 var ele = r.hoverData.down;
24670 if (ele) {
24671 ele.emit({
24672 originalEvent: e,
24673 type: 'taphold',
24674 position: {
24675 x: pos[0],
24676 y: pos[1]
24677 }
24678 });
24679 } else {
24680 cy.emit({
24681 originalEvent: e,
24682 type: 'taphold',
24683 position: {
24684 x: pos[0],
24685 y: pos[1]
24686 }
24687 });
24688 }
24689 }
24690 }, r.tapholdDuration);
24691 };
24692
24693 // Right click button
24694 if (e.which == 3) {
24695 r.hoverData.cxtStarted = true;
24696 var cxtEvt = {
24697 originalEvent: e,
24698 type: 'cxttapstart',
24699 position: {
24700 x: pos[0],
24701 y: pos[1]
24702 }
24703 };
24704 if (near) {
24705 near.activate();
24706 near.emit(cxtEvt);
24707 r.hoverData.down = near;
24708 } else {
24709 cy.emit(cxtEvt);
24710 }
24711 r.hoverData.downTime = new Date().getTime();
24712 r.hoverData.cxtDragged = false;
24713
24714 // Primary button
24715 } else if (e.which == 1) {
24716 if (near) {
24717 near.activate();
24718 }
24719
24720 // Element dragging
24721 {
24722 // If something is under the cursor and it is draggable, prepare to grab it
24723 if (near != null) {
24724 if (r.nodeIsGrabbable(near)) {
24725 var makeEvent = function makeEvent(type) {
24726 return {
24727 originalEvent: e,
24728 type: type,
24729 position: {
24730 x: pos[0],
24731 y: pos[1]
24732 }
24733 };
24734 };
24735 var triggerGrab = function triggerGrab(ele) {
24736 ele.emit(makeEvent('grab'));
24737 };
24738 setGrabTarget(near);
24739 if (!near.selected()) {
24740 draggedElements = r.dragData.possibleDragElements = cy.collection();
24741 addNodeToDrag(near, {
24742 addToList: draggedElements
24743 });
24744 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24745 } else {
24746 draggedElements = r.dragData.possibleDragElements = cy.collection();
24747 var selectedNodes = cy.$(function (ele) {
24748 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24749 });
24750 addNodesToDrag(selectedNodes, {
24751 addToList: draggedElements
24752 });
24753 near.emit(makeEvent('grabon'));
24754 selectedNodes.forEach(triggerGrab);
24755 }
24756 r.redrawHint('eles', true);
24757 r.redrawHint('drag', true);
24758 }
24759 }
24760 r.hoverData.down = near;
24761 r.hoverData.downs = nears;
24762 r.hoverData.downTime = new Date().getTime();
24763 }
24764 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24765 x: pos[0],
24766 y: pos[1]
24767 });
24768 if (near == null) {
24769 select[4] = 1;
24770 r.data.bgActivePosistion = {
24771 x: pos[0],
24772 y: pos[1]
24773 };
24774 r.redrawHint('select', true);
24775 r.redraw();
24776 } else if (near.pannable()) {
24777 select[4] = 1; // for future pan
24778 }
24779
24780 checkForTaphold();
24781 }
24782
24783 // Initialize selection box coordinates
24784 select[0] = select[2] = pos[0];
24785 select[1] = select[3] = pos[1];
24786 }, false);
24787 r.registerBinding(containerWindow, 'mousemove', function mousemoveHandler(e) {
24788 // eslint-disable-line no-undef
24789 var capture = r.hoverData.capture;
24790 if (!capture && !eventInContainer(e)) {
24791 return;
24792 }
24793 var preventDefault = false;
24794 var cy = r.cy;
24795 var zoom = cy.zoom();
24796 var gpos = [e.clientX, e.clientY];
24797 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24798 var mdownPos = r.hoverData.mdownPos;
24799 var mdownGPos = r.hoverData.mdownGPos;
24800 var select = r.selection;
24801 var near = null;
24802 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24803 near = r.findNearestElement(pos[0], pos[1], true, false);
24804 }
24805 var last = r.hoverData.last;
24806 var down = r.hoverData.down;
24807 var disp = [pos[0] - select[2], pos[1] - select[3]];
24808 var draggedElements = r.dragData.possibleDragElements;
24809 var isOverThresholdDrag;
24810 if (mdownGPos) {
24811 var dx = gpos[0] - mdownGPos[0];
24812 var dx2 = dx * dx;
24813 var dy = gpos[1] - mdownGPos[1];
24814 var dy2 = dy * dy;
24815 var dist2 = dx2 + dy2;
24816 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24817 }
24818 var multSelKeyDown = isMultSelKeyDown(e);
24819 if (isOverThresholdDrag) {
24820 r.hoverData.tapholdCancelled = true;
24821 }
24822 var updateDragDelta = function updateDragDelta() {
24823 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24824 if (dragDelta.length === 0) {
24825 dragDelta.push(disp[0]);
24826 dragDelta.push(disp[1]);
24827 } else {
24828 dragDelta[0] += disp[0];
24829 dragDelta[1] += disp[1];
24830 }
24831 };
24832 preventDefault = true;
24833 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24834 x: pos[0],
24835 y: pos[1]
24836 });
24837 var goIntoBoxMode = function goIntoBoxMode() {
24838 r.data.bgActivePosistion = undefined;
24839 if (!r.hoverData.selecting) {
24840 cy.emit({
24841 originalEvent: e,
24842 type: 'boxstart',
24843 position: {
24844 x: pos[0],
24845 y: pos[1]
24846 }
24847 });
24848 }
24849 select[4] = 1;
24850 r.hoverData.selecting = true;
24851 r.redrawHint('select', true);
24852 r.redraw();
24853 };
24854
24855 // trigger context drag if rmouse down
24856 if (r.hoverData.which === 3) {
24857 // but only if over threshold
24858 if (isOverThresholdDrag) {
24859 var cxtEvt = {
24860 originalEvent: e,
24861 type: 'cxtdrag',
24862 position: {
24863 x: pos[0],
24864 y: pos[1]
24865 }
24866 };
24867 if (down) {
24868 down.emit(cxtEvt);
24869 } else {
24870 cy.emit(cxtEvt);
24871 }
24872 r.hoverData.cxtDragged = true;
24873 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24874 if (r.hoverData.cxtOver) {
24875 r.hoverData.cxtOver.emit({
24876 originalEvent: e,
24877 type: 'cxtdragout',
24878 position: {
24879 x: pos[0],
24880 y: pos[1]
24881 }
24882 });
24883 }
24884 r.hoverData.cxtOver = near;
24885 if (near) {
24886 near.emit({
24887 originalEvent: e,
24888 type: 'cxtdragover',
24889 position: {
24890 x: pos[0],
24891 y: pos[1]
24892 }
24893 });
24894 }
24895 }
24896 }
24897
24898 // Check if we are drag panning the entire graph
24899 } else if (r.hoverData.dragging) {
24900 preventDefault = true;
24901 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24902 var deltaP;
24903 if (r.hoverData.justStartedPan) {
24904 var mdPos = r.hoverData.mdownPos;
24905 deltaP = {
24906 x: (pos[0] - mdPos[0]) * zoom,
24907 y: (pos[1] - mdPos[1]) * zoom
24908 };
24909 r.hoverData.justStartedPan = false;
24910 } else {
24911 deltaP = {
24912 x: disp[0] * zoom,
24913 y: disp[1] * zoom
24914 };
24915 }
24916 cy.panBy(deltaP);
24917 cy.emit('dragpan');
24918 r.hoverData.dragged = true;
24919 }
24920
24921 // Needs reproject due to pan changing viewport
24922 pos = r.projectIntoViewport(e.clientX, e.clientY);
24923
24924 // Checks primary button down & out of time & mouse not moved much
24925 } else if (select[4] == 1 && (down == null || down.pannable())) {
24926 if (isOverThresholdDrag) {
24927 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24928 goIntoBoxMode();
24929 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24930 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24931 if (allowPassthrough) {
24932 r.hoverData.dragging = true;
24933 r.hoverData.justStartedPan = true;
24934 select[4] = 0;
24935 r.data.bgActivePosistion = array2point(mdownPos);
24936 r.redrawHint('select', true);
24937 r.redraw();
24938 }
24939 }
24940 if (down && down.pannable() && down.active()) {
24941 down.unactivate();
24942 }
24943 }
24944 } else {
24945 if (down && down.pannable() && down.active()) {
24946 down.unactivate();
24947 }
24948 if ((!down || !down.grabbed()) && near != last) {
24949 if (last) {
24950 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24951 x: pos[0],
24952 y: pos[1]
24953 });
24954 }
24955 if (near) {
24956 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24957 x: pos[0],
24958 y: pos[1]
24959 });
24960 }
24961 r.hoverData.last = near;
24962 }
24963 if (down) {
24964 if (isOverThresholdDrag) {
24965 // then we can take action
24966
24967 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24968 // then selection overrides
24969 if (down && down.grabbed()) {
24970 freeDraggedElements(draggedElements);
24971 down.emit('freeon');
24972 draggedElements.emit('free');
24973 if (r.dragData.didDrag) {
24974 down.emit('dragfreeon');
24975 draggedElements.emit('dragfree');
24976 }
24977 }
24978 goIntoBoxMode();
24979 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24980 // drag node
24981 var justStartedDrag = !r.dragData.didDrag;
24982 if (justStartedDrag) {
24983 r.redrawHint('eles', true);
24984 }
24985 r.dragData.didDrag = true; // indicate that we actually did drag the node
24986
24987 // now, add the elements to the drag layer if not done already
24988 if (!r.hoverData.draggingEles) {
24989 addNodesToDrag(draggedElements, {
24990 inDragLayer: true
24991 });
24992 }
24993 var totalShift = {
24994 x: 0,
24995 y: 0
24996 };
24997 if (number$1(disp[0]) && number$1(disp[1])) {
24998 totalShift.x += disp[0];
24999 totalShift.y += disp[1];
25000 if (justStartedDrag) {
25001 var dragDelta = r.hoverData.dragDelta;
25002 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25003 totalShift.x += dragDelta[0];
25004 totalShift.y += dragDelta[1];
25005 }
25006 }
25007 }
25008 r.hoverData.draggingEles = true;
25009 draggedElements.silentShift(totalShift).emit('position drag');
25010 r.redrawHint('drag', true);
25011 r.redraw();
25012 }
25013 } else {
25014 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
25015 updateDragDelta();
25016 }
25017 }
25018
25019 // prevent the dragging from triggering text selection on the page
25020 preventDefault = true;
25021 }
25022 select[2] = pos[0];
25023 select[3] = pos[1];
25024 if (preventDefault) {
25025 if (e.stopPropagation) e.stopPropagation();
25026 if (e.preventDefault) e.preventDefault();
25027 return false;
25028 }
25029 }, false);
25030 var clickTimeout, didDoubleClick, prevClickTimeStamp;
25031 r.registerBinding(containerWindow, 'mouseup', function mouseupHandler(e) {
25032 // eslint-disable-line no-undef
25033 // during left mouse button gestures, ignore other buttons
25034 if (r.hoverData.which === 1 && e.which !== 1 && r.hoverData.capture) {
25035 return;
25036 }
25037 var capture = r.hoverData.capture;
25038 if (!capture) {
25039 return;
25040 }
25041 r.hoverData.capture = false;
25042 var cy = r.cy;
25043 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25044 var select = r.selection;
25045 var near = r.findNearestElement(pos[0], pos[1], true, false);
25046 var draggedElements = r.dragData.possibleDragElements;
25047 var down = r.hoverData.down;
25048 var multSelKeyDown = isMultSelKeyDown(e);
25049 if (r.data.bgActivePosistion) {
25050 r.redrawHint('select', true);
25051 r.redraw();
25052 }
25053 r.hoverData.tapholdCancelled = true;
25054 r.data.bgActivePosistion = undefined; // not active bg now
25055
25056 if (down) {
25057 down.unactivate();
25058 }
25059 if (r.hoverData.which === 3) {
25060 var cxtEvt = {
25061 originalEvent: e,
25062 type: 'cxttapend',
25063 position: {
25064 x: pos[0],
25065 y: pos[1]
25066 }
25067 };
25068 if (down) {
25069 down.emit(cxtEvt);
25070 } else {
25071 cy.emit(cxtEvt);
25072 }
25073 if (!r.hoverData.cxtDragged) {
25074 var cxtTap = {
25075 originalEvent: e,
25076 type: 'cxttap',
25077 position: {
25078 x: pos[0],
25079 y: pos[1]
25080 }
25081 };
25082 if (down) {
25083 down.emit(cxtTap);
25084 } else {
25085 cy.emit(cxtTap);
25086 }
25087 }
25088 r.hoverData.cxtDragged = false;
25089 r.hoverData.which = null;
25090 } else if (r.hoverData.which === 1) {
25091 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
25092 x: pos[0],
25093 y: pos[1]
25094 });
25095 if (!r.dragData.didDrag &&
25096 // didn't move a node around
25097 !r.hoverData.dragged &&
25098 // didn't pan
25099 !r.hoverData.selecting &&
25100 // not box selection
25101 !r.hoverData.isOverThresholdDrag // didn't move too much
25102 ) {
25103 triggerEvents(down, ["click", "tap", "vclick"], e, {
25104 x: pos[0],
25105 y: pos[1]
25106 });
25107 didDoubleClick = false;
25108 if (e.timeStamp - prevClickTimeStamp <= cy.multiClickDebounceTime()) {
25109 clickTimeout && clearTimeout(clickTimeout);
25110 didDoubleClick = true;
25111 prevClickTimeStamp = null;
25112 triggerEvents(down, ["dblclick", "dbltap", "vdblclick"], e, {
25113 x: pos[0],
25114 y: pos[1]
25115 });
25116 } else {
25117 clickTimeout = setTimeout(function () {
25118 if (didDoubleClick) return;
25119 triggerEvents(down, ["oneclick", "onetap", "voneclick"], e, {
25120 x: pos[0],
25121 y: pos[1]
25122 });
25123 }, cy.multiClickDebounceTime());
25124 prevClickTimeStamp = e.timeStamp;
25125 }
25126 }
25127
25128 // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
25129 if (down == null // not mousedown on node
25130 && !r.dragData.didDrag // didn't move the node around
25131 && !r.hoverData.selecting // not box selection
25132 && !r.hoverData.dragged // didn't pan
25133 && !isMultSelKeyDown(e)) {
25134 cy.$(isSelected).unselect(['tapunselect']);
25135 if (draggedElements.length > 0) {
25136 r.redrawHint('eles', true);
25137 }
25138 r.dragData.possibleDragElements = draggedElements = cy.collection();
25139 }
25140
25141 // Single selection
25142 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
25143 if (near != null && near._private.selectable) {
25144 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
25145 if (near.selected()) {
25146 near.unselect(['tapunselect']);
25147 } else {
25148 near.select(['tapselect']);
25149 }
25150 } else {
25151 if (!multSelKeyDown) {
25152 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25153 near.select(['tapselect']);
25154 }
25155 }
25156 r.redrawHint('eles', true);
25157 }
25158 }
25159 if (r.hoverData.selecting) {
25160 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25161 r.redrawHint('select', true);
25162 if (box.length > 0) {
25163 r.redrawHint('eles', true);
25164 }
25165 cy.emit({
25166 type: 'boxend',
25167 originalEvent: e,
25168 position: {
25169 x: pos[0],
25170 y: pos[1]
25171 }
25172 });
25173 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25174 return ele.selectable() && !ele.selected();
25175 };
25176 if (cy.selectionType() === 'additive') {
25177 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25178 } else {
25179 if (!multSelKeyDown) {
25180 cy.$(isSelected).unmerge(box).unselect();
25181 }
25182 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25183 }
25184
25185 // always need redraw in case eles unselectable
25186 r.redraw();
25187 }
25188
25189 // Cancel drag pan
25190 if (r.hoverData.dragging) {
25191 r.hoverData.dragging = false;
25192 r.redrawHint('select', true);
25193 r.redrawHint('eles', true);
25194 r.redraw();
25195 }
25196 if (!select[4]) {
25197 r.redrawHint('drag', true);
25198 r.redrawHint('eles', true);
25199 var downWasGrabbed = down && down.grabbed();
25200 freeDraggedElements(draggedElements);
25201 if (downWasGrabbed) {
25202 down.emit('freeon');
25203 draggedElements.emit('free');
25204 if (r.dragData.didDrag) {
25205 down.emit('dragfreeon');
25206 draggedElements.emit('dragfree');
25207 }
25208 }
25209 }
25210 } // else not right mouse
25211
25212 select[4] = 0;
25213 r.hoverData.down = null;
25214 r.hoverData.cxtStarted = false;
25215 r.hoverData.draggingEles = false;
25216 r.hoverData.selecting = false;
25217 r.hoverData.isOverThresholdDrag = false;
25218 r.dragData.didDrag = false;
25219 r.hoverData.dragged = false;
25220 r.hoverData.dragDelta = [];
25221 r.hoverData.mdownPos = null;
25222 r.hoverData.mdownGPos = null;
25223 r.hoverData.which = null;
25224 }, false);
25225 var wheelHandler = function wheelHandler(e) {
25226 if (r.scrollingPage) {
25227 return;
25228 } // while scrolling, ignore wheel-to-zoom
25229
25230 var cy = r.cy;
25231 var zoom = cy.zoom();
25232 var pan = cy.pan();
25233 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25234 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25235 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25236 // if pan dragging or cxt dragging, wheel movements make no zoom
25237 e.preventDefault();
25238 return;
25239 }
25240 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25241 e.preventDefault();
25242 r.data.wheelZooming = true;
25243 clearTimeout(r.data.wheelTimeout);
25244 r.data.wheelTimeout = setTimeout(function () {
25245 r.data.wheelZooming = false;
25246 r.redrawHint('eles', true);
25247 r.redraw();
25248 }, 150);
25249 var diff;
25250 if (e.deltaY != null) {
25251 diff = e.deltaY / -250;
25252 } else if (e.wheelDeltaY != null) {
25253 diff = e.wheelDeltaY / 1000;
25254 } else {
25255 diff = e.wheelDelta / 1000;
25256 }
25257 diff = diff * r.wheelSensitivity;
25258 var needsWheelFix = e.deltaMode === 1;
25259 if (needsWheelFix) {
25260 // fixes slow wheel events on ff/linux and ff/windows
25261 diff *= 33;
25262 }
25263 var newZoom = cy.zoom() * Math.pow(10, diff);
25264 if (e.type === 'gesturechange') {
25265 newZoom = r.gestureStartZoom * e.scale;
25266 }
25267 cy.zoom({
25268 level: newZoom,
25269 renderedPosition: {
25270 x: rpos[0],
25271 y: rpos[1]
25272 }
25273 });
25274 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25275 }
25276 };
25277
25278 // Functions to help with whether mouse wheel should trigger zooming
25279 // --
25280 r.registerBinding(r.container, 'wheel', wheelHandler, true);
25281
25282 // disable nonstandard wheel events
25283 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25284 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25285 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25286
25287 r.registerBinding(containerWindow, 'scroll', function scrollHandler(e) {
25288 // eslint-disable-line no-unused-vars
25289 r.scrollingPage = true;
25290 clearTimeout(r.scrollingPageTimeout);
25291 r.scrollingPageTimeout = setTimeout(function () {
25292 r.scrollingPage = false;
25293 }, 250);
25294 }, true);
25295
25296 // desktop safari pinch to zoom start
25297 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25298 r.gestureStartZoom = r.cy.zoom();
25299 if (!r.hasTouchStarted) {
25300 // don't affect touch devices like iphone
25301 e.preventDefault();
25302 }
25303 }, true);
25304 r.registerBinding(r.container, 'gesturechange', function (e) {
25305 if (!r.hasTouchStarted) {
25306 // don't affect touch devices like iphone
25307 wheelHandler(e);
25308 }
25309 }, true);
25310
25311 // Functions to help with handling mouseout/mouseover on the Cytoscape container
25312 // Handle mouseout on Cytoscape container
25313 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25314 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25315 r.cy.emit({
25316 originalEvent: e,
25317 type: 'mouseout',
25318 position: {
25319 x: pos[0],
25320 y: pos[1]
25321 }
25322 });
25323 }, false);
25324 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25325 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25326 r.cy.emit({
25327 originalEvent: e,
25328 type: 'mouseover',
25329 position: {
25330 x: pos[0],
25331 y: pos[1]
25332 }
25333 });
25334 }, false);
25335 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25336 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25337 var center1, modelCenter1; // center point on start pinch to zoom
25338 var offsetLeft, offsetTop;
25339 var containerWidth, containerHeight;
25340 var twoFingersStartInside;
25341 var distance = function distance(x1, y1, x2, y2) {
25342 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25343 };
25344 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25345 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25346 };
25347 var touchstartHandler;
25348 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25349 r.hasTouchStarted = true;
25350 if (!eventInContainer(e)) {
25351 return;
25352 }
25353 blurActiveDomElement();
25354 r.touchData.capture = true;
25355 r.data.bgActivePosistion = undefined;
25356 var cy = r.cy;
25357 var now = r.touchData.now;
25358 var earlier = r.touchData.earlier;
25359 if (e.touches[0]) {
25360 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25361 now[0] = pos[0];
25362 now[1] = pos[1];
25363 }
25364 if (e.touches[1]) {
25365 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25366 now[2] = pos[0];
25367 now[3] = pos[1];
25368 }
25369 if (e.touches[2]) {
25370 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25371 now[4] = pos[0];
25372 now[5] = pos[1];
25373 }
25374
25375 // record starting points for pinch-to-zoom
25376 if (e.touches[1]) {
25377 r.touchData.singleTouchMoved = true;
25378 freeDraggedElements(r.dragData.touchDragEles);
25379 var offsets = r.findContainerClientCoords();
25380 offsetLeft = offsets[0];
25381 offsetTop = offsets[1];
25382 containerWidth = offsets[2];
25383 containerHeight = offsets[3];
25384 f1x1 = e.touches[0].clientX - offsetLeft;
25385 f1y1 = e.touches[0].clientY - offsetTop;
25386 f2x1 = e.touches[1].clientX - offsetLeft;
25387 f2y1 = e.touches[1].clientY - offsetTop;
25388 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25389 var pan = cy.pan();
25390 var zoom = cy.zoom();
25391 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25392 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25393 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25394 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom];
25395
25396 // consider context tap
25397 var cxtDistThreshold = 200;
25398 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25399 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25400 var near1 = r.findNearestElement(now[0], now[1], true, true);
25401 var near2 = r.findNearestElement(now[2], now[3], true, true);
25402 if (near1 && near1.isNode()) {
25403 near1.activate().emit({
25404 originalEvent: e,
25405 type: 'cxttapstart',
25406 position: {
25407 x: now[0],
25408 y: now[1]
25409 }
25410 });
25411 r.touchData.start = near1;
25412 } else if (near2 && near2.isNode()) {
25413 near2.activate().emit({
25414 originalEvent: e,
25415 type: 'cxttapstart',
25416 position: {
25417 x: now[0],
25418 y: now[1]
25419 }
25420 });
25421 r.touchData.start = near2;
25422 } else {
25423 cy.emit({
25424 originalEvent: e,
25425 type: 'cxttapstart',
25426 position: {
25427 x: now[0],
25428 y: now[1]
25429 }
25430 });
25431 }
25432 if (r.touchData.start) {
25433 r.touchData.start._private.grabbed = false;
25434 }
25435 r.touchData.cxt = true;
25436 r.touchData.cxtDragged = false;
25437 r.data.bgActivePosistion = undefined;
25438 r.redraw();
25439 return;
25440 }
25441 }
25442 if (e.touches[2]) {
25443 // ignore
25444
25445 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25446 if (cy.boxSelectionEnabled()) {
25447 e.preventDefault();
25448 }
25449 } else if (e.touches[1]) ; else if (e.touches[0]) {
25450 var nears = r.findNearestElements(now[0], now[1], true, true);
25451 var near = nears[0];
25452 if (near != null) {
25453 near.activate();
25454 r.touchData.start = near;
25455 r.touchData.starts = nears;
25456 if (r.nodeIsGrabbable(near)) {
25457 var draggedEles = r.dragData.touchDragEles = cy.collection();
25458 var selectedNodes = null;
25459 r.redrawHint('eles', true);
25460 r.redrawHint('drag', true);
25461 if (near.selected()) {
25462 // reset drag elements, since near will be added again
25463
25464 selectedNodes = cy.$(function (ele) {
25465 return ele.selected() && r.nodeIsGrabbable(ele);
25466 });
25467 addNodesToDrag(selectedNodes, {
25468 addToList: draggedEles
25469 });
25470 } else {
25471 addNodeToDrag(near, {
25472 addToList: draggedEles
25473 });
25474 }
25475 setGrabTarget(near);
25476 var makeEvent = function makeEvent(type) {
25477 return {
25478 originalEvent: e,
25479 type: type,
25480 position: {
25481 x: now[0],
25482 y: now[1]
25483 }
25484 };
25485 };
25486 near.emit(makeEvent('grabon'));
25487 if (selectedNodes) {
25488 selectedNodes.forEach(function (n) {
25489 n.emit(makeEvent('grab'));
25490 });
25491 } else {
25492 near.emit(makeEvent('grab'));
25493 }
25494 }
25495 }
25496 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25497 x: now[0],
25498 y: now[1]
25499 });
25500 if (near == null) {
25501 r.data.bgActivePosistion = {
25502 x: pos[0],
25503 y: pos[1]
25504 };
25505 r.redrawHint('select', true);
25506 r.redraw();
25507 }
25508
25509 // Tap, taphold
25510 // -----
25511
25512 r.touchData.singleTouchMoved = false;
25513 r.touchData.singleTouchStartTime = +new Date();
25514 clearTimeout(r.touchData.tapholdTimeout);
25515 r.touchData.tapholdTimeout = setTimeout(function () {
25516 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25517 && !r.touchData.selecting // box selection shouldn't allow taphold through
25518 ) {
25519 triggerEvents(r.touchData.start, ['taphold'], e, {
25520 x: now[0],
25521 y: now[1]
25522 });
25523 }
25524 }, r.tapholdDuration);
25525 }
25526 if (e.touches.length >= 1) {
25527 var sPos = r.touchData.startPosition = [null, null, null, null, null, null];
25528 for (var i = 0; i < now.length; i++) {
25529 sPos[i] = earlier[i] = now[i];
25530 }
25531 var touch0 = e.touches[0];
25532 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25533 }
25534 }, false);
25535 var touchmoveHandler;
25536 r.registerBinding(containerWindow, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25537 // eslint-disable-line no-undef
25538 var capture = r.touchData.capture;
25539 if (!capture && !eventInContainer(e)) {
25540 return;
25541 }
25542 var select = r.selection;
25543 var cy = r.cy;
25544 var now = r.touchData.now;
25545 var earlier = r.touchData.earlier;
25546 var zoom = cy.zoom();
25547 if (e.touches[0]) {
25548 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25549 now[0] = pos[0];
25550 now[1] = pos[1];
25551 }
25552 if (e.touches[1]) {
25553 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25554 now[2] = pos[0];
25555 now[3] = pos[1];
25556 }
25557 if (e.touches[2]) {
25558 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25559 now[4] = pos[0];
25560 now[5] = pos[1];
25561 }
25562 var startGPos = r.touchData.startGPosition;
25563 var isOverThresholdDrag;
25564 if (capture && e.touches[0] && startGPos) {
25565 var disp = [];
25566 for (var j = 0; j < now.length; j++) {
25567 disp[j] = now[j] - earlier[j];
25568 }
25569 var dx = e.touches[0].clientX - startGPos[0];
25570 var dx2 = dx * dx;
25571 var dy = e.touches[0].clientY - startGPos[1];
25572 var dy2 = dy * dy;
25573 var dist2 = dx2 + dy2;
25574 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25575 }
25576
25577 // context swipe cancelling
25578 if (capture && r.touchData.cxt) {
25579 e.preventDefault();
25580 var f1x2 = e.touches[0].clientX - offsetLeft,
25581 f1y2 = e.touches[0].clientY - offsetTop;
25582 var f2x2 = e.touches[1].clientX - offsetLeft,
25583 f2y2 = e.touches[1].clientY - offsetTop;
25584 // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25585 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25586 var factorSq = distance2Sq / distance1Sq;
25587 var distThreshold = 150;
25588 var distThresholdSq = distThreshold * distThreshold;
25589 var factorThreshold = 1.5;
25590 var factorThresholdSq = factorThreshold * factorThreshold;
25591
25592 // cancel ctx gestures if the distance b/t the fingers increases
25593 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25594 r.touchData.cxt = false;
25595 r.data.bgActivePosistion = undefined;
25596 r.redrawHint('select', true);
25597 var cxtEvt = {
25598 originalEvent: e,
25599 type: 'cxttapend',
25600 position: {
25601 x: now[0],
25602 y: now[1]
25603 }
25604 };
25605 if (r.touchData.start) {
25606 r.touchData.start.unactivate().emit(cxtEvt);
25607 r.touchData.start = null;
25608 } else {
25609 cy.emit(cxtEvt);
25610 }
25611 }
25612 }
25613
25614 // context swipe
25615 if (capture && r.touchData.cxt) {
25616 var cxtEvt = {
25617 originalEvent: e,
25618 type: 'cxtdrag',
25619 position: {
25620 x: now[0],
25621 y: now[1]
25622 }
25623 };
25624 r.data.bgActivePosistion = undefined;
25625 r.redrawHint('select', true);
25626 if (r.touchData.start) {
25627 r.touchData.start.emit(cxtEvt);
25628 } else {
25629 cy.emit(cxtEvt);
25630 }
25631 if (r.touchData.start) {
25632 r.touchData.start._private.grabbed = false;
25633 }
25634 r.touchData.cxtDragged = true;
25635 var near = r.findNearestElement(now[0], now[1], true, true);
25636 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25637 if (r.touchData.cxtOver) {
25638 r.touchData.cxtOver.emit({
25639 originalEvent: e,
25640 type: 'cxtdragout',
25641 position: {
25642 x: now[0],
25643 y: now[1]
25644 }
25645 });
25646 }
25647 r.touchData.cxtOver = near;
25648 if (near) {
25649 near.emit({
25650 originalEvent: e,
25651 type: 'cxtdragover',
25652 position: {
25653 x: now[0],
25654 y: now[1]
25655 }
25656 });
25657 }
25658 }
25659
25660 // box selection
25661 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25662 e.preventDefault();
25663 r.data.bgActivePosistion = undefined;
25664 this.lastThreeTouch = +new Date();
25665 if (!r.touchData.selecting) {
25666 cy.emit({
25667 originalEvent: e,
25668 type: 'boxstart',
25669 position: {
25670 x: now[0],
25671 y: now[1]
25672 }
25673 });
25674 }
25675 r.touchData.selecting = true;
25676 r.touchData.didSelect = true;
25677 select[4] = 1;
25678 if (!select || select.length === 0 || select[0] === undefined) {
25679 select[0] = (now[0] + now[2] + now[4]) / 3;
25680 select[1] = (now[1] + now[3] + now[5]) / 3;
25681 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25682 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25683 } else {
25684 select[2] = (now[0] + now[2] + now[4]) / 3;
25685 select[3] = (now[1] + now[3] + now[5]) / 3;
25686 }
25687 r.redrawHint('select', true);
25688 r.redraw();
25689
25690 // pinch to zoom
25691 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25692 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25693 // two fingers => pinch to zoom
25694 e.preventDefault();
25695 r.data.bgActivePosistion = undefined;
25696 r.redrawHint('select', true);
25697 var draggedEles = r.dragData.touchDragEles;
25698 if (draggedEles) {
25699 r.redrawHint('drag', true);
25700 for (var i = 0; i < draggedEles.length; i++) {
25701 var de_p = draggedEles[i]._private;
25702 de_p.grabbed = false;
25703 de_p.rscratch.inDragLayer = false;
25704 }
25705 }
25706 var _start = r.touchData.start;
25707
25708 // (x2, y2) for fingers 1 and 2
25709 var f1x2 = e.touches[0].clientX - offsetLeft,
25710 f1y2 = e.touches[0].clientY - offsetTop;
25711 var f2x2 = e.touches[1].clientX - offsetLeft,
25712 f2y2 = e.touches[1].clientY - offsetTop;
25713 var distance2 = distance(f1x2, f1y2, f2x2, f2y2);
25714 // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25715 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25716 var factor = distance2 / distance1;
25717 if (twoFingersStartInside) {
25718 // delta finger1
25719 var df1x = f1x2 - f1x1;
25720 var df1y = f1y2 - f1y1;
25721
25722 // delta finger 2
25723 var df2x = f2x2 - f2x1;
25724 var df2y = f2y2 - f2y1;
25725
25726 // translation is the normalised vector of the two fingers movement
25727 // i.e. so pinching cancels out and moving together pans
25728 var tx = (df1x + df2x) / 2;
25729 var ty = (df1y + df2y) / 2;
25730
25731 // now calculate the zoom
25732 var zoom1 = cy.zoom();
25733 var zoom2 = zoom1 * factor;
25734 var pan1 = cy.pan();
25735
25736 // the model center point converted to the current rendered pos
25737 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25738 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25739 var pan2 = {
25740 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25741 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25742 };
25743
25744 // remove dragged eles
25745 if (_start && _start.active()) {
25746 var draggedEles = r.dragData.touchDragEles;
25747 freeDraggedElements(draggedEles);
25748 r.redrawHint('drag', true);
25749 r.redrawHint('eles', true);
25750 _start.unactivate().emit('freeon');
25751 draggedEles.emit('free');
25752 if (r.dragData.didDrag) {
25753 _start.emit('dragfreeon');
25754 draggedEles.emit('dragfree');
25755 }
25756 }
25757 cy.viewport({
25758 zoom: zoom2,
25759 pan: pan2,
25760 cancelOnFailedZoom: true
25761 });
25762 cy.emit('pinchzoom');
25763 distance1 = distance2;
25764 f1x1 = f1x2;
25765 f1y1 = f1y2;
25766 f2x1 = f2x2;
25767 f2y1 = f2y2;
25768 r.pinching = true;
25769 }
25770
25771 // Re-project
25772 if (e.touches[0]) {
25773 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25774 now[0] = pos[0];
25775 now[1] = pos[1];
25776 }
25777 if (e.touches[1]) {
25778 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25779 now[2] = pos[0];
25780 now[3] = pos[1];
25781 }
25782 if (e.touches[2]) {
25783 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25784 now[4] = pos[0];
25785 now[5] = pos[1];
25786 }
25787 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25788 ) {
25789 var start = r.touchData.start;
25790 var last = r.touchData.last;
25791 var near;
25792 if (!r.hoverData.draggingEles && !r.swipePanning) {
25793 near = r.findNearestElement(now[0], now[1], true, true);
25794 }
25795 if (capture && start != null) {
25796 e.preventDefault();
25797 }
25798
25799 // dragging nodes
25800 if (capture && start != null && r.nodeIsDraggable(start)) {
25801 if (isOverThresholdDrag) {
25802 // then dragging can happen
25803 var draggedEles = r.dragData.touchDragEles;
25804 var justStartedDrag = !r.dragData.didDrag;
25805 if (justStartedDrag) {
25806 addNodesToDrag(draggedEles, {
25807 inDragLayer: true
25808 });
25809 }
25810 r.dragData.didDrag = true;
25811 var totalShift = {
25812 x: 0,
25813 y: 0
25814 };
25815 if (number$1(disp[0]) && number$1(disp[1])) {
25816 totalShift.x += disp[0];
25817 totalShift.y += disp[1];
25818 if (justStartedDrag) {
25819 r.redrawHint('eles', true);
25820 var dragDelta = r.touchData.dragDelta;
25821 if (dragDelta && number$1(dragDelta[0]) && number$1(dragDelta[1])) {
25822 totalShift.x += dragDelta[0];
25823 totalShift.y += dragDelta[1];
25824 }
25825 }
25826 }
25827 r.hoverData.draggingEles = true;
25828 draggedEles.silentShift(totalShift).emit('position drag');
25829 r.redrawHint('drag', true);
25830 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25831 r.redrawHint('eles', true);
25832 }
25833 r.redraw();
25834 } else {
25835 // otherwise keep track of drag delta for later
25836 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25837 if (dragDelta.length === 0) {
25838 dragDelta.push(disp[0]);
25839 dragDelta.push(disp[1]);
25840 } else {
25841 dragDelta[0] += disp[0];
25842 dragDelta[1] += disp[1];
25843 }
25844 }
25845 }
25846
25847 // touchmove
25848 {
25849 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25850 x: now[0],
25851 y: now[1]
25852 });
25853 if ((!start || !start.grabbed()) && near != last) {
25854 if (last) {
25855 last.emit({
25856 originalEvent: e,
25857 type: 'tapdragout',
25858 position: {
25859 x: now[0],
25860 y: now[1]
25861 }
25862 });
25863 }
25864 if (near) {
25865 near.emit({
25866 originalEvent: e,
25867 type: 'tapdragover',
25868 position: {
25869 x: now[0],
25870 y: now[1]
25871 }
25872 });
25873 }
25874 }
25875 r.touchData.last = near;
25876 }
25877
25878 // check to cancel taphold
25879 if (capture) {
25880 for (var i = 0; i < now.length; i++) {
25881 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25882 r.touchData.singleTouchMoved = true;
25883 }
25884 }
25885 }
25886
25887 // panning
25888 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25889 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25890 if (allowPassthrough) {
25891 e.preventDefault();
25892 if (!r.data.bgActivePosistion) {
25893 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25894 }
25895 if (r.swipePanning) {
25896 cy.panBy({
25897 x: disp[0] * zoom,
25898 y: disp[1] * zoom
25899 });
25900 cy.emit('dragpan');
25901 } else if (isOverThresholdDrag) {
25902 r.swipePanning = true;
25903 cy.panBy({
25904 x: dx * zoom,
25905 y: dy * zoom
25906 });
25907 cy.emit('dragpan');
25908 if (start) {
25909 start.unactivate();
25910 r.redrawHint('select', true);
25911 r.touchData.start = null;
25912 }
25913 }
25914 }
25915
25916 // Re-project
25917 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25918 now[0] = pos[0];
25919 now[1] = pos[1];
25920 }
25921 }
25922 for (var j = 0; j < now.length; j++) {
25923 earlier[j] = now[j];
25924 }
25925
25926 // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25927 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25928 r.data.bgActivePosistion = undefined;
25929 r.redrawHint('select', true);
25930 r.redraw();
25931 }
25932 }, false);
25933 var touchcancelHandler;
25934 r.registerBinding(containerWindow, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25935 // eslint-disable-line no-unused-vars
25936 var start = r.touchData.start;
25937 r.touchData.capture = false;
25938 if (start) {
25939 start.unactivate();
25940 }
25941 });
25942 var touchendHandler, didDoubleTouch, touchTimeout, prevTouchTimeStamp;
25943 r.registerBinding(containerWindow, 'touchend', touchendHandler = function touchendHandler(e) {
25944 // eslint-disable-line no-unused-vars
25945 var start = r.touchData.start;
25946 var capture = r.touchData.capture;
25947 if (capture) {
25948 if (e.touches.length === 0) {
25949 r.touchData.capture = false;
25950 }
25951 e.preventDefault();
25952 } else {
25953 return;
25954 }
25955 var select = r.selection;
25956 r.swipePanning = false;
25957 r.hoverData.draggingEles = false;
25958 var cy = r.cy;
25959 var zoom = cy.zoom();
25960 var now = r.touchData.now;
25961 var earlier = r.touchData.earlier;
25962 if (e.touches[0]) {
25963 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25964 now[0] = pos[0];
25965 now[1] = pos[1];
25966 }
25967 if (e.touches[1]) {
25968 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25969 now[2] = pos[0];
25970 now[3] = pos[1];
25971 }
25972 if (e.touches[2]) {
25973 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25974 now[4] = pos[0];
25975 now[5] = pos[1];
25976 }
25977 if (start) {
25978 start.unactivate();
25979 }
25980 var ctxTapend;
25981 if (r.touchData.cxt) {
25982 ctxTapend = {
25983 originalEvent: e,
25984 type: 'cxttapend',
25985 position: {
25986 x: now[0],
25987 y: now[1]
25988 }
25989 };
25990 if (start) {
25991 start.emit(ctxTapend);
25992 } else {
25993 cy.emit(ctxTapend);
25994 }
25995 if (!r.touchData.cxtDragged) {
25996 var ctxTap = {
25997 originalEvent: e,
25998 type: 'cxttap',
25999 position: {
26000 x: now[0],
26001 y: now[1]
26002 }
26003 };
26004 if (start) {
26005 start.emit(ctxTap);
26006 } else {
26007 cy.emit(ctxTap);
26008 }
26009 }
26010 if (r.touchData.start) {
26011 r.touchData.start._private.grabbed = false;
26012 }
26013 r.touchData.cxt = false;
26014 r.touchData.start = null;
26015 r.redraw();
26016 return;
26017 }
26018
26019 // no more box selection if we don't have three fingers
26020 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
26021 r.touchData.selecting = false;
26022 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
26023 select[0] = undefined;
26024 select[1] = undefined;
26025 select[2] = undefined;
26026 select[3] = undefined;
26027 select[4] = 0;
26028 r.redrawHint('select', true);
26029 cy.emit({
26030 type: 'boxend',
26031 originalEvent: e,
26032 position: {
26033 x: now[0],
26034 y: now[1]
26035 }
26036 });
26037 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
26038 return ele.selectable() && !ele.selected();
26039 };
26040 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
26041 if (box.nonempty()) {
26042 r.redrawHint('eles', true);
26043 }
26044 r.redraw();
26045 }
26046 if (start != null) {
26047 start.unactivate();
26048 }
26049 if (e.touches[2]) {
26050 r.data.bgActivePosistion = undefined;
26051 r.redrawHint('select', true);
26052 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26053 r.data.bgActivePosistion = undefined;
26054 r.redrawHint('select', true);
26055 var draggedEles = r.dragData.touchDragEles;
26056 if (start != null) {
26057 var startWasGrabbed = start._private.grabbed;
26058 freeDraggedElements(draggedEles);
26059 r.redrawHint('drag', true);
26060 r.redrawHint('eles', true);
26061 if (startWasGrabbed) {
26062 start.emit('freeon');
26063 draggedEles.emit('free');
26064 if (r.dragData.didDrag) {
26065 start.emit('dragfreeon');
26066 draggedEles.emit('dragfree');
26067 }
26068 }
26069 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26070 x: now[0],
26071 y: now[1]
26072 });
26073 start.unactivate();
26074 r.touchData.start = null;
26075 } else {
26076 var near = r.findNearestElement(now[0], now[1], true, true);
26077 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26078 x: now[0],
26079 y: now[1]
26080 });
26081 }
26082 var dx = r.touchData.startPosition[0] - now[0];
26083 var dx2 = dx * dx;
26084 var dy = r.touchData.startPosition[1] - now[1];
26085 var dy2 = dy * dy;
26086 var dist2 = dx2 + dy2;
26087 var rdist2 = dist2 * zoom * zoom;
26088
26089 // Tap event, roughly same as mouse click event for touch
26090 if (!r.touchData.singleTouchMoved) {
26091 if (!start) {
26092 cy.$(':selected').unselect(['tapunselect']);
26093 }
26094 triggerEvents(start, ['tap', 'vclick'], e, {
26095 x: now[0],
26096 y: now[1]
26097 });
26098 didDoubleTouch = false;
26099 if (e.timeStamp - prevTouchTimeStamp <= cy.multiClickDebounceTime()) {
26100 touchTimeout && clearTimeout(touchTimeout);
26101 didDoubleTouch = true;
26102 prevTouchTimeStamp = null;
26103 triggerEvents(start, ['dbltap', 'vdblclick'], e, {
26104 x: now[0],
26105 y: now[1]
26106 });
26107 } else {
26108 touchTimeout = setTimeout(function () {
26109 if (didDoubleTouch) return;
26110 triggerEvents(start, ['onetap', 'voneclick'], e, {
26111 x: now[0],
26112 y: now[1]
26113 });
26114 }, cy.multiClickDebounceTime());
26115 prevTouchTimeStamp = e.timeStamp;
26116 }
26117 }
26118
26119 // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26120 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26121 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26122 ) {
26123 if (cy.selectionType() === 'single') {
26124 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26125 start.select(['tapselect']);
26126 } else {
26127 if (start.selected()) {
26128 start.unselect(['tapunselect']);
26129 } else {
26130 start.select(['tapselect']);
26131 }
26132 }
26133 r.redrawHint('eles', true);
26134 }
26135 r.touchData.singleTouchMoved = true;
26136 }
26137 for (var j = 0; j < now.length; j++) {
26138 earlier[j] = now[j];
26139 }
26140 r.dragData.didDrag = false; // reset for next touchstart
26141
26142 if (e.touches.length === 0) {
26143 r.touchData.dragDelta = [];
26144 r.touchData.startPosition = [null, null, null, null, null, null];
26145 r.touchData.startGPosition = null;
26146 r.touchData.didSelect = false;
26147 }
26148 if (e.touches.length < 2) {
26149 if (e.touches.length === 1) {
26150 // the old start global pos'n may not be the same finger that remains
26151 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26152 }
26153 r.pinching = false;
26154 r.redrawHint('eles', true);
26155 r.redraw();
26156 }
26157
26158 //r.redraw();
26159 }, false);
26160
26161 // fallback compatibility layer for ms pointer events
26162 if (typeof TouchEvent === 'undefined') {
26163 var pointers = [];
26164 var makeTouch = function makeTouch(e) {
26165 return {
26166 clientX: e.clientX,
26167 clientY: e.clientY,
26168 force: 1,
26169 identifier: e.pointerId,
26170 pageX: e.pageX,
26171 pageY: e.pageY,
26172 radiusX: e.width / 2,
26173 radiusY: e.height / 2,
26174 screenX: e.screenX,
26175 screenY: e.screenY,
26176 target: e.target
26177 };
26178 };
26179 var makePointer = function makePointer(e) {
26180 return {
26181 event: e,
26182 touch: makeTouch(e)
26183 };
26184 };
26185 var addPointer = function addPointer(e) {
26186 pointers.push(makePointer(e));
26187 };
26188 var removePointer = function removePointer(e) {
26189 for (var i = 0; i < pointers.length; i++) {
26190 var p = pointers[i];
26191 if (p.event.pointerId === e.pointerId) {
26192 pointers.splice(i, 1);
26193 return;
26194 }
26195 }
26196 };
26197 var updatePointer = function updatePointer(e) {
26198 var p = pointers.filter(function (p) {
26199 return p.event.pointerId === e.pointerId;
26200 })[0];
26201 p.event = e;
26202 p.touch = makeTouch(e);
26203 };
26204 var addTouchesToEvent = function addTouchesToEvent(e) {
26205 e.touches = pointers.map(function (p) {
26206 return p.touch;
26207 });
26208 };
26209 var pointerIsMouse = function pointerIsMouse(e) {
26210 return e.pointerType === 'mouse' || e.pointerType === 4;
26211 };
26212 r.registerBinding(r.container, 'pointerdown', function (e) {
26213 if (pointerIsMouse(e)) {
26214 return;
26215 } // mouse already handled
26216
26217 e.preventDefault();
26218 addPointer(e);
26219 addTouchesToEvent(e);
26220 touchstartHandler(e);
26221 });
26222 r.registerBinding(r.container, 'pointerup', function (e) {
26223 if (pointerIsMouse(e)) {
26224 return;
26225 } // mouse already handled
26226
26227 removePointer(e);
26228 addTouchesToEvent(e);
26229 touchendHandler(e);
26230 });
26231 r.registerBinding(r.container, 'pointercancel', function (e) {
26232 if (pointerIsMouse(e)) {
26233 return;
26234 } // mouse already handled
26235
26236 removePointer(e);
26237 addTouchesToEvent(e);
26238 touchcancelHandler(e);
26239 });
26240 r.registerBinding(r.container, 'pointermove', function (e) {
26241 if (pointerIsMouse(e)) {
26242 return;
26243 } // mouse already handled
26244
26245 e.preventDefault();
26246 updatePointer(e);
26247 addTouchesToEvent(e);
26248 touchmoveHandler(e);
26249 });
26250 }
26251 };
26252
26253 var BRp$2 = {};
26254 BRp$2.generatePolygon = function (name, points) {
26255 return this.nodeShapes[name] = {
26256 renderer: this,
26257 name: name,
26258 points: points,
26259 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26260 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26261 },
26262 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26263 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26264 },
26265 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26266 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26267 }
26268 };
26269 };
26270 BRp$2.generateEllipse = function () {
26271 return this.nodeShapes['ellipse'] = {
26272 renderer: this,
26273 name: 'ellipse',
26274 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26275 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26276 },
26277 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26278 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26279 },
26280 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26281 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26282 }
26283 };
26284 };
26285 BRp$2.generateRoundPolygon = function (name, points) {
26286 return this.nodeShapes[name] = {
26287 renderer: this,
26288 name: name,
26289 points: points,
26290 getOrCreateCorners: function getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, field) {
26291 if (rs[field] !== undefined && rs[field + '-cx'] === centerX && rs[field + '-cy'] === centerY) {
26292 return rs[field];
26293 }
26294 rs[field] = new Array(points.length / 2);
26295 rs[field + '-cx'] = centerX;
26296 rs[field + '-cy'] = centerY;
26297 var halfW = width / 2;
26298 var halfH = height / 2;
26299 cornerRadius = cornerRadius === 'auto' ? getRoundPolygonRadius(width, height) : cornerRadius;
26300 var p = new Array(points.length / 2);
26301 for (var _i = 0; _i < points.length / 2; _i++) {
26302 p[_i] = {
26303 x: centerX + halfW * points[_i * 2],
26304 y: centerY + halfH * points[_i * 2 + 1]
26305 };
26306 }
26307 var i,
26308 p1,
26309 p2,
26310 p3,
26311 len = p.length;
26312 p1 = p[len - 1];
26313 // for each point
26314 for (i = 0; i < len; i++) {
26315 p2 = p[i % len];
26316 p3 = p[(i + 1) % len];
26317 rs[field][i] = getRoundCorner(p1, p2, p3, cornerRadius);
26318 p1 = p2;
26319 p2 = p3;
26320 }
26321 return rs[field];
26322 },
26323 draw: function draw(context, centerX, centerY, width, height, cornerRadius, rs) {
26324 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points, this.getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, 'drawCorners'));
26325 },
26326 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius, rs) {
26327 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height, padding, this.getOrCreateCorners(nodeX, nodeY, width, height, cornerRadius, rs, 'corners'));
26328 },
26329 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius, rs) {
26330 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height, this.getOrCreateCorners(centerX, centerY, width, height, cornerRadius, rs, 'corners'));
26331 }
26332 };
26333 };
26334 BRp$2.generateRoundRectangle = function () {
26335 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26336 renderer: this,
26337 name: 'round-rectangle',
26338 points: generateUnitNgonPointsFitToSquare(4, 0),
26339 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26340 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, this.points, cornerRadius);
26341 },
26342 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26343 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding, cornerRadius);
26344 },
26345 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26346 var halfWidth = width / 2;
26347 var halfHeight = height / 2;
26348 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(width, height) : cornerRadius;
26349 cornerRadius = Math.min(halfWidth, halfHeight, cornerRadius);
26350 var diam = cornerRadius * 2;
26351
26352 // Check hBox
26353 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26354 return true;
26355 }
26356
26357 // Check vBox
26358 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26359 return true;
26360 }
26361
26362 // Check top left quarter circle
26363 if (checkInEllipse(x, y, diam, diam, centerX - halfWidth + cornerRadius, centerY - halfHeight + cornerRadius, padding)) {
26364 return true;
26365 }
26366
26367 // Check top right quarter circle
26368 if (checkInEllipse(x, y, diam, diam, centerX + halfWidth - cornerRadius, centerY - halfHeight + cornerRadius, padding)) {
26369 return true;
26370 }
26371
26372 // Check bottom right quarter circle
26373 if (checkInEllipse(x, y, diam, diam, centerX + halfWidth - cornerRadius, centerY + halfHeight - cornerRadius, padding)) {
26374 return true;
26375 }
26376
26377 // Check bottom left quarter circle
26378 if (checkInEllipse(x, y, diam, diam, centerX - halfWidth + cornerRadius, centerY + halfHeight - cornerRadius, padding)) {
26379 return true;
26380 }
26381 return false;
26382 }
26383 };
26384 };
26385 BRp$2.generateCutRectangle = function () {
26386 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26387 renderer: this,
26388 name: 'cut-rectangle',
26389 cornerLength: getCutRectangleCornerLength(),
26390 points: generateUnitNgonPointsFitToSquare(4, 0),
26391 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26392 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, null, cornerRadius);
26393 },
26394 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY, cornerRadius) {
26395 var cl = cornerRadius === 'auto' ? this.cornerLength : cornerRadius;
26396 var hh = height / 2;
26397 var hw = width / 2;
26398 var xBegin = centerX - hw;
26399 var xEnd = centerX + hw;
26400 var yBegin = centerY - hh;
26401 var yEnd = centerY + hh;
26402
26403 // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26404 return {
26405 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26406 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26407 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26408 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26409 };
26410 },
26411 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26412 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY, cornerRadius);
26413 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26414 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26415 },
26416 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26417 var cl = cornerRadius === 'auto' ? this.cornerLength : cornerRadius;
26418 // Check hBox
26419 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * cl, [0, -1], padding)) {
26420 return true;
26421 }
26422
26423 // Check vBox
26424 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * cl, height, [0, -1], padding)) {
26425 return true;
26426 }
26427 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26428 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26429 }
26430 };
26431 };
26432 BRp$2.generateBarrel = function () {
26433 return this.nodeShapes['barrel'] = {
26434 renderer: this,
26435 name: 'barrel',
26436 points: generateUnitNgonPointsFitToSquare(4, 0),
26437 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26438 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26439 },
26440 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26441 // use two fixed t values for the bezier curve approximation
26442
26443 var t0 = 0.15;
26444 var t1 = 0.5;
26445 var t2 = 0.85;
26446 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26447 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26448 // approximate curve pts based on the two t values
26449 var m0 = qbezierPtAt({
26450 x: pts[0],
26451 y: pts[1]
26452 }, {
26453 x: pts[2],
26454 y: pts[3]
26455 }, {
26456 x: pts[4],
26457 y: pts[5]
26458 }, t0);
26459 var m1 = qbezierPtAt({
26460 x: pts[0],
26461 y: pts[1]
26462 }, {
26463 x: pts[2],
26464 y: pts[3]
26465 }, {
26466 x: pts[4],
26467 y: pts[5]
26468 }, t1);
26469 var m2 = qbezierPtAt({
26470 x: pts[0],
26471 y: pts[1]
26472 }, {
26473 x: pts[2],
26474 y: pts[3]
26475 }, {
26476 x: pts[4],
26477 y: pts[5]
26478 }, t2);
26479 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26480 };
26481 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26482 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26483 },
26484 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26485 var hh = height / 2;
26486 var hw = width / 2;
26487 var xBegin = centerX - hw;
26488 var xEnd = centerX + hw;
26489 var yBegin = centerY - hh;
26490 var yEnd = centerY + hh;
26491 var curveConstants = getBarrelCurveConstants(width, height);
26492 var hOffset = curveConstants.heightOffset;
26493 var wOffset = curveConstants.widthOffset;
26494 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width;
26495
26496 // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26497 var pts = {
26498 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26499 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26500 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26501 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26502 };
26503 pts.topLeft.isTop = true;
26504 pts.topRight.isTop = true;
26505 pts.bottomLeft.isBottom = true;
26506 pts.bottomRight.isBottom = true;
26507 return pts;
26508 },
26509 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26510 var curveConstants = getBarrelCurveConstants(width, height);
26511 var hOffset = curveConstants.heightOffset;
26512 var wOffset = curveConstants.widthOffset;
26513
26514 // Check hBox
26515 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26516 return true;
26517 }
26518
26519 // Check vBox
26520 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26521 return true;
26522 }
26523 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26524 var getCurveT = function getCurveT(x, y, curvePts) {
26525 var x0 = curvePts[4];
26526 var x1 = curvePts[2];
26527 var x2 = curvePts[0];
26528 var y0 = curvePts[5];
26529 // var y1 = curvePts[ 3 ];
26530 var y2 = curvePts[1];
26531 var xMin = Math.min(x0, x2);
26532 var xMax = Math.max(x0, x2);
26533 var yMin = Math.min(y0, y2);
26534 var yMax = Math.max(y0, y2);
26535 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26536 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26537 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26538 var validRoots = roots.filter(function (r) {
26539 return 0 <= r && r <= 1;
26540 });
26541 if (validRoots.length > 0) {
26542 return validRoots[0];
26543 }
26544 }
26545 return null;
26546 };
26547 var curveRegions = Object.keys(barrelCurvePts);
26548 for (var i = 0; i < curveRegions.length; i++) {
26549 var corner = curveRegions[i];
26550 var cornerPts = barrelCurvePts[corner];
26551 var t = getCurveT(x, y, cornerPts);
26552 if (t == null) {
26553 continue;
26554 }
26555 var y0 = cornerPts[5];
26556 var y1 = cornerPts[3];
26557 var y2 = cornerPts[1];
26558 var bezY = qbezierAt(y0, y1, y2, t);
26559 if (cornerPts.isTop && bezY <= y) {
26560 return true;
26561 }
26562 if (cornerPts.isBottom && y <= bezY) {
26563 return true;
26564 }
26565 }
26566 return false;
26567 }
26568 };
26569 };
26570 BRp$2.generateBottomRoundrectangle = function () {
26571 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26572 renderer: this,
26573 name: 'bottom-round-rectangle',
26574 points: generateUnitNgonPointsFitToSquare(4, 0),
26575 draw: function draw(context, centerX, centerY, width, height, cornerRadius) {
26576 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height, this.points, cornerRadius);
26577 },
26578 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding, cornerRadius) {
26579 var topStartX = nodeX - (width / 2 + padding);
26580 var topStartY = nodeY - (height / 2 + padding);
26581 var topEndY = topStartY;
26582 var topEndX = nodeX + (width / 2 + padding);
26583 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26584 if (topIntersections.length > 0) {
26585 return topIntersections;
26586 }
26587 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding, cornerRadius);
26588 },
26589 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY, cornerRadius) {
26590 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(width, height) : cornerRadius;
26591 var diam = 2 * cornerRadius;
26592
26593 // Check hBox
26594 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26595 return true;
26596 }
26597
26598 // Check vBox
26599 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26600 return true;
26601 }
26602
26603 // check non-rounded top side
26604 var outerWidth = width / 2 + 2 * padding;
26605 var outerHeight = height / 2 + 2 * padding;
26606 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26607 if (pointInsidePolygonPoints(x, y, points)) {
26608 return true;
26609 }
26610
26611 // Check bottom right quarter circle
26612 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26613 return true;
26614 }
26615
26616 // Check bottom left quarter circle
26617 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26618 return true;
26619 }
26620 return false;
26621 }
26622 };
26623 };
26624 BRp$2.registerNodeShapes = function () {
26625 var nodeShapes = this.nodeShapes = {};
26626 var renderer = this;
26627 this.generateEllipse();
26628 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26629 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26630 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26631 nodeShapes['square'] = nodeShapes['rectangle'];
26632 this.generateRoundRectangle();
26633 this.generateCutRectangle();
26634 this.generateBarrel();
26635 this.generateBottomRoundrectangle();
26636 {
26637 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26638 this.generatePolygon('diamond', diamondPoints);
26639 this.generateRoundPolygon('round-diamond', diamondPoints);
26640 }
26641 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26642 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26643 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26644 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26645 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26646 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26647 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26648 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26649 var star5Points = new Array(20);
26650 {
26651 var outerPoints = generateUnitNgonPoints(5, 0);
26652 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5);
26653
26654 // Outer radius is 1; inner radius of star is smaller
26655 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26656 innerRadius *= 1.57;
26657 for (var i = 0; i < innerPoints.length / 2; i++) {
26658 innerPoints[i * 2] *= innerRadius;
26659 innerPoints[i * 2 + 1] *= innerRadius;
26660 }
26661 for (var i = 0; i < 20 / 4; i++) {
26662 star5Points[i * 4] = outerPoints[i * 2];
26663 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26664 star5Points[i * 4 + 2] = innerPoints[i * 2];
26665 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26666 }
26667 }
26668 star5Points = fitPolygonToSquare(star5Points);
26669 this.generatePolygon('star', star5Points);
26670 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26671 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26672 this.generatePolygon('right-rhomboid', [-0.333, -1, 1, -1, 0.333, 1, -1, 1]);
26673 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]);
26674 {
26675 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26676 this.generatePolygon('tag', tagPoints);
26677 this.generateRoundPolygon('round-tag', tagPoints);
26678 }
26679 nodeShapes.makePolygon = function (points) {
26680 // use caching on user-specified polygons so they are as fast as native shapes
26681
26682 var key = points.join('$');
26683 var name = 'polygon-' + key;
26684 var shape;
26685 if (shape = this[name]) {
26686 // got cached shape
26687 return shape;
26688 }
26689
26690 // create and cache new shape
26691 return renderer.generatePolygon(name, points);
26692 };
26693 };
26694
26695 var BRp$1 = {};
26696 BRp$1.timeToRender = function () {
26697 return this.redrawTotalTime / this.redrawCount;
26698 };
26699 BRp$1.redraw = function (options) {
26700 options = options || staticEmptyObject();
26701 var r = this;
26702 if (r.averageRedrawTime === undefined) {
26703 r.averageRedrawTime = 0;
26704 }
26705 if (r.lastRedrawTime === undefined) {
26706 r.lastRedrawTime = 0;
26707 }
26708 if (r.lastDrawTime === undefined) {
26709 r.lastDrawTime = 0;
26710 }
26711 r.requestedFrame = true;
26712 r.renderOptions = options;
26713 };
26714 BRp$1.beforeRender = function (fn, priority) {
26715 // the renderer can't add tick callbacks when destroyed
26716 if (this.destroyed) {
26717 return;
26718 }
26719 if (priority == null) {
26720 error('Priority is not optional for beforeRender');
26721 }
26722 var cbs = this.beforeRenderCallbacks;
26723 cbs.push({
26724 fn: fn,
26725 priority: priority
26726 });
26727
26728 // higher priority callbacks executed first
26729 cbs.sort(function (a, b) {
26730 return b.priority - a.priority;
26731 });
26732 };
26733 var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26734 var cbs = r.beforeRenderCallbacks;
26735 for (var i = 0; i < cbs.length; i++) {
26736 cbs[i].fn(willDraw, startTime);
26737 }
26738 };
26739 BRp$1.startRenderLoop = function () {
26740 var r = this;
26741 var cy = r.cy;
26742 if (r.renderLoopStarted) {
26743 return;
26744 } else {
26745 r.renderLoopStarted = true;
26746 }
26747 var renderFn = function renderFn(requestTime) {
26748 if (r.destroyed) {
26749 return;
26750 }
26751 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26752 beforeRenderCallbacks(r, true, requestTime);
26753 var startTime = performanceNow();
26754 r.render(r.renderOptions);
26755 var endTime = r.lastDrawTime = performanceNow();
26756 if (r.averageRedrawTime === undefined) {
26757 r.averageRedrawTime = endTime - startTime;
26758 }
26759 if (r.redrawCount === undefined) {
26760 r.redrawCount = 0;
26761 }
26762 r.redrawCount++;
26763 if (r.redrawTotalTime === undefined) {
26764 r.redrawTotalTime = 0;
26765 }
26766 var duration = endTime - startTime;
26767 r.redrawTotalTime += duration;
26768 r.lastRedrawTime = duration;
26769
26770 // use a weighted average with a bias from the previous average so we don't spike so easily
26771 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26772 r.requestedFrame = false;
26773 } else {
26774 beforeRenderCallbacks(r, false, requestTime);
26775 }
26776 r.skipFrame = false;
26777 requestAnimationFrame(renderFn);
26778 };
26779 requestAnimationFrame(renderFn);
26780 };
26781
26782 var BaseRenderer = function BaseRenderer(options) {
26783 this.init(options);
26784 };
26785 var BR = BaseRenderer;
26786 var BRp = BR.prototype;
26787 BRp.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26788 BRp.init = function (options) {
26789 var r = this;
26790 r.options = options;
26791 r.cy = options.cy;
26792 var ctr = r.container = options.cy.container();
26793 var containerWindow = r.cy.window();
26794
26795 // prepend a stylesheet in the head such that
26796 if (containerWindow) {
26797 var document = containerWindow.document;
26798 var head = document.head;
26799 var stylesheetId = '__________cytoscape_stylesheet';
26800 var className = '__________cytoscape_container';
26801 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26802 if (ctr.className.indexOf(className) < 0) {
26803 ctr.className = (ctr.className || '') + ' ' + className;
26804 }
26805 if (!stylesheetAlreadyExists) {
26806 var stylesheet = document.createElement('style');
26807 stylesheet.id = stylesheetId;
26808 stylesheet.textContent = '.' + className + ' { position: relative; }';
26809 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26810 }
26811
26812 var computedStyle = containerWindow.getComputedStyle(ctr);
26813 var position = computedStyle.getPropertyValue('position');
26814 if (position === 'static') {
26815 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26816 }
26817 }
26818 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26819
26820 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95];
26821
26822 //--Pointer-related data
26823 r.hoverData = {
26824 down: null,
26825 last: null,
26826 downTime: null,
26827 triggerMode: null,
26828 dragging: false,
26829 initialPan: [null, null],
26830 capture: false
26831 };
26832 r.dragData = {
26833 possibleDragElements: []
26834 };
26835 r.touchData = {
26836 start: null,
26837 capture: false,
26838 // These 3 fields related to tap, taphold events
26839 startPosition: [null, null, null, null, null, null],
26840 singleTouchStartTime: null,
26841 singleTouchMoved: true,
26842 now: [null, null, null, null, null, null],
26843 earlier: [null, null, null, null, null, null]
26844 };
26845 r.redraws = 0;
26846 r.showFps = options.showFps;
26847 r.debug = options.debug;
26848 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26849 r.textureOnViewport = options.textureOnViewport;
26850 r.wheelSensitivity = options.wheelSensitivity;
26851 r.motionBlurEnabled = options.motionBlur; // on by default
26852 r.forcedPixelRatio = number$1(options.pixelRatio) ? options.pixelRatio : null;
26853 r.motionBlur = options.motionBlur; // for initial kick off
26854 r.motionBlurOpacity = options.motionBlurOpacity;
26855 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26856 r.motionBlurPxRatio = 1;
26857 r.mbPxRBlurry = 1; //0.8;
26858 r.minMbLowQualFrames = 4;
26859 r.fullQualityMb = false;
26860 r.clearedForMotionBlur = [];
26861 r.desktopTapThreshold = options.desktopTapThreshold;
26862 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26863 r.touchTapThreshold = options.touchTapThreshold;
26864 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26865 r.tapholdDuration = 500;
26866 r.bindings = [];
26867 r.beforeRenderCallbacks = [];
26868 r.beforeRenderPriorities = {
26869 // higher priority execs before lower one
26870 animations: 400,
26871 eleCalcs: 300,
26872 eleTxrDeq: 200,
26873 lyrTxrDeq: 150,
26874 lyrTxrSkip: 100
26875 };
26876 r.registerNodeShapes();
26877 r.registerArrowShapes();
26878 r.registerCalculationListeners();
26879 };
26880 BRp.notify = function (eventName, eles) {
26881 var r = this;
26882 var cy = r.cy;
26883
26884 // the renderer can't be notified after it's destroyed
26885 if (this.destroyed) {
26886 return;
26887 }
26888 if (eventName === 'init') {
26889 r.load();
26890 return;
26891 }
26892 if (eventName === 'destroy') {
26893 r.destroy();
26894 return;
26895 }
26896 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26897 r.invalidateCachedZSortedEles();
26898 }
26899 if (eventName === 'viewport') {
26900 r.redrawHint('select', true);
26901 }
26902 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26903 r.invalidateContainerClientCoordsCache();
26904 r.matchCanvasSize(r.container);
26905 }
26906 r.redrawHint('eles', true);
26907 r.redrawHint('drag', true);
26908 this.startRenderLoop();
26909 this.redraw();
26910 };
26911 BRp.destroy = function () {
26912 var r = this;
26913 r.destroyed = true;
26914 r.cy.stopAnimationLoop();
26915 for (var i = 0; i < r.bindings.length; i++) {
26916 var binding = r.bindings[i];
26917 var b = binding;
26918 var tgt = b.target;
26919 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26920 }
26921 r.bindings = [];
26922 r.beforeRenderCallbacks = [];
26923 r.onUpdateEleCalcsFns = [];
26924 if (r.removeObserver) {
26925 r.removeObserver.disconnect();
26926 }
26927 if (r.styleObserver) {
26928 r.styleObserver.disconnect();
26929 }
26930 if (r.resizeObserver) {
26931 r.resizeObserver.disconnect();
26932 }
26933 if (r.labelCalcDiv) {
26934 try {
26935 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26936 } catch (e) {
26937 // ie10 issue #1014
26938 }
26939 }
26940 };
26941 BRp.isHeadless = function () {
26942 return false;
26943 };
26944 [BRp$f, BRp$5, BRp$4, BRp$3, BRp$2, BRp$1].forEach(function (props) {
26945 extend(BRp, props);
26946 });
26947
26948 var fullFpsTime = 1000 / 60; // assume 60 frames per second
26949
26950 var defs = {
26951 setupDequeueing: function setupDequeueing(opts) {
26952 return function setupDequeueingImpl() {
26953 var self = this;
26954 var r = this.renderer;
26955 if (self.dequeueingSetup) {
26956 return;
26957 } else {
26958 self.dequeueingSetup = true;
26959 }
26960 var queueRedraw = debounce_1(function () {
26961 r.redrawHint('eles', true);
26962 r.redrawHint('drag', true);
26963 r.redraw();
26964 }, opts.deqRedrawThreshold);
26965 var dequeue = function dequeue(willDraw, frameStartTime) {
26966 var startTime = performanceNow();
26967 var avgRenderTime = r.averageRedrawTime;
26968 var renderTime = r.lastRedrawTime;
26969 var deqd = [];
26970 var extent = r.cy.extent();
26971 var pixelRatio = r.getPixelRatio();
26972
26973 // if we aren't in a tick that causes a draw, then the rendered style
26974 // queue won't automatically be flushed before dequeueing starts
26975 if (!willDraw) {
26976 r.flushRenderedStyleQueue();
26977 }
26978 while (true) {
26979 // eslint-disable-line no-constant-condition
26980 var now = performanceNow();
26981 var duration = now - startTime;
26982 var frameDuration = now - frameStartTime;
26983 if (renderTime < fullFpsTime) {
26984 // if we're rendering faster than the ideal fps, then do dequeueing
26985 // during all of the remaining frame time
26986
26987 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26988 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26989 break;
26990 }
26991 } else {
26992 if (willDraw) {
26993 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26994 break;
26995 }
26996 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26997 break;
26998 }
26999 }
27000 var thisDeqd = opts.deq(self, pixelRatio, extent);
27001 if (thisDeqd.length > 0) {
27002 for (var i = 0; i < thisDeqd.length; i++) {
27003 deqd.push(thisDeqd[i]);
27004 }
27005 } else {
27006 break;
27007 }
27008 }
27009
27010 // callbacks on dequeue
27011 if (deqd.length > 0) {
27012 opts.onDeqd(self, deqd);
27013 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27014 queueRedraw();
27015 }
27016 }
27017 };
27018 var priority = opts.priority || noop$1;
27019 r.beforeRender(dequeue, priority(self));
27020 };
27021 }
27022 };
27023
27024 // Allows lookups for (ele, lvl) => cache.
27025 // Uses keys so elements may share the same cache.
27026 var ElementTextureCacheLookup = /*#__PURE__*/function () {
27027 function ElementTextureCacheLookup(getKey) {
27028 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27029 _classCallCheck(this, ElementTextureCacheLookup);
27030 this.idsByKey = new Map$2();
27031 this.keyForId = new Map$2();
27032 this.cachesByLvl = new Map$2();
27033 this.lvls = [];
27034 this.getKey = getKey;
27035 this.doesEleInvalidateKey = doesEleInvalidateKey;
27036 }
27037 _createClass(ElementTextureCacheLookup, [{
27038 key: "getIdsFor",
27039 value: function getIdsFor(key) {
27040 if (key == null) {
27041 error("Can not get id list for null key");
27042 }
27043 var idsByKey = this.idsByKey;
27044 var ids = this.idsByKey.get(key);
27045 if (!ids) {
27046 ids = new Set$1();
27047 idsByKey.set(key, ids);
27048 }
27049 return ids;
27050 }
27051 }, {
27052 key: "addIdForKey",
27053 value: function addIdForKey(key, id) {
27054 if (key != null) {
27055 this.getIdsFor(key).add(id);
27056 }
27057 }
27058 }, {
27059 key: "deleteIdForKey",
27060 value: function deleteIdForKey(key, id) {
27061 if (key != null) {
27062 this.getIdsFor(key)["delete"](id);
27063 }
27064 }
27065 }, {
27066 key: "getNumberOfIdsForKey",
27067 value: function getNumberOfIdsForKey(key) {
27068 if (key == null) {
27069 return 0;
27070 } else {
27071 return this.getIdsFor(key).size;
27072 }
27073 }
27074 }, {
27075 key: "updateKeyMappingFor",
27076 value: function updateKeyMappingFor(ele) {
27077 var id = ele.id();
27078 var prevKey = this.keyForId.get(id);
27079 var currKey = this.getKey(ele);
27080 this.deleteIdForKey(prevKey, id);
27081 this.addIdForKey(currKey, id);
27082 this.keyForId.set(id, currKey);
27083 }
27084 }, {
27085 key: "deleteKeyMappingFor",
27086 value: function deleteKeyMappingFor(ele) {
27087 var id = ele.id();
27088 var prevKey = this.keyForId.get(id);
27089 this.deleteIdForKey(prevKey, id);
27090 this.keyForId["delete"](id);
27091 }
27092 }, {
27093 key: "keyHasChangedFor",
27094 value: function keyHasChangedFor(ele) {
27095 var id = ele.id();
27096 var prevKey = this.keyForId.get(id);
27097 var newKey = this.getKey(ele);
27098 return prevKey !== newKey;
27099 }
27100 }, {
27101 key: "isInvalid",
27102 value: function isInvalid(ele) {
27103 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27104 }
27105 }, {
27106 key: "getCachesAt",
27107 value: function getCachesAt(lvl) {
27108 var cachesByLvl = this.cachesByLvl,
27109 lvls = this.lvls;
27110 var caches = cachesByLvl.get(lvl);
27111 if (!caches) {
27112 caches = new Map$2();
27113 cachesByLvl.set(lvl, caches);
27114 lvls.push(lvl);
27115 }
27116 return caches;
27117 }
27118 }, {
27119 key: "getCache",
27120 value: function getCache(key, lvl) {
27121 return this.getCachesAt(lvl).get(key);
27122 }
27123 }, {
27124 key: "get",
27125 value: function get(ele, lvl) {
27126 var key = this.getKey(ele);
27127 var cache = this.getCache(key, lvl);
27128
27129 // getting for an element may need to add to the id list b/c eles can share keys
27130 if (cache != null) {
27131 this.updateKeyMappingFor(ele);
27132 }
27133 return cache;
27134 }
27135 }, {
27136 key: "getForCachedKey",
27137 value: function getForCachedKey(ele, lvl) {
27138 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27139 var cache = this.getCache(key, lvl);
27140 return cache;
27141 }
27142 }, {
27143 key: "hasCache",
27144 value: function hasCache(key, lvl) {
27145 return this.getCachesAt(lvl).has(key);
27146 }
27147 }, {
27148 key: "has",
27149 value: function has(ele, lvl) {
27150 var key = this.getKey(ele);
27151 return this.hasCache(key, lvl);
27152 }
27153 }, {
27154 key: "setCache",
27155 value: function setCache(key, lvl, cache) {
27156 cache.key = key;
27157 this.getCachesAt(lvl).set(key, cache);
27158 }
27159 }, {
27160 key: "set",
27161 value: function set(ele, lvl, cache) {
27162 var key = this.getKey(ele);
27163 this.setCache(key, lvl, cache);
27164 this.updateKeyMappingFor(ele);
27165 }
27166 }, {
27167 key: "deleteCache",
27168 value: function deleteCache(key, lvl) {
27169 this.getCachesAt(lvl)["delete"](key);
27170 }
27171 }, {
27172 key: "delete",
27173 value: function _delete(ele, lvl) {
27174 var key = this.getKey(ele);
27175 this.deleteCache(key, lvl);
27176 }
27177 }, {
27178 key: "invalidateKey",
27179 value: function invalidateKey(key) {
27180 var _this = this;
27181 this.lvls.forEach(function (lvl) {
27182 return _this.deleteCache(key, lvl);
27183 });
27184 }
27185
27186 // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27187 }, {
27188 key: "invalidate",
27189 value: function invalidate(ele) {
27190 var id = ele.id();
27191 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27192
27193 this.deleteKeyMappingFor(ele);
27194 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27195 if (entireKeyInvalidated) {
27196 // clear mapping for current key
27197 this.invalidateKey(key);
27198 }
27199 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27200 }
27201 }]);
27202 return ElementTextureCacheLookup;
27203 }();
27204
27205 var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27206 var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27207 var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27208 var maxLvl$1 = 3; // when larger than this scale just render directly (caching is not helpful)
27209 var maxZoom$1 = 7.99; // beyond this zoom level, layered textures are not used
27210 var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27211 var defTxrWidth = 1024; // default/minimum texture width
27212 var maxTxrW = 1024; // the maximum width of a texture
27213 var maxTxrH = 1024; // the maximum height of a texture
27214 var minUtility = 0.2; // if usage of texture is less than this, it is retired
27215 var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27216 var maxFullnessChecks = 10; // dequeued after this many checks
27217 var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27218 var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27219 var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27220 var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27221 var deqRedrawThreshold$1 = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27222 var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27223
27224 var getTxrReasons = {
27225 dequeue: 'dequeue',
27226 downscale: 'downscale',
27227 highQuality: 'highQuality'
27228 };
27229 var initDefaults = defaults$g({
27230 getKey: null,
27231 doesEleInvalidateKey: falsify,
27232 drawElement: null,
27233 getBoundingBox: null,
27234 getRotationPoint: null,
27235 getRotationOffset: null,
27236 isVisible: trueify,
27237 allowEdgeTxrCaching: true,
27238 allowParentTxrCaching: true
27239 });
27240 var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27241 var self = this;
27242 self.renderer = renderer;
27243 self.onDequeues = [];
27244 var opts = initDefaults(initOptions);
27245 extend(self, opts);
27246 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27247 self.setupDequeueing();
27248 };
27249 var ETCp = ElementTextureCache.prototype;
27250 ETCp.reasons = getTxrReasons;
27251
27252 // the list of textures in which new subtextures for elements can be placed
27253 ETCp.getTextureQueue = function (txrH) {
27254 var self = this;
27255 self.eleImgCaches = self.eleImgCaches || {};
27256 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27257 };
27258
27259 // the list of usused textures which can be recycled (in use in texture queue)
27260 ETCp.getRetiredTextureQueue = function (txrH) {
27261 var self = this;
27262 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27263 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27264 return rtxtrQ;
27265 };
27266
27267 // queue of element draw requests at different scale levels
27268 ETCp.getElementQueue = function () {
27269 var self = this;
27270 var q = self.eleCacheQueue = self.eleCacheQueue || new heap(function (a, b) {
27271 return b.reqs - a.reqs;
27272 });
27273 return q;
27274 };
27275
27276 // queue of element draw requests at different scale levels (element id lookup)
27277 ETCp.getElementKeyToQueue = function () {
27278 var self = this;
27279 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27280 return k2q;
27281 };
27282 ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27283 var self = this;
27284 var r = this.renderer;
27285 var zoom = r.cy.zoom();
27286 var lookup = this.lookup;
27287 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27288 return null;
27289 }
27290 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27291 return null;
27292 }
27293 if (lvl == null) {
27294 lvl = Math.ceil(log2(zoom * pxRatio));
27295 }
27296 if (lvl < minLvl$1) {
27297 lvl = minLvl$1;
27298 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27299 return null;
27300 }
27301 var scale = Math.pow(2, lvl);
27302 var eleScaledH = bb.h * scale;
27303 var eleScaledW = bb.w * scale;
27304 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27305 if (!this.isVisible(ele, scaledLabelShown)) {
27306 return null;
27307 }
27308 var eleCache = lookup.get(ele, lvl);
27309
27310 // if this get was on an unused/invalidated cache, then restore the texture usage metric
27311 if (eleCache && eleCache.invalidated) {
27312 eleCache.invalidated = false;
27313 eleCache.texture.invalidatedWidth -= eleCache.width;
27314 }
27315 if (eleCache) {
27316 return eleCache;
27317 }
27318 var txrH; // which texture height this ele belongs to
27319
27320 if (eleScaledH <= minTxrH) {
27321 txrH = minTxrH;
27322 } else if (eleScaledH <= txrStepH) {
27323 txrH = txrStepH;
27324 } else {
27325 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27326 }
27327 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27328 return null; // caching large elements is not efficient
27329 }
27330
27331 var txrQ = self.getTextureQueue(txrH);
27332
27333 // first try the second last one in case it has space at the end
27334 var txr = txrQ[txrQ.length - 2];
27335 var addNewTxr = function addNewTxr() {
27336 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27337 };
27338
27339 // try the last one if there is no second last one
27340 if (!txr) {
27341 txr = txrQ[txrQ.length - 1];
27342 }
27343
27344 // if the last one doesn't exist, we need a first one
27345 if (!txr) {
27346 txr = addNewTxr();
27347 }
27348
27349 // if there's no room in the current texture, we need a new one
27350 if (txr.width - txr.usedWidth < eleScaledW) {
27351 txr = addNewTxr();
27352 }
27353 var scalableFrom = function scalableFrom(otherCache) {
27354 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27355 };
27356 var deqing = reason && reason === getTxrReasons.dequeue;
27357 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27358 var downscaleReq = reason && reason === getTxrReasons.downscale;
27359 var higherCache; // the nearest cache with a higher level
27360 for (var l = lvl + 1; l <= maxLvl$1; l++) {
27361 var c = lookup.get(ele, l);
27362 if (c) {
27363 higherCache = c;
27364 break;
27365 }
27366 }
27367 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27368 var downscale = function downscale() {
27369 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27370 };
27371
27372 // reset ele area in texture
27373 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27374 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27375 if (scalableFrom(oneUpCache)) {
27376 // then we can relatively cheaply rescale the existing image w/o rerendering
27377 downscale();
27378 } else if (scalableFrom(higherCache)) {
27379 // then use the higher cache for now and queue the next level down
27380 // to cheaply scale towards the smaller level
27381
27382 if (highQualityReq) {
27383 for (var _l = higherCache.level; _l > lvl; _l--) {
27384 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27385 }
27386 downscale();
27387 } else {
27388 self.queueElement(ele, higherCache.level - 1);
27389 return higherCache;
27390 }
27391 } else {
27392 var lowerCache; // the nearest cache with a lower level
27393 if (!deqing && !highQualityReq && !downscaleReq) {
27394 for (var _l2 = lvl - 1; _l2 >= minLvl$1; _l2--) {
27395 var _c = lookup.get(ele, _l2);
27396 if (_c) {
27397 lowerCache = _c;
27398 break;
27399 }
27400 }
27401 }
27402 if (scalableFrom(lowerCache)) {
27403 // then use the lower quality cache for now and queue the better one for later
27404
27405 self.queueElement(ele, lvl);
27406 return lowerCache;
27407 }
27408 txr.context.translate(txr.usedWidth, 0);
27409 txr.context.scale(scale, scale);
27410 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27411 txr.context.scale(1 / scale, 1 / scale);
27412 txr.context.translate(-txr.usedWidth, 0);
27413 }
27414 eleCache = {
27415 x: txr.usedWidth,
27416 texture: txr,
27417 level: lvl,
27418 scale: scale,
27419 width: eleScaledW,
27420 height: eleScaledH,
27421 scaledLabelShown: scaledLabelShown
27422 };
27423 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27424 txr.eleCaches.push(eleCache);
27425 lookup.set(ele, lvl, eleCache);
27426 self.checkTextureFullness(txr);
27427 return eleCache;
27428 };
27429 ETCp.invalidateElements = function (eles) {
27430 for (var i = 0; i < eles.length; i++) {
27431 this.invalidateElement(eles[i]);
27432 }
27433 };
27434 ETCp.invalidateElement = function (ele) {
27435 var self = this;
27436 var lookup = self.lookup;
27437 var caches = [];
27438 var invalid = lookup.isInvalid(ele);
27439 if (!invalid) {
27440 return; // override the invalidation request if the element key has not changed
27441 }
27442
27443 for (var lvl = minLvl$1; lvl <= maxLvl$1; lvl++) {
27444 var cache = lookup.getForCachedKey(ele, lvl);
27445 if (cache) {
27446 caches.push(cache);
27447 }
27448 }
27449 var noOtherElesUseCache = lookup.invalidate(ele);
27450 if (noOtherElesUseCache) {
27451 for (var i = 0; i < caches.length; i++) {
27452 var _cache = caches[i];
27453 var txr = _cache.texture;
27454
27455 // remove space from the texture it belongs to
27456 txr.invalidatedWidth += _cache.width;
27457
27458 // mark the cache as invalidated
27459 _cache.invalidated = true;
27460
27461 // retire the texture if its utility is low
27462 self.checkTextureUtility(txr);
27463 }
27464 }
27465
27466 // remove from queue since the old req was for the old state
27467 self.removeFromQueue(ele);
27468 };
27469 ETCp.checkTextureUtility = function (txr) {
27470 // invalidate all entries in the cache if the cache size is small
27471 if (txr.invalidatedWidth >= minUtility * txr.width) {
27472 this.retireTexture(txr);
27473 }
27474 };
27475 ETCp.checkTextureFullness = function (txr) {
27476 // if texture has been mostly filled and passed over several times, remove
27477 // it from the queue so we don't need to waste time looking at it to put new things
27478
27479 var self = this;
27480 var txrQ = self.getTextureQueue(txr.height);
27481 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27482 removeFromArray(txrQ, txr);
27483 } else {
27484 txr.fullnessChecks++;
27485 }
27486 };
27487 ETCp.retireTexture = function (txr) {
27488 var self = this;
27489 var txrH = txr.height;
27490 var txrQ = self.getTextureQueue(txrH);
27491 var lookup = this.lookup;
27492
27493 // retire the texture from the active / searchable queue:
27494
27495 removeFromArray(txrQ, txr);
27496 txr.retired = true;
27497
27498 // remove the refs from the eles to the caches:
27499
27500 var eleCaches = txr.eleCaches;
27501 for (var i = 0; i < eleCaches.length; i++) {
27502 var eleCache = eleCaches[i];
27503 lookup.deleteCache(eleCache.key, eleCache.level);
27504 }
27505 clearArray(eleCaches);
27506
27507 // add the texture to a retired queue so it can be recycled in future:
27508
27509 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27510 rtxtrQ.push(txr);
27511 };
27512 ETCp.addTexture = function (txrH, minW) {
27513 var self = this;
27514 var txrQ = self.getTextureQueue(txrH);
27515 var txr = {};
27516 txrQ.push(txr);
27517 txr.eleCaches = [];
27518 txr.height = txrH;
27519 txr.width = Math.max(defTxrWidth, minW);
27520 txr.usedWidth = 0;
27521 txr.invalidatedWidth = 0;
27522 txr.fullnessChecks = 0;
27523 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27524 txr.context = txr.canvas.getContext('2d');
27525 return txr;
27526 };
27527 ETCp.recycleTexture = function (txrH, minW) {
27528 var self = this;
27529 var txrQ = self.getTextureQueue(txrH);
27530 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27531 for (var i = 0; i < rtxtrQ.length; i++) {
27532 var txr = rtxtrQ[i];
27533 if (txr.width >= minW) {
27534 txr.retired = false;
27535 txr.usedWidth = 0;
27536 txr.invalidatedWidth = 0;
27537 txr.fullnessChecks = 0;
27538 clearArray(txr.eleCaches);
27539 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27540 txr.context.clearRect(0, 0, txr.width, txr.height);
27541 removeFromArray(rtxtrQ, txr);
27542 txrQ.push(txr);
27543 return txr;
27544 }
27545 }
27546 };
27547 ETCp.queueElement = function (ele, lvl) {
27548 var self = this;
27549 var q = self.getElementQueue();
27550 var k2q = self.getElementKeyToQueue();
27551 var key = this.getKey(ele);
27552 var existingReq = k2q[key];
27553 if (existingReq) {
27554 // use the max lvl b/c in between lvls are cheap to make
27555 existingReq.level = Math.max(existingReq.level, lvl);
27556 existingReq.eles.merge(ele);
27557 existingReq.reqs++;
27558 q.updateItem(existingReq);
27559 } else {
27560 var req = {
27561 eles: ele.spawn().merge(ele),
27562 level: lvl,
27563 reqs: 1,
27564 key: key
27565 };
27566 q.push(req);
27567 k2q[key] = req;
27568 }
27569 };
27570 ETCp.dequeue = function (pxRatio /*, extent*/) {
27571 var self = this;
27572 var q = self.getElementQueue();
27573 var k2q = self.getElementKeyToQueue();
27574 var dequeued = [];
27575 var lookup = self.lookup;
27576 for (var i = 0; i < maxDeqSize$1; i++) {
27577 if (q.size() > 0) {
27578 var req = q.pop();
27579 var key = req.key;
27580 var ele = req.eles[0]; // all eles have the same key
27581 var cacheExists = lookup.hasCache(ele, req.level);
27582
27583 // clear out the key to req lookup
27584 k2q[key] = null;
27585
27586 // dequeueing isn't necessary with an existing cache
27587 if (cacheExists) {
27588 continue;
27589 }
27590 dequeued.push(req);
27591 var bb = self.getBoundingBox(ele);
27592 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27593 } else {
27594 break;
27595 }
27596 }
27597 return dequeued;
27598 };
27599 ETCp.removeFromQueue = function (ele) {
27600 var self = this;
27601 var q = self.getElementQueue();
27602 var k2q = self.getElementKeyToQueue();
27603 var key = this.getKey(ele);
27604 var req = k2q[key];
27605 if (req != null) {
27606 if (req.eles.length === 1) {
27607 // remove if last ele in the req
27608 // bring to front of queue
27609 req.reqs = MAX_INT$1;
27610 q.updateItem(req);
27611 q.pop(); // remove from queue
27612
27613 k2q[key] = null; // remove from lookup map
27614 } else {
27615 // otherwise just remove ele from req
27616 req.eles.unmerge(ele);
27617 }
27618 }
27619 };
27620 ETCp.onDequeue = function (fn) {
27621 this.onDequeues.push(fn);
27622 };
27623 ETCp.offDequeue = function (fn) {
27624 removeFromArray(this.onDequeues, fn);
27625 };
27626 ETCp.setupDequeueing = defs.setupDequeueing({
27627 deqRedrawThreshold: deqRedrawThreshold$1,
27628 deqCost: deqCost$1,
27629 deqAvgCost: deqAvgCost$1,
27630 deqNoDrawCost: deqNoDrawCost$1,
27631 deqFastCost: deqFastCost$1,
27632 deq: function deq(self, pxRatio, extent) {
27633 return self.dequeue(pxRatio, extent);
27634 },
27635 onDeqd: function onDeqd(self, deqd) {
27636 for (var i = 0; i < self.onDequeues.length; i++) {
27637 var fn = self.onDequeues[i];
27638 fn(deqd);
27639 }
27640 },
27641 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27642 for (var i = 0; i < deqd.length; i++) {
27643 var eles = deqd[i].eles;
27644 for (var j = 0; j < eles.length; j++) {
27645 var bb = eles[j].boundingBox();
27646 if (boundingBoxesIntersect(bb, extent)) {
27647 return true;
27648 }
27649 }
27650 }
27651 return false;
27652 },
27653 priority: function priority(self) {
27654 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27655 }
27656 });
27657
27658 var defNumLayers = 1; // default number of layers to use
27659 var minLvl = -4; // when scaling smaller than that we don't need to re-render
27660 var maxLvl = 2; // when larger than this scale just render directly (caching is not helpful)
27661 var maxZoom = 3.99; // beyond this zoom level, layered textures are not used
27662 var deqRedrawThreshold = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27663 var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27664 var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27665 var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27666 var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27667 var deqFastCost = 0.9; // % of frame time to be used when >60fps
27668 var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27669 var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27670 var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27671 var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27672
27673 // var log = function(){ console.log.apply( console, arguments ); };
27674
27675 var LayeredTextureCache = function LayeredTextureCache(renderer) {
27676 var self = this;
27677 var r = self.renderer = renderer;
27678 var cy = r.cy;
27679 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27680
27681 self.firstGet = true;
27682 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27683 self.skipping = false;
27684 self.eleTxrDeqs = cy.collection();
27685 self.scheduleElementRefinement = debounce_1(function () {
27686 self.refineElementTextures(self.eleTxrDeqs);
27687 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27688 }, refineEleDebounceTime);
27689 r.beforeRender(function (willDraw, now) {
27690 if (now - self.lastInvalidationTime <= invalidThreshold) {
27691 self.skipping = true;
27692 } else {
27693 self.skipping = false;
27694 }
27695 }, r.beforeRenderPriorities.lyrTxrSkip);
27696 var qSort = function qSort(a, b) {
27697 return b.reqs - a.reqs;
27698 };
27699 self.layersQueue = new heap(qSort);
27700 self.setupDequeueing();
27701 };
27702 var LTCp = LayeredTextureCache.prototype;
27703 var layerIdPool = 0;
27704 var MAX_INT = Math.pow(2, 53) - 1;
27705 LTCp.makeLayer = function (bb, lvl) {
27706 var scale = Math.pow(2, lvl);
27707 var w = Math.ceil(bb.w * scale);
27708 var h = Math.ceil(bb.h * scale);
27709 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27710 var layer = {
27711 id: layerIdPool = ++layerIdPool % MAX_INT,
27712 bb: bb,
27713 level: lvl,
27714 width: w,
27715 height: h,
27716 canvas: canvas,
27717 context: canvas.getContext('2d'),
27718 eles: [],
27719 elesQueue: [],
27720 reqs: 0
27721 };
27722
27723 // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27724
27725 var cxt = layer.context;
27726 var dx = -layer.bb.x1;
27727 var dy = -layer.bb.y1;
27728
27729 // do the transform on creation to save cycles (it's the same for all eles)
27730 cxt.scale(scale, scale);
27731 cxt.translate(dx, dy);
27732 return layer;
27733 };
27734 LTCp.getLayers = function (eles, pxRatio, lvl) {
27735 var self = this;
27736 var r = self.renderer;
27737 var cy = r.cy;
27738 var zoom = cy.zoom();
27739 var firstGet = self.firstGet;
27740 self.firstGet = false;
27741
27742 // log('--\nget layers with %s eles', eles.length);
27743 //log eles.map(function(ele){ return ele.id() }) );
27744
27745 if (lvl == null) {
27746 lvl = Math.ceil(log2(zoom * pxRatio));
27747 if (lvl < minLvl) {
27748 lvl = minLvl;
27749 } else if (zoom >= maxZoom || lvl > maxLvl) {
27750 return null;
27751 }
27752 }
27753 self.validateLayersElesOrdering(lvl, eles);
27754 var layersByLvl = self.layersByLevel;
27755 var scale = Math.pow(2, lvl);
27756 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27757 var bb;
27758 var lvlComplete = self.levelIsComplete(lvl, eles);
27759 var tmpLayers;
27760 var checkTempLevels = function checkTempLevels() {
27761 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27762 self.validateLayersElesOrdering(l, eles);
27763 if (self.levelIsComplete(l, eles)) {
27764 tmpLayers = layersByLvl[l];
27765 return true;
27766 }
27767 };
27768 var checkLvls = function checkLvls(dir) {
27769 if (tmpLayers) {
27770 return;
27771 }
27772 for (var l = lvl + dir; minLvl <= l && l <= maxLvl; l += dir) {
27773 if (canUseAsTmpLvl(l)) {
27774 break;
27775 }
27776 }
27777 };
27778 checkLvls(+1);
27779 checkLvls(-1);
27780
27781 // remove the invalid layers; they will be replaced as needed later in this function
27782 for (var i = layers.length - 1; i >= 0; i--) {
27783 var layer = layers[i];
27784 if (layer.invalid) {
27785 removeFromArray(layers, layer);
27786 }
27787 }
27788 };
27789 if (!lvlComplete) {
27790 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27791 // and later queue the current layerset so we can get the proper quality level soon
27792
27793 checkTempLevels();
27794 } else {
27795 // log('level complete, using existing layers\n--');
27796 return layers;
27797 }
27798 var getBb = function getBb() {
27799 if (!bb) {
27800 bb = makeBoundingBox();
27801 for (var i = 0; i < eles.length; i++) {
27802 updateBoundingBox(bb, eles[i].boundingBox());
27803 }
27804 }
27805 return bb;
27806 };
27807 var makeLayer = function makeLayer(opts) {
27808 opts = opts || {};
27809 var after = opts.after;
27810 getBb();
27811 var area = bb.w * scale * (bb.h * scale);
27812 if (area > maxLayerArea) {
27813 return null;
27814 }
27815 var layer = self.makeLayer(bb, lvl);
27816 if (after != null) {
27817 var index = layers.indexOf(after) + 1;
27818 layers.splice(index, 0, layer);
27819 } else if (opts.insert === undefined || opts.insert) {
27820 // no after specified => first layer made so put at start
27821 layers.unshift(layer);
27822 }
27823
27824 // if( tmpLayers ){
27825 //self.queueLayer( layer );
27826 // }
27827
27828 return layer;
27829 };
27830 if (self.skipping && !firstGet) {
27831 // log('skip layers');
27832 return null;
27833 }
27834
27835 // log('do layers');
27836
27837 var layer = null;
27838 var maxElesPerLayer = eles.length / defNumLayers;
27839 var allowLazyQueueing = !firstGet;
27840 for (var i = 0; i < eles.length; i++) {
27841 var ele = eles[i];
27842 var rs = ele._private.rscratch;
27843 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27844
27845 // log('look at ele', ele.id());
27846
27847 var existingLayer = caches[lvl];
27848 if (existingLayer) {
27849 // reuse layer for later eles
27850 // log('reuse layer for', ele.id());
27851 layer = existingLayer;
27852 continue;
27853 }
27854 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27855 // log('make new layer for ele %s', ele.id());
27856
27857 layer = makeLayer({
27858 insert: true,
27859 after: layer
27860 });
27861
27862 // if now layer can be built then we can't use layers at this level
27863 if (!layer) {
27864 return null;
27865 }
27866
27867 // log('new layer with id %s', layer.id);
27868 }
27869
27870 if (tmpLayers || allowLazyQueueing) {
27871 // log('queue ele %s in layer %s', ele.id(), layer.id);
27872 self.queueLayer(layer, ele);
27873 } else {
27874 // log('draw ele %s in layer %s', ele.id(), layer.id);
27875 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27876 }
27877 layer.eles.push(ele);
27878 caches[lvl] = layer;
27879 }
27880
27881 // log('--');
27882
27883 if (tmpLayers) {
27884 // then we only queued the current layerset and can't draw it yet
27885 return tmpLayers;
27886 }
27887 if (allowLazyQueueing) {
27888 // log('lazy queue level', lvl);
27889 return null;
27890 }
27891 return layers;
27892 };
27893
27894 // a layer may want to use an ele cache of a higher level to avoid blurriness
27895 // so the layer level might not equal the ele level
27896 LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27897 return lvl;
27898 };
27899 LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27900 var self = this;
27901 var r = this.renderer;
27902 var context = layer.context;
27903 var bb = ele.boundingBox();
27904 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27905 return;
27906 }
27907 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27908 {
27909 r.setImgSmoothing(context, false);
27910 }
27911 {
27912 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27913 }
27914 {
27915 r.setImgSmoothing(context, true);
27916 }
27917 };
27918 LTCp.levelIsComplete = function (lvl, eles) {
27919 var self = this;
27920 var layers = self.layersByLevel[lvl];
27921 if (!layers || layers.length === 0) {
27922 return false;
27923 }
27924 var numElesInLayers = 0;
27925 for (var i = 0; i < layers.length; i++) {
27926 var layer = layers[i];
27927
27928 // if there are any eles needed to be drawn yet, the level is not complete
27929 if (layer.reqs > 0) {
27930 return false;
27931 }
27932
27933 // if the layer is invalid, the level is not complete
27934 if (layer.invalid) {
27935 return false;
27936 }
27937 numElesInLayers += layer.eles.length;
27938 }
27939
27940 // we should have exactly the number of eles passed in to be complete
27941 if (numElesInLayers !== eles.length) {
27942 return false;
27943 }
27944 return true;
27945 };
27946 LTCp.validateLayersElesOrdering = function (lvl, eles) {
27947 var layers = this.layersByLevel[lvl];
27948 if (!layers) {
27949 return;
27950 }
27951
27952 // if in a layer the eles are not in the same order, then the layer is invalid
27953 // (i.e. there is an ele in between the eles in the layer)
27954
27955 for (var i = 0; i < layers.length; i++) {
27956 var layer = layers[i];
27957 var offset = -1;
27958
27959 // find the offset
27960 for (var j = 0; j < eles.length; j++) {
27961 if (layer.eles[0] === eles[j]) {
27962 offset = j;
27963 break;
27964 }
27965 }
27966 if (offset < 0) {
27967 // then the layer has nonexistent elements and is invalid
27968 this.invalidateLayer(layer);
27969 continue;
27970 }
27971
27972 // the eles in the layer must be in the same continuous order, else the layer is invalid
27973
27974 var o = offset;
27975 for (var j = 0; j < layer.eles.length; j++) {
27976 if (layer.eles[j] !== eles[o + j]) {
27977 // log('invalidate based on ordering', layer.id);
27978
27979 this.invalidateLayer(layer);
27980 break;
27981 }
27982 }
27983 }
27984 };
27985 LTCp.updateElementsInLayers = function (eles, update) {
27986 var self = this;
27987 var isEles = element(eles[0]);
27988
27989 // collect udpated elements (cascaded from the layers) and update each
27990 // layer itself along the way
27991 for (var i = 0; i < eles.length; i++) {
27992 var req = isEles ? null : eles[i];
27993 var ele = isEles ? eles[i] : eles[i].ele;
27994 var rs = ele._private.rscratch;
27995 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
27996 for (var l = minLvl; l <= maxLvl; l++) {
27997 var layer = caches[l];
27998 if (!layer) {
27999 continue;
28000 }
28001
28002 // if update is a request from the ele cache, then it affects only
28003 // the matching level
28004 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28005 continue;
28006 }
28007 update(layer, ele, req);
28008 }
28009 }
28010 };
28011 LTCp.haveLayers = function () {
28012 var self = this;
28013 var haveLayers = false;
28014 for (var l = minLvl; l <= maxLvl; l++) {
28015 var layers = self.layersByLevel[l];
28016 if (layers && layers.length > 0) {
28017 haveLayers = true;
28018 break;
28019 }
28020 }
28021 return haveLayers;
28022 };
28023 LTCp.invalidateElements = function (eles) {
28024 var self = this;
28025 if (eles.length === 0) {
28026 return;
28027 }
28028 self.lastInvalidationTime = performanceNow();
28029
28030 // log('update invalidate layer time from eles');
28031
28032 if (eles.length === 0 || !self.haveLayers()) {
28033 return;
28034 }
28035 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28036 self.invalidateLayer(layer);
28037 });
28038 };
28039 LTCp.invalidateLayer = function (layer) {
28040 // log('update invalidate layer time');
28041
28042 this.lastInvalidationTime = performanceNow();
28043 if (layer.invalid) {
28044 return;
28045 } // save cycles
28046
28047 var lvl = layer.level;
28048 var eles = layer.eles;
28049 var layers = this.layersByLevel[lvl];
28050
28051 // log('invalidate layer', layer.id );
28052
28053 removeFromArray(layers, layer);
28054 // layer.eles = [];
28055
28056 layer.elesQueue = [];
28057 layer.invalid = true;
28058 if (layer.replacement) {
28059 layer.replacement.invalid = true;
28060 }
28061 for (var i = 0; i < eles.length; i++) {
28062 var caches = eles[i]._private.rscratch.imgLayerCaches;
28063 if (caches) {
28064 caches[lvl] = null;
28065 }
28066 }
28067 };
28068 LTCp.refineElementTextures = function (eles) {
28069 var self = this;
28070
28071 // log('refine', eles.length);
28072
28073 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28074 var rLyr = layer.replacement;
28075 if (!rLyr) {
28076 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28077 rLyr.replaces = layer;
28078 rLyr.eles = layer.eles;
28079
28080 // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28081 }
28082
28083 if (!rLyr.reqs) {
28084 for (var i = 0; i < rLyr.eles.length; i++) {
28085 self.queueLayer(rLyr, rLyr.eles[i]);
28086 }
28087
28088 // log('queue replacement layer refinement', rLyr.id);
28089 }
28090 });
28091 };
28092
28093 LTCp.enqueueElementRefinement = function (ele) {
28094 this.eleTxrDeqs.merge(ele);
28095 this.scheduleElementRefinement();
28096 };
28097 LTCp.queueLayer = function (layer, ele) {
28098 var self = this;
28099 var q = self.layersQueue;
28100 var elesQ = layer.elesQueue;
28101 var hasId = elesQ.hasId = elesQ.hasId || {};
28102
28103 // if a layer is going to be replaced, queuing is a waste of time
28104 if (layer.replacement) {
28105 return;
28106 }
28107 if (ele) {
28108 if (hasId[ele.id()]) {
28109 return;
28110 }
28111 elesQ.push(ele);
28112 hasId[ele.id()] = true;
28113 }
28114 if (layer.reqs) {
28115 layer.reqs++;
28116 q.updateItem(layer);
28117 } else {
28118 layer.reqs = 1;
28119 q.push(layer);
28120 }
28121 };
28122 LTCp.dequeue = function (pxRatio) {
28123 var self = this;
28124 var q = self.layersQueue;
28125 var deqd = [];
28126 var eleDeqs = 0;
28127 while (eleDeqs < maxDeqSize) {
28128 if (q.size() === 0) {
28129 break;
28130 }
28131 var layer = q.peek();
28132
28133 // if a layer has been or will be replaced, then don't waste time with it
28134 if (layer.replacement) {
28135 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28136 q.pop();
28137 continue;
28138 }
28139
28140 // if this is a replacement layer that has been superceded, then forget it
28141 if (layer.replaces && layer !== layer.replaces.replacement) {
28142 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28143 q.pop();
28144 continue;
28145 }
28146 if (layer.invalid) {
28147 // log('replacement layer %s is invalid; dequeued', layer.id);
28148 q.pop();
28149 continue;
28150 }
28151 var ele = layer.elesQueue.shift();
28152 if (ele) {
28153 // log('dequeue layer %s', layer.id);
28154
28155 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28156 eleDeqs++;
28157 }
28158 if (deqd.length === 0) {
28159 // we need only one entry in deqd to queue redrawing etc
28160 deqd.push(true);
28161 }
28162
28163 // if the layer has all its eles done, then remove from the queue
28164 if (layer.elesQueue.length === 0) {
28165 q.pop();
28166 layer.reqs = 0;
28167
28168 // log('dequeue of layer %s complete', layer.id);
28169
28170 // when a replacement layer is dequeued, it replaces the old layer in the level
28171 if (layer.replaces) {
28172 self.applyLayerReplacement(layer);
28173 }
28174 self.requestRedraw();
28175 }
28176 }
28177 return deqd;
28178 };
28179 LTCp.applyLayerReplacement = function (layer) {
28180 var self = this;
28181 var layersInLevel = self.layersByLevel[layer.level];
28182 var replaced = layer.replaces;
28183 var index = layersInLevel.indexOf(replaced);
28184
28185 // if the replaced layer is not in the active list for the level, then replacing
28186 // refs would be a mistake (i.e. overwriting the true active layer)
28187 if (index < 0 || replaced.invalid) {
28188 // log('replacement layer would have no effect', layer.id);
28189 return;
28190 }
28191 layersInLevel[index] = layer; // replace level ref
28192
28193 // replace refs in eles
28194 for (var i = 0; i < layer.eles.length; i++) {
28195 var _p = layer.eles[i]._private;
28196 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28197 if (cache) {
28198 cache[layer.level] = layer;
28199 }
28200 }
28201
28202 // log('apply replacement layer %s over %s', layer.id, replaced.id);
28203
28204 self.requestRedraw();
28205 };
28206 LTCp.requestRedraw = debounce_1(function () {
28207 var r = this.renderer;
28208 r.redrawHint('eles', true);
28209 r.redrawHint('drag', true);
28210 r.redraw();
28211 }, 100);
28212 LTCp.setupDequeueing = defs.setupDequeueing({
28213 deqRedrawThreshold: deqRedrawThreshold,
28214 deqCost: deqCost,
28215 deqAvgCost: deqAvgCost,
28216 deqNoDrawCost: deqNoDrawCost,
28217 deqFastCost: deqFastCost,
28218 deq: function deq(self, pxRatio) {
28219 return self.dequeue(pxRatio);
28220 },
28221 onDeqd: noop$1,
28222 shouldRedraw: trueify,
28223 priority: function priority(self) {
28224 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28225 }
28226 });
28227
28228 var CRp$a = {};
28229 var impl;
28230 function polygon(context, points) {
28231 for (var i = 0; i < points.length; i++) {
28232 var pt = points[i];
28233 context.lineTo(pt.x, pt.y);
28234 }
28235 }
28236 function triangleBackcurve(context, points, controlPoint) {
28237 var firstPt;
28238 for (var i = 0; i < points.length; i++) {
28239 var pt = points[i];
28240 if (i === 0) {
28241 firstPt = pt;
28242 }
28243 context.lineTo(pt.x, pt.y);
28244 }
28245 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28246 }
28247 function triangleTee(context, trianglePoints, teePoints) {
28248 if (context.beginPath) {
28249 context.beginPath();
28250 }
28251 var triPts = trianglePoints;
28252 for (var i = 0; i < triPts.length; i++) {
28253 var pt = triPts[i];
28254 context.lineTo(pt.x, pt.y);
28255 }
28256 var teePts = teePoints;
28257 var firstTeePt = teePoints[0];
28258 context.moveTo(firstTeePt.x, firstTeePt.y);
28259 for (var i = 1; i < teePts.length; i++) {
28260 var pt = teePts[i];
28261 context.lineTo(pt.x, pt.y);
28262 }
28263 if (context.closePath) {
28264 context.closePath();
28265 }
28266 }
28267 function circleTriangle(context, trianglePoints, rx, ry, r) {
28268 if (context.beginPath) {
28269 context.beginPath();
28270 }
28271 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28272 var triPts = trianglePoints;
28273 var firstTrPt = triPts[0];
28274 context.moveTo(firstTrPt.x, firstTrPt.y);
28275 for (var i = 0; i < triPts.length; i++) {
28276 var pt = triPts[i];
28277 context.lineTo(pt.x, pt.y);
28278 }
28279 if (context.closePath) {
28280 context.closePath();
28281 }
28282 }
28283 function circle(context, rx, ry, r) {
28284 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28285 }
28286 CRp$a.arrowShapeImpl = function (name) {
28287 return (impl || (impl = {
28288 'polygon': polygon,
28289 'triangle-backcurve': triangleBackcurve,
28290 'triangle-tee': triangleTee,
28291 'circle-triangle': circleTriangle,
28292 'triangle-cross': triangleTee,
28293 'circle': circle
28294 }))[name];
28295 };
28296
28297 var CRp$9 = {};
28298 CRp$9.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28299 var r = this;
28300 if (ele.isNode()) {
28301 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28302 } else {
28303 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28304 }
28305 };
28306 CRp$9.drawElementOverlay = function (context, ele) {
28307 var r = this;
28308 if (ele.isNode()) {
28309 r.drawNodeOverlay(context, ele);
28310 } else {
28311 r.drawEdgeOverlay(context, ele);
28312 }
28313 };
28314 CRp$9.drawElementUnderlay = function (context, ele) {
28315 var r = this;
28316 if (ele.isNode()) {
28317 r.drawNodeUnderlay(context, ele);
28318 } else {
28319 r.drawEdgeUnderlay(context, ele);
28320 }
28321 };
28322 CRp$9.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28323 var r = this;
28324 var bb = eleTxrCache.getBoundingBox(ele);
28325 if (bb.w === 0 || bb.h === 0) {
28326 return;
28327 } // ignore zero size case
28328
28329 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28330 if (eleCache != null) {
28331 var opacity = getOpacity(r, ele);
28332 if (opacity === 0) {
28333 return;
28334 }
28335 var theta = getRotation(r, ele);
28336 var x1 = bb.x1,
28337 y1 = bb.y1,
28338 w = bb.w,
28339 h = bb.h;
28340 var x, y, sx, sy, smooth;
28341 if (theta !== 0) {
28342 var rotPt = eleTxrCache.getRotationPoint(ele);
28343 sx = rotPt.x;
28344 sy = rotPt.y;
28345 context.translate(sx, sy);
28346 context.rotate(theta);
28347 smooth = r.getImgSmoothing(context);
28348 if (!smooth) {
28349 r.setImgSmoothing(context, true);
28350 }
28351 var off = eleTxrCache.getRotationOffset(ele);
28352 x = off.x;
28353 y = off.y;
28354 } else {
28355 x = x1;
28356 y = y1;
28357 }
28358 var oldGlobalAlpha;
28359 if (opacity !== 1) {
28360 oldGlobalAlpha = context.globalAlpha;
28361 context.globalAlpha = oldGlobalAlpha * opacity;
28362 }
28363 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28364 if (opacity !== 1) {
28365 context.globalAlpha = oldGlobalAlpha;
28366 }
28367 if (theta !== 0) {
28368 context.rotate(-theta);
28369 context.translate(-sx, -sy);
28370 if (!smooth) {
28371 r.setImgSmoothing(context, false);
28372 }
28373 }
28374 } else {
28375 eleTxrCache.drawElement(context, ele); // direct draw fallback
28376 }
28377 };
28378
28379 var getZeroRotation = function getZeroRotation() {
28380 return 0;
28381 };
28382 var getLabelRotation = function getLabelRotation(r, ele) {
28383 return r.getTextAngle(ele, null);
28384 };
28385 var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28386 return r.getTextAngle(ele, 'source');
28387 };
28388 var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28389 return r.getTextAngle(ele, 'target');
28390 };
28391 var getOpacity = function getOpacity(r, ele) {
28392 return ele.effectiveOpacity();
28393 };
28394 var getTextOpacity = function getTextOpacity(e, ele) {
28395 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28396 };
28397 CRp$9.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28398 var r = this;
28399 var _r$data = r.data,
28400 eleTxrCache = _r$data.eleTxrCache,
28401 lblTxrCache = _r$data.lblTxrCache,
28402 slbTxrCache = _r$data.slbTxrCache,
28403 tlbTxrCache = _r$data.tlbTxrCache;
28404 var bb = ele.boundingBox();
28405 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28406 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28407 return;
28408 }
28409 if (!extent || boundingBoxesIntersect(bb, extent)) {
28410 var isEdge = ele.isEdge();
28411 var badLine = ele.element()._private.rscratch.badLine;
28412 r.drawElementUnderlay(context, ele);
28413 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28414 if (!isEdge || !badLine) {
28415 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28416 }
28417 if (isEdge && !badLine) {
28418 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28419 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28420 }
28421 r.drawElementOverlay(context, ele);
28422 }
28423 };
28424 CRp$9.drawElements = function (context, eles) {
28425 var r = this;
28426 for (var i = 0; i < eles.length; i++) {
28427 var ele = eles[i];
28428 r.drawElement(context, ele);
28429 }
28430 };
28431 CRp$9.drawCachedElements = function (context, eles, pxRatio, extent) {
28432 var r = this;
28433 for (var i = 0; i < eles.length; i++) {
28434 var ele = eles[i];
28435 r.drawCachedElement(context, ele, pxRatio, extent);
28436 }
28437 };
28438 CRp$9.drawCachedNodes = function (context, eles, pxRatio, extent) {
28439 var r = this;
28440 for (var i = 0; i < eles.length; i++) {
28441 var ele = eles[i];
28442 if (!ele.isNode()) {
28443 continue;
28444 }
28445 r.drawCachedElement(context, ele, pxRatio, extent);
28446 }
28447 };
28448 CRp$9.drawLayeredElements = function (context, eles, pxRatio, extent) {
28449 var r = this;
28450 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28451 if (layers) {
28452 for (var i = 0; i < layers.length; i++) {
28453 var layer = layers[i];
28454 var bb = layer.bb;
28455 if (bb.w === 0 || bb.h === 0) {
28456 continue;
28457 }
28458 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28459 }
28460 } else {
28461 // fall back on plain caching if no layers
28462 r.drawCachedElements(context, eles, pxRatio, extent);
28463 }
28464 };
28465
28466 var CRp$8 = {};
28467 CRp$8.drawEdge = function (context, edge, shiftToOriginWithBb) {
28468 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28469 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28470 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28471 var r = this;
28472 var rs = edge._private.rscratch;
28473 if (shouldDrawOpacity && !edge.visible()) {
28474 return;
28475 }
28476
28477 // if bezier ctrl pts can not be calculated, then die
28478 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28479 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28480 return;
28481 }
28482 var bb;
28483 if (shiftToOriginWithBb) {
28484 bb = shiftToOriginWithBb;
28485 context.translate(-bb.x1, -bb.y1);
28486 }
28487 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28488 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28489 var curveStyle = edge.pstyle('curve-style').value;
28490 var lineStyle = edge.pstyle('line-style').value;
28491 var edgeWidth = edge.pstyle('width').pfValue;
28492 var lineCap = edge.pstyle('line-cap').value;
28493 var lineOutlineWidth = edge.pstyle('line-outline-width').value;
28494 var lineOutlineColor = edge.pstyle('line-outline-color').value;
28495 var effectiveLineOpacity = opacity * lineOpacity;
28496 // separate arrow opacity would require arrow-opacity property
28497 var effectiveArrowOpacity = opacity * lineOpacity;
28498 var drawLine = function drawLine() {
28499 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28500 if (curveStyle === 'straight-triangle') {
28501 r.eleStrokeStyle(context, edge, strokeOpacity);
28502 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28503 } else {
28504 context.lineWidth = edgeWidth;
28505 context.lineCap = lineCap;
28506 r.eleStrokeStyle(context, edge, strokeOpacity);
28507 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28508 context.lineCap = 'butt'; // reset for other drawing functions
28509 }
28510 };
28511
28512 var drawLineOutline = function drawLineOutline() {
28513 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28514 context.lineWidth = edgeWidth + lineOutlineWidth;
28515 context.lineCap = lineCap;
28516 if (lineOutlineWidth > 0) {
28517 r.colorStrokeStyle(context, lineOutlineColor[0], lineOutlineColor[1], lineOutlineColor[2], strokeOpacity);
28518 } else {
28519 // do not draw any lineOutline
28520 context.lineCap = 'butt'; // reset for other drawing functions
28521 return;
28522 }
28523 if (curveStyle === 'straight-triangle') {
28524 r.drawEdgeTrianglePath(edge, context, rs.allpts);
28525 } else {
28526 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28527 context.lineCap = 'butt'; // reset for other drawing functions
28528 }
28529 };
28530
28531 var drawOverlay = function drawOverlay() {
28532 if (!shouldDrawOverlay) {
28533 return;
28534 }
28535 r.drawEdgeOverlay(context, edge);
28536 };
28537 var drawUnderlay = function drawUnderlay() {
28538 if (!shouldDrawOverlay) {
28539 return;
28540 }
28541 r.drawEdgeUnderlay(context, edge);
28542 };
28543 var drawArrows = function drawArrows() {
28544 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28545 r.drawArrowheads(context, edge, arrowOpacity);
28546 };
28547 var drawText = function drawText() {
28548 r.drawElementText(context, edge, null, drawLabel);
28549 };
28550 context.lineJoin = 'round';
28551 var ghost = edge.pstyle('ghost').value === 'yes';
28552 if (ghost) {
28553 var gx = edge.pstyle('ghost-offset-x').pfValue;
28554 var gy = edge.pstyle('ghost-offset-y').pfValue;
28555 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28556 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28557 context.translate(gx, gy);
28558 drawLine(effectiveGhostOpacity);
28559 drawArrows(effectiveGhostOpacity);
28560 context.translate(-gx, -gy);
28561 } else {
28562 drawLineOutline();
28563 }
28564 drawUnderlay();
28565 drawLine();
28566 drawArrows();
28567 drawOverlay();
28568 drawText();
28569 if (shiftToOriginWithBb) {
28570 context.translate(bb.x1, bb.y1);
28571 }
28572 };
28573 var drawEdgeOverlayUnderlay = function drawEdgeOverlayUnderlay(overlayOrUnderlay) {
28574 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
28575 throw new Error('Invalid state');
28576 }
28577 return function (context, edge) {
28578 if (!edge.visible()) {
28579 return;
28580 }
28581 var opacity = edge.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
28582 if (opacity === 0) {
28583 return;
28584 }
28585 var r = this;
28586 var usePaths = r.usePaths();
28587 var rs = edge._private.rscratch;
28588 var padding = edge.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
28589 var width = 2 * padding;
28590 var color = edge.pstyle("".concat(overlayOrUnderlay, "-color")).value;
28591 context.lineWidth = width;
28592 if (rs.edgeType === 'self' && !usePaths) {
28593 context.lineCap = 'butt';
28594 } else {
28595 context.lineCap = 'round';
28596 }
28597 r.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28598 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28599 };
28600 };
28601 CRp$8.drawEdgeOverlay = drawEdgeOverlayUnderlay('overlay');
28602 CRp$8.drawEdgeUnderlay = drawEdgeOverlayUnderlay('underlay');
28603 CRp$8.drawEdgePath = function (edge, context, pts, type) {
28604 var rs = edge._private.rscratch;
28605 var canvasCxt = context;
28606 var path;
28607 var pathCacheHit = false;
28608 var usePaths = this.usePaths();
28609 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28610 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28611 if (usePaths) {
28612 var pathCacheKey = pts.join('$');
28613 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28614 if (keyMatches) {
28615 path = context = rs.pathCache;
28616 pathCacheHit = true;
28617 } else {
28618 path = context = new Path2D();
28619 rs.pathCacheKey = pathCacheKey;
28620 rs.pathCache = path;
28621 }
28622 }
28623 if (canvasCxt.setLineDash) {
28624 // for very outofdate browsers
28625 switch (type) {
28626 case 'dotted':
28627 canvasCxt.setLineDash([1, 1]);
28628 break;
28629 case 'dashed':
28630 canvasCxt.setLineDash(lineDashPattern);
28631 canvasCxt.lineDashOffset = lineDashOffset;
28632 break;
28633 case 'solid':
28634 canvasCxt.setLineDash([]);
28635 break;
28636 }
28637 }
28638 if (!pathCacheHit && !rs.badLine) {
28639 if (context.beginPath) {
28640 context.beginPath();
28641 }
28642 context.moveTo(pts[0], pts[1]);
28643 switch (rs.edgeType) {
28644 case 'bezier':
28645 case 'self':
28646 case 'compound':
28647 case 'multibezier':
28648 for (var i = 2; i + 3 < pts.length; i += 4) {
28649 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28650 }
28651 break;
28652 case 'straight':
28653 case 'haystack':
28654 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28655 context.lineTo(pts[_i], pts[_i + 1]);
28656 }
28657 break;
28658 case 'segments':
28659 if (rs.isRound) {
28660 var _iterator = _createForOfIteratorHelper(rs.roundCorners),
28661 _step;
28662 try {
28663 for (_iterator.s(); !(_step = _iterator.n()).done;) {
28664 var corner = _step.value;
28665 drawPreparedRoundCorner(context, corner);
28666 }
28667 } catch (err) {
28668 _iterator.e(err);
28669 } finally {
28670 _iterator.f();
28671 }
28672 context.lineTo(pts[pts.length - 2], pts[pts.length - 1]);
28673 } else {
28674 for (var _i2 = 2; _i2 + 1 < pts.length; _i2 += 2) {
28675 context.lineTo(pts[_i2], pts[_i2 + 1]);
28676 }
28677 }
28678 break;
28679 }
28680 }
28681 context = canvasCxt;
28682 if (usePaths) {
28683 context.stroke(path);
28684 } else {
28685 context.stroke();
28686 }
28687
28688 // reset any line dashes
28689 if (context.setLineDash) {
28690 // for very outofdate browsers
28691 context.setLineDash([]);
28692 }
28693 };
28694 CRp$8.drawEdgeTrianglePath = function (edge, context, pts) {
28695 // use line stroke style for triangle fill style
28696 context.fillStyle = context.strokeStyle;
28697 var edgeWidth = edge.pstyle('width').pfValue;
28698 for (var i = 0; i + 1 < pts.length; i += 2) {
28699 var vector = [pts[i + 2] - pts[i], pts[i + 3] - pts[i + 1]];
28700 var length = Math.sqrt(vector[0] * vector[0] + vector[1] * vector[1]);
28701 var normal = [vector[1] / length, -vector[0] / length];
28702 var triangleHead = [normal[0] * edgeWidth / 2, normal[1] * edgeWidth / 2];
28703 context.beginPath();
28704 context.moveTo(pts[i] - triangleHead[0], pts[i + 1] - triangleHead[1]);
28705 context.lineTo(pts[i] + triangleHead[0], pts[i + 1] + triangleHead[1]);
28706 context.lineTo(pts[i + 2], pts[i + 3]);
28707 context.closePath();
28708 context.fill();
28709 }
28710 };
28711 CRp$8.drawArrowheads = function (context, edge, opacity) {
28712 var rs = edge._private.rscratch;
28713 var isHaystack = rs.edgeType === 'haystack';
28714 if (!isHaystack) {
28715 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28716 }
28717 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28718 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28719 if (!isHaystack) {
28720 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28721 }
28722 };
28723 CRp$8.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28724 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28725 return;
28726 }
28727 var self = this;
28728 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28729 if (arrowShape === 'none') {
28730 return;
28731 }
28732 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28733 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28734 var edgeWidth = edge.pstyle('width').pfValue;
28735 var pArrowWidth = edge.pstyle(prefix + '-arrow-width');
28736 var arrowWidth = pArrowWidth.value === 'match-line' ? edgeWidth : pArrowWidth.pfValue;
28737 if (pArrowWidth.units === '%') arrowWidth *= edgeWidth;
28738 var edgeOpacity = edge.pstyle('opacity').value;
28739 if (opacity === undefined) {
28740 opacity = edgeOpacity;
28741 }
28742 var gco = context.globalCompositeOperation;
28743 if (opacity !== 1 || arrowFill === 'hollow') {
28744 // then extra clear is needed
28745 context.globalCompositeOperation = 'destination-out';
28746 self.colorFillStyle(context, 255, 255, 255, 1);
28747 self.colorStrokeStyle(context, 255, 255, 255, 1);
28748 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, arrowWidth, x, y, angle);
28749 context.globalCompositeOperation = gco;
28750 } // otherwise, the opaque arrow clears it for free :)
28751
28752 var color = edge.pstyle(prefix + '-arrow-color').value;
28753 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28754 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28755 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, arrowWidth, x, y, angle);
28756 };
28757 CRp$8.drawArrowShape = function (edge, context, fill, edgeWidth, shape, shapeWidth, x, y, angle) {
28758 var r = this;
28759 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28760 var pathCacheHit = false;
28761 var path;
28762 var canvasContext = context;
28763 var translation = {
28764 x: x,
28765 y: y
28766 };
28767 var scale = edge.pstyle('arrow-scale').value;
28768 var size = this.getArrowWidth(edgeWidth, scale);
28769 var shapeImpl = r.arrowShapes[shape];
28770 if (usePaths) {
28771 var cache = r.arrowPathCache = r.arrowPathCache || [];
28772 var key = hashString(shape);
28773 var cachedPath = cache[key];
28774 if (cachedPath != null) {
28775 path = context = cachedPath;
28776 pathCacheHit = true;
28777 } else {
28778 path = context = new Path2D();
28779 cache[key] = path;
28780 }
28781 }
28782 if (!pathCacheHit) {
28783 if (context.beginPath) {
28784 context.beginPath();
28785 }
28786 if (usePaths) {
28787 // store in the path cache with values easily manipulated later
28788 shapeImpl.draw(context, 1, 0, {
28789 x: 0,
28790 y: 0
28791 }, 1);
28792 } else {
28793 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28794 }
28795 if (context.closePath) {
28796 context.closePath();
28797 }
28798 }
28799 context = canvasContext;
28800 if (usePaths) {
28801 // set transform to arrow position/orientation
28802 context.translate(x, y);
28803 context.rotate(angle);
28804 context.scale(size, size);
28805 }
28806 if (fill === 'filled' || fill === 'both') {
28807 if (usePaths) {
28808 context.fill(path);
28809 } else {
28810 context.fill();
28811 }
28812 }
28813 if (fill === 'hollow' || fill === 'both') {
28814 context.lineWidth = shapeWidth / (usePaths ? size : 1);
28815 context.lineJoin = 'miter';
28816 if (usePaths) {
28817 context.stroke(path);
28818 } else {
28819 context.stroke();
28820 }
28821 }
28822 if (usePaths) {
28823 // reset transform by applying inverse
28824 context.scale(1 / size, 1 / size);
28825 context.rotate(-angle);
28826 context.translate(-x, -y);
28827 }
28828 };
28829
28830 var CRp$7 = {};
28831 CRp$7.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28832 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28833 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28834 return;
28835 }
28836 try {
28837 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28838 } catch (e) {
28839 warn(e);
28840 }
28841 };
28842 CRp$7.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28843 var r = this;
28844 var pos = node.position();
28845 var nodeX = pos.x;
28846 var nodeY = pos.y;
28847 var styleObj = node.cy().style();
28848 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28849 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28850 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28851 var nodeW = node.width();
28852 var nodeH = node.height();
28853 var paddingX2 = node.padding() * 2;
28854 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28855 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28856 var rs = node._private.rscratch;
28857 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28858 var shouldClip = clip === 'node';
28859 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28860 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
28861 var cornerRadius = node.pstyle('corner-radius').value;
28862 if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
28863 var imgW = img.width || img.cachedW;
28864 var imgH = img.height || img.cachedH;
28865
28866 // workaround for broken browsers like ie
28867 if (null == imgW || null == imgH) {
28868 document.body.appendChild(img); // eslint-disable-line no-undef
28869
28870 imgW = img.cachedW = img.width || img.offsetWidth;
28871 imgH = img.cachedH = img.height || img.offsetHeight;
28872 document.body.removeChild(img); // eslint-disable-line no-undef
28873 }
28874
28875 var w = imgW;
28876 var h = imgH;
28877 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
28878 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
28879 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
28880 } else {
28881 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
28882 }
28883 }
28884 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
28885 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
28886 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
28887 } else {
28888 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
28889 }
28890 }
28891 if (w === 0 || h === 0) {
28892 return; // no point in drawing empty image (and chrome is broken in this case)
28893 }
28894
28895 if (fit === 'contain') {
28896 var scale = Math.min(nodeTW / w, nodeTH / h);
28897 w *= scale;
28898 h *= scale;
28899 } else if (fit === 'cover') {
28900 var scale = Math.max(nodeTW / w, nodeTH / h);
28901 w *= scale;
28902 h *= scale;
28903 }
28904 var x = nodeX - nodeTW / 2; // left
28905 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
28906 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
28907 if (posXUnits === '%') {
28908 x += (nodeTW - w) * posXPfVal;
28909 } else {
28910 x += posXPfVal;
28911 }
28912 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
28913 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
28914 if (offXUnits === '%') {
28915 x += (nodeTW - w) * offXPfVal;
28916 } else {
28917 x += offXPfVal;
28918 }
28919 var y = nodeY - nodeTH / 2; // top
28920 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
28921 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
28922 if (posYUnits === '%') {
28923 y += (nodeTH - h) * posYPfVal;
28924 } else {
28925 y += posYPfVal;
28926 }
28927 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
28928 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
28929 if (offYUnits === '%') {
28930 y += (nodeTH - h) * offYPfVal;
28931 } else {
28932 y += offYPfVal;
28933 }
28934 if (rs.pathCache) {
28935 x -= nodeX;
28936 y -= nodeY;
28937 nodeX = 0;
28938 nodeY = 0;
28939 }
28940 var gAlpha = context.globalAlpha;
28941 context.globalAlpha = imgOpacity;
28942 var smoothingEnabled = r.getImgSmoothing(context);
28943 var isSmoothingSwitched = false;
28944 if (smooth === 'no' && smoothingEnabled) {
28945 r.setImgSmoothing(context, false);
28946 isSmoothingSwitched = true;
28947 } else if (smooth === 'yes' && !smoothingEnabled) {
28948 r.setImgSmoothing(context, true);
28949 isSmoothingSwitched = true;
28950 }
28951 if (repeat === 'no-repeat') {
28952 if (shouldClip) {
28953 context.save();
28954 if (rs.pathCache) {
28955 context.clip(rs.pathCache);
28956 } else {
28957 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH, cornerRadius, rs);
28958 context.clip();
28959 }
28960 }
28961 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
28962 if (shouldClip) {
28963 context.restore();
28964 }
28965 } else {
28966 var pattern = context.createPattern(img, repeat);
28967 context.fillStyle = pattern;
28968 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH, cornerRadius, rs);
28969 context.translate(x, y);
28970 context.fill();
28971 context.translate(-x, -y);
28972 }
28973 context.globalAlpha = gAlpha;
28974 if (isSmoothingSwitched) {
28975 r.setImgSmoothing(context, smoothingEnabled);
28976 }
28977 };
28978
28979 var CRp$6 = {};
28980 CRp$6.eleTextBiggerThanMin = function (ele, scale) {
28981 if (!scale) {
28982 var zoom = ele.cy().zoom();
28983 var pxRatio = this.getPixelRatio();
28984 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
28985
28986 scale = Math.pow(2, lvl);
28987 }
28988 var computedSize = ele.pstyle('font-size').pfValue * scale;
28989 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
28990 if (computedSize < minSize) {
28991 return false;
28992 }
28993 return true;
28994 };
28995 CRp$6.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
28996 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28997 var r = this;
28998 if (force == null) {
28999 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29000 return;
29001 }
29002 } else if (force === false) {
29003 return;
29004 }
29005 if (ele.isNode()) {
29006 var label = ele.pstyle('label');
29007 if (!label || !label.value) {
29008 return;
29009 }
29010 var justification = r.getLabelJustification(ele);
29011 context.textAlign = justification;
29012 context.textBaseline = 'bottom';
29013 } else {
29014 var badLine = ele.element()._private.rscratch.badLine;
29015 var _label = ele.pstyle('label');
29016 var srcLabel = ele.pstyle('source-label');
29017 var tgtLabel = ele.pstyle('target-label');
29018 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29019 return;
29020 }
29021 context.textAlign = 'center';
29022 context.textBaseline = 'bottom';
29023 }
29024 var applyRotation = !shiftToOriginWithBb;
29025 var bb;
29026 if (shiftToOriginWithBb) {
29027 bb = shiftToOriginWithBb;
29028 context.translate(-bb.x1, -bb.y1);
29029 }
29030 if (prefix == null) {
29031 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29032 if (ele.isEdge()) {
29033 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29034 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29035 }
29036 } else {
29037 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29038 }
29039 if (shiftToOriginWithBb) {
29040 context.translate(bb.x1, bb.y1);
29041 }
29042 };
29043 CRp$6.getFontCache = function (context) {
29044 var cache;
29045 this.fontCaches = this.fontCaches || [];
29046 for (var i = 0; i < this.fontCaches.length; i++) {
29047 cache = this.fontCaches[i];
29048 if (cache.context === context) {
29049 return cache;
29050 }
29051 }
29052 cache = {
29053 context: context
29054 };
29055 this.fontCaches.push(cache);
29056 return cache;
29057 };
29058
29059 // set up canvas context with font
29060 // returns transformed text string
29061 CRp$6.setupTextStyle = function (context, ele) {
29062 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29063 // Font style
29064 var labelStyle = ele.pstyle('font-style').strValue;
29065 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29066 var labelFamily = ele.pstyle('font-family').strValue;
29067 var labelWeight = ele.pstyle('font-weight').strValue;
29068 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29069 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29070 var color = ele.pstyle('color').value;
29071 var outlineColor = ele.pstyle('text-outline-color').value;
29072 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29073 context.lineJoin = 'round'; // so text outlines aren't jagged
29074
29075 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29076 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29077 };
29078
29079 // TODO ensure re-used
29080 function roundRect(ctx, x, y, width, height) {
29081 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29082 var stroke = arguments.length > 6 ? arguments[6] : undefined;
29083 ctx.beginPath();
29084 ctx.moveTo(x + radius, y);
29085 ctx.lineTo(x + width - radius, y);
29086 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29087 ctx.lineTo(x + width, y + height - radius);
29088 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29089 ctx.lineTo(x + radius, y + height);
29090 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29091 ctx.lineTo(x, y + radius);
29092 ctx.quadraticCurveTo(x, y, x + radius, y);
29093 ctx.closePath();
29094 if (stroke) ctx.stroke();else ctx.fill();
29095 }
29096 CRp$6.getTextAngle = function (ele, prefix) {
29097 var theta;
29098 var _p = ele._private;
29099 var rscratch = _p.rscratch;
29100 var pdash = prefix ? prefix + '-' : '';
29101 var rotation = ele.pstyle(pdash + 'text-rotation');
29102 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29103 if (rotation.strValue === 'autorotate') {
29104 theta = ele.isEdge() ? textAngle : 0;
29105 } else if (rotation.strValue === 'none') {
29106 theta = 0;
29107 } else {
29108 theta = rotation.pfValue;
29109 }
29110 return theta;
29111 };
29112 CRp$6.drawText = function (context, ele, prefix) {
29113 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29114 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29115 var _p = ele._private;
29116 var rscratch = _p.rscratch;
29117 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29118 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29119 return;
29120 }
29121
29122 // use 'main' as an alias for the main label (i.e. null prefix)
29123 if (prefix === 'main') {
29124 prefix = null;
29125 }
29126 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29127 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29128 var orgTextX, orgTextY; // used for rotation
29129 var text = this.getLabelText(ele, prefix);
29130 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29131 this.setupTextStyle(context, ele, useEleOpacity);
29132 var pdash = prefix ? prefix + '-' : '';
29133 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29134 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29135 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29136 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29137 var isEdge = ele.isEdge();
29138 var halign = ele.pstyle('text-halign').value;
29139 var valign = ele.pstyle('text-valign').value;
29140 if (isEdge) {
29141 halign = 'center';
29142 valign = 'center';
29143 }
29144 textX += marginX;
29145 textY += marginY;
29146 var theta;
29147 if (!applyRotation) {
29148 theta = 0;
29149 } else {
29150 theta = this.getTextAngle(ele, prefix);
29151 }
29152 if (theta !== 0) {
29153 orgTextX = textX;
29154 orgTextY = textY;
29155 context.translate(orgTextX, orgTextY);
29156 context.rotate(theta);
29157 textX = 0;
29158 textY = 0;
29159 }
29160 switch (valign) {
29161 case 'top':
29162 break;
29163 case 'center':
29164 textY += textH / 2;
29165 break;
29166 case 'bottom':
29167 textY += textH;
29168 break;
29169 }
29170 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29171 var borderOpacity = ele.pstyle('text-border-opacity').value;
29172 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29173 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29174 var styleShape = ele.pstyle('text-background-shape').strValue;
29175 var rounded = styleShape.indexOf('round') === 0;
29176 var roundRadius = 2;
29177 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29178 var bgX = textX - backgroundPadding;
29179 switch (halign) {
29180 case 'left':
29181 bgX -= textW;
29182 break;
29183 case 'center':
29184 bgX -= textW / 2;
29185 break;
29186 }
29187 var bgY = textY - textH - backgroundPadding;
29188 var bgW = textW + 2 * backgroundPadding;
29189 var bgH = textH + 2 * backgroundPadding;
29190 if (backgroundOpacity > 0) {
29191 var textFill = context.fillStyle;
29192 var textBackgroundColor = ele.pstyle('text-background-color').value;
29193 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29194 if (rounded) {
29195 roundRect(context, bgX, bgY, bgW, bgH, roundRadius);
29196 } else {
29197 context.fillRect(bgX, bgY, bgW, bgH);
29198 }
29199 context.fillStyle = textFill;
29200 }
29201 if (textBorderWidth > 0 && borderOpacity > 0) {
29202 var textStroke = context.strokeStyle;
29203 var textLineWidth = context.lineWidth;
29204 var textBorderColor = ele.pstyle('text-border-color').value;
29205 var textBorderStyle = ele.pstyle('text-border-style').value;
29206 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29207 context.lineWidth = textBorderWidth;
29208 if (context.setLineDash) {
29209 // for very outofdate browsers
29210 switch (textBorderStyle) {
29211 case 'dotted':
29212 context.setLineDash([1, 1]);
29213 break;
29214 case 'dashed':
29215 context.setLineDash([4, 2]);
29216 break;
29217 case 'double':
29218 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29219 context.setLineDash([]);
29220 break;
29221 case 'solid':
29222 context.setLineDash([]);
29223 break;
29224 }
29225 }
29226 if (rounded) {
29227 roundRect(context, bgX, bgY, bgW, bgH, roundRadius, 'stroke');
29228 } else {
29229 context.strokeRect(bgX, bgY, bgW, bgH);
29230 }
29231 if (textBorderStyle === 'double') {
29232 var whiteWidth = textBorderWidth / 2;
29233 if (rounded) {
29234 roundRect(context, bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2, roundRadius, 'stroke');
29235 } else {
29236 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29237 }
29238 }
29239 if (context.setLineDash) {
29240 // for very outofdate browsers
29241 context.setLineDash([]);
29242 }
29243 context.lineWidth = textLineWidth;
29244 context.strokeStyle = textStroke;
29245 }
29246 }
29247 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29248
29249 if (lineWidth > 0) {
29250 context.lineWidth = lineWidth;
29251 }
29252 if (ele.pstyle('text-wrap').value === 'wrap') {
29253 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29254 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29255 var halfTextW = textW / 2;
29256 var justification = this.getLabelJustification(ele);
29257 if (justification === 'auto') ; else if (halign === 'left') {
29258 // auto justification : right
29259 if (justification === 'left') {
29260 textX += -textW;
29261 } else if (justification === 'center') {
29262 textX += -halfTextW;
29263 } // else same as auto
29264 } else if (halign === 'center') {
29265 // auto justfication : center
29266 if (justification === 'left') {
29267 textX += -halfTextW;
29268 } else if (justification === 'right') {
29269 textX += halfTextW;
29270 } // else same as auto
29271 } else if (halign === 'right') {
29272 // auto justification : left
29273 if (justification === 'center') {
29274 textX += halfTextW;
29275 } else if (justification === 'right') {
29276 textX += textW;
29277 } // else same as auto
29278 }
29279
29280 switch (valign) {
29281 case 'top':
29282 textY -= (lines.length - 1) * lineHeight;
29283 break;
29284 case 'center':
29285 case 'bottom':
29286 textY -= (lines.length - 1) * lineHeight;
29287 break;
29288 }
29289 for (var l = 0; l < lines.length; l++) {
29290 if (lineWidth > 0) {
29291 context.strokeText(lines[l], textX, textY);
29292 }
29293 context.fillText(lines[l], textX, textY);
29294 textY += lineHeight;
29295 }
29296 } else {
29297 if (lineWidth > 0) {
29298 context.strokeText(text, textX, textY);
29299 }
29300 context.fillText(text, textX, textY);
29301 }
29302 if (theta !== 0) {
29303 context.rotate(-theta);
29304 context.translate(-orgTextX, -orgTextY);
29305 }
29306 }
29307 };
29308
29309 /* global Path2D */
29310 var CRp$5 = {};
29311 CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29312 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29313 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29314 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29315 var r = this;
29316 var nodeWidth, nodeHeight;
29317 var _p = node._private;
29318 var rs = _p.rscratch;
29319 var pos = node.position();
29320 if (!number$1(pos.x) || !number$1(pos.y)) {
29321 return; // can't draw node with undefined position
29322 }
29323
29324 if (shouldDrawOpacity && !node.visible()) {
29325 return;
29326 }
29327 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29328 var usePaths = r.usePaths();
29329 var path;
29330 var pathCacheHit = false;
29331 var padding = node.padding();
29332 nodeWidth = node.width() + 2 * padding;
29333 nodeHeight = node.height() + 2 * padding;
29334
29335 //
29336 // setup shift
29337
29338 var bb;
29339 if (shiftToOriginWithBb) {
29340 bb = shiftToOriginWithBb;
29341 context.translate(-bb.x1, -bb.y1);
29342 }
29343
29344 //
29345 // load bg image
29346
29347 var bgImgProp = node.pstyle('background-image');
29348 var urls = bgImgProp.value;
29349 var urlDefined = new Array(urls.length);
29350 var image = new Array(urls.length);
29351 var numImages = 0;
29352 for (var i = 0; i < urls.length; i++) {
29353 var url = urls[i];
29354 var defd = urlDefined[i] = url != null && url !== 'none';
29355 if (defd) {
29356 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29357 numImages++;
29358
29359 // get image, and if not loaded then ask to redraw when later loaded
29360 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29361 _p.backgroundTimestamp = Date.now();
29362 node.emitAndNotify('background');
29363 });
29364 }
29365 }
29366
29367 //
29368 // setup styles
29369
29370 var darkness = node.pstyle('background-blacken').value;
29371 var borderWidth = node.pstyle('border-width').pfValue;
29372 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29373 var borderColor = node.pstyle('border-color').value;
29374 var borderStyle = node.pstyle('border-style').value;
29375 var borderJoin = node.pstyle('border-join').value;
29376 var borderCap = node.pstyle('border-cap').value;
29377 var borderPosition = node.pstyle('border-position').value;
29378 var borderPattern = node.pstyle('border-dash-pattern').pfValue;
29379 var borderOffset = node.pstyle('border-dash-offset').pfValue;
29380 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29381 var outlineWidth = node.pstyle('outline-width').pfValue;
29382 var outlineColor = node.pstyle('outline-color').value;
29383 var outlineStyle = node.pstyle('outline-style').value;
29384 var outlineOpacity = node.pstyle('outline-opacity').value * eleOpacity;
29385 var outlineOffset = node.pstyle('outline-offset').value;
29386 var cornerRadius = node.pstyle('corner-radius').value;
29387 if (cornerRadius !== 'auto') cornerRadius = node.pstyle('corner-radius').pfValue;
29388 var setupShapeColor = function setupShapeColor() {
29389 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29390 r.eleFillStyle(context, node, bgOpy);
29391 };
29392 var setupBorderColor = function setupBorderColor() {
29393 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29394 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29395 };
29396 var setupOutlineColor = function setupOutlineColor() {
29397 var otlnOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : outlineOpacity;
29398 r.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], otlnOpy);
29399 };
29400
29401 //
29402 // setup shape
29403
29404 var getPath = function getPath(width, height, shape, points) {
29405 var pathCache = r.nodePathCache = r.nodePathCache || [];
29406 var key = hashStrings(shape === 'polygon' ? shape + ',' + points.join(',') : shape, '' + height, '' + width, '' + cornerRadius);
29407 var cachedPath = pathCache[key];
29408 var path;
29409 var cacheHit = false;
29410 if (cachedPath != null) {
29411 path = cachedPath;
29412 cacheHit = true;
29413 rs.pathCache = path;
29414 } else {
29415 path = new Path2D();
29416 pathCache[key] = rs.pathCache = path;
29417 }
29418 return {
29419 path: path,
29420 cacheHit: cacheHit
29421 };
29422 };
29423 var styleShape = node.pstyle('shape').strValue;
29424 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29425 if (usePaths) {
29426 context.translate(pos.x, pos.y);
29427 var shapePath = getPath(nodeWidth, nodeHeight, styleShape, shapePts);
29428 path = shapePath.path;
29429 pathCacheHit = shapePath.cacheHit;
29430 }
29431 var drawShape = function drawShape() {
29432 if (!pathCacheHit) {
29433 var npos = pos;
29434 if (usePaths) {
29435 npos = {
29436 x: 0,
29437 y: 0
29438 };
29439 }
29440 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight, cornerRadius, rs);
29441 }
29442 if (usePaths) {
29443 context.fill(path);
29444 } else {
29445 context.fill();
29446 }
29447 };
29448 var drawImages = function drawImages() {
29449 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29450 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29451 var prevBging = _p.backgrounding;
29452 var totalCompleted = 0;
29453 for (var _i = 0; _i < image.length; _i++) {
29454 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29455 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29456 totalCompleted++;
29457 continue;
29458 }
29459 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29460 totalCompleted++;
29461 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29462 }
29463 }
29464 _p.backgrounding = !(totalCompleted === numImages);
29465 if (prevBging !== _p.backgrounding) {
29466 // update style b/c :backgrounding state changed
29467 node.updateStyle(false);
29468 }
29469 };
29470 var drawPie = function drawPie() {
29471 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29472 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29473 if (r.hasPie(node)) {
29474 r.drawPie(context, node, pieOpacity);
29475
29476 // redraw/restore path if steps after pie need it
29477 if (redrawShape) {
29478 if (!usePaths) {
29479 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight, cornerRadius, rs);
29480 }
29481 }
29482 }
29483 };
29484 var darken = function darken() {
29485 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29486 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29487 var c = darkness > 0 ? 0 : 255;
29488 if (darkness !== 0) {
29489 r.colorFillStyle(context, c, c, c, opacity);
29490 if (usePaths) {
29491 context.fill(path);
29492 } else {
29493 context.fill();
29494 }
29495 }
29496 };
29497 var drawBorder = function drawBorder() {
29498 if (borderWidth > 0) {
29499 context.lineWidth = borderWidth;
29500 context.lineCap = borderCap;
29501 context.lineJoin = borderJoin;
29502 if (context.setLineDash) {
29503 // for very outofdate browsers
29504 switch (borderStyle) {
29505 case 'dotted':
29506 context.setLineDash([1, 1]);
29507 break;
29508 case 'dashed':
29509 context.setLineDash(borderPattern);
29510 context.lineDashOffset = borderOffset;
29511 break;
29512 case 'solid':
29513 case 'double':
29514 context.setLineDash([]);
29515 break;
29516 }
29517 }
29518 if (borderPosition !== 'center') {
29519 context.save();
29520 context.lineWidth *= 2;
29521 if (borderPosition === 'inside') {
29522 usePaths ? context.clip(path) : context.clip();
29523 } else {
29524 var region = new Path2D();
29525 region.rect(-nodeWidth / 2 - borderWidth, -nodeHeight / 2 - borderWidth, nodeWidth + 2 * borderWidth, nodeHeight + 2 * borderWidth);
29526 region.addPath(path);
29527 context.clip(region, 'evenodd');
29528 }
29529 usePaths ? context.stroke(path) : context.stroke();
29530 context.restore();
29531 } else {
29532 usePaths ? context.stroke(path) : context.stroke();
29533 }
29534 if (borderStyle === 'double') {
29535 context.lineWidth = borderWidth / 3;
29536 var gco = context.globalCompositeOperation;
29537 context.globalCompositeOperation = 'destination-out';
29538 if (usePaths) {
29539 context.stroke(path);
29540 } else {
29541 context.stroke();
29542 }
29543 context.globalCompositeOperation = gco;
29544 }
29545
29546 // reset in case we changed the border style
29547 if (context.setLineDash) {
29548 // for very outofdate browsers
29549 context.setLineDash([]);
29550 }
29551 }
29552 };
29553 var drawOutline = function drawOutline() {
29554 if (outlineWidth > 0) {
29555 context.lineWidth = outlineWidth;
29556 context.lineCap = 'butt';
29557 if (context.setLineDash) {
29558 // for very outofdate browsers
29559 switch (outlineStyle) {
29560 case 'dotted':
29561 context.setLineDash([1, 1]);
29562 break;
29563 case 'dashed':
29564 context.setLineDash([4, 2]);
29565 break;
29566 case 'solid':
29567 case 'double':
29568 context.setLineDash([]);
29569 break;
29570 }
29571 }
29572 var npos = pos;
29573 if (usePaths) {
29574 npos = {
29575 x: 0,
29576 y: 0
29577 };
29578 }
29579 var shape = r.getNodeShape(node);
29580 var bWidth = borderWidth;
29581 if (borderPosition === 'inside') bWidth = 0;
29582 if (borderPosition === 'outside') bWidth *= 2;
29583 var scaleX = (nodeWidth + bWidth + (outlineWidth + outlineOffset)) / nodeWidth;
29584 var scaleY = (nodeHeight + bWidth + (outlineWidth + outlineOffset)) / nodeHeight;
29585 var sWidth = nodeWidth * scaleX;
29586 var sHeight = nodeHeight * scaleY;
29587 var points = r.nodeShapes[shape].points;
29588 var _path;
29589 if (usePaths) {
29590 var outlinePath = getPath(sWidth, sHeight, shape, points);
29591 _path = outlinePath.path;
29592 }
29593
29594 // draw the outline path, either by using expanded points or by scaling
29595 // the dimensions, depending on shape
29596 if (shape === "ellipse") {
29597 r.drawEllipsePath(_path || context, npos.x, npos.y, sWidth, sHeight);
29598 } else if (['round-diamond', 'round-heptagon', 'round-hexagon', 'round-octagon', 'round-pentagon', 'round-polygon', 'round-triangle', 'round-tag'].includes(shape)) {
29599 var sMult = 0;
29600 var offsetX = 0;
29601 var offsetY = 0;
29602 if (shape === 'round-diamond') {
29603 sMult = (bWidth + outlineOffset + outlineWidth) * 1.4;
29604 } else if (shape === 'round-heptagon') {
29605 sMult = (bWidth + outlineOffset + outlineWidth) * 1.075;
29606 offsetY = -(bWidth / 2 + outlineOffset + outlineWidth) / 35;
29607 } else if (shape === 'round-hexagon') {
29608 sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
29609 } else if (shape === 'round-pentagon') {
29610 sMult = (bWidth + outlineOffset + outlineWidth) * 1.13;
29611 offsetY = -(bWidth / 2 + outlineOffset + outlineWidth) / 15;
29612 } else if (shape === 'round-tag') {
29613 sMult = (bWidth + outlineOffset + outlineWidth) * 1.12;
29614 offsetX = (bWidth / 2 + outlineWidth + outlineOffset) * .07;
29615 } else if (shape === 'round-triangle') {
29616 sMult = (bWidth + outlineOffset + outlineWidth) * (Math.PI / 2);
29617 offsetY = -(bWidth + outlineOffset / 2 + outlineWidth) / Math.PI;
29618 }
29619 if (sMult !== 0) {
29620 scaleX = (nodeWidth + sMult) / nodeWidth;
29621 sWidth = nodeWidth * scaleX;
29622 if (!['round-hexagon', 'round-tag'].includes(shape)) {
29623 scaleY = (nodeHeight + sMult) / nodeHeight;
29624 sHeight = nodeHeight * scaleY;
29625 }
29626 }
29627 cornerRadius = cornerRadius === 'auto' ? getRoundPolygonRadius(sWidth, sHeight) : cornerRadius;
29628 var halfW = sWidth / 2;
29629 var halfH = sHeight / 2;
29630 var radius = cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2;
29631 var p = new Array(points.length / 2);
29632 var corners = new Array(points.length / 2);
29633 for (var _i3 = 0; _i3 < points.length / 2; _i3++) {
29634 p[_i3] = {
29635 x: npos.x + offsetX + halfW * points[_i3 * 2],
29636 y: npos.y + offsetY + halfH * points[_i3 * 2 + 1]
29637 };
29638 }
29639 var _i2,
29640 p1,
29641 p2,
29642 p3,
29643 len = p.length;
29644 p1 = p[len - 1];
29645 // for each point
29646 for (_i2 = 0; _i2 < len; _i2++) {
29647 p2 = p[_i2 % len];
29648 p3 = p[(_i2 + 1) % len];
29649 corners[_i2] = getRoundCorner(p1, p2, p3, radius);
29650 p1 = p2;
29651 p2 = p3;
29652 }
29653 r.drawRoundPolygonPath(_path || context, npos.x + offsetX, npos.y + offsetY, nodeWidth * scaleX, nodeHeight * scaleY, points, corners);
29654 } else if (['roundrectangle', 'round-rectangle'].includes(shape)) {
29655 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(sWidth, sHeight) : cornerRadius;
29656 r.drawRoundRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2);
29657 } else if (['cutrectangle', 'cut-rectangle'].includes(shape)) {
29658 cornerRadius = cornerRadius === 'auto' ? getCutRectangleCornerLength() : cornerRadius;
29659 r.drawCutRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, null, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 4);
29660 } else if (['bottomroundrectangle', 'bottom-round-rectangle'].includes(shape)) {
29661 cornerRadius = cornerRadius === 'auto' ? getRoundRectangleRadius(sWidth, sHeight) : cornerRadius;
29662 r.drawBottomRoundRectanglePath(_path || context, npos.x, npos.y, sWidth, sHeight, cornerRadius + (bWidth + outlineWidth + outlineOffset) / 2);
29663 } else if (shape === "barrel") {
29664 r.drawBarrelPath(_path || context, npos.x, npos.y, sWidth, sHeight);
29665 } else if (shape.startsWith("polygon") || ['rhomboid', 'right-rhomboid', 'round-tag', 'tag', 'vee'].includes(shape)) {
29666 var pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
29667 points = joinLines(expandPolygon(points, pad));
29668 r.drawPolygonPath(_path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
29669 } else {
29670 var _pad = (bWidth + outlineWidth + outlineOffset) / nodeWidth;
29671 points = joinLines(expandPolygon(points, -_pad));
29672 r.drawPolygonPath(_path || context, npos.x, npos.y, nodeWidth, nodeHeight, points);
29673 }
29674 if (usePaths) {
29675 context.stroke(_path);
29676 } else {
29677 context.stroke();
29678 }
29679 if (outlineStyle === 'double') {
29680 context.lineWidth = bWidth / 3;
29681 var gco = context.globalCompositeOperation;
29682 context.globalCompositeOperation = 'destination-out';
29683 if (usePaths) {
29684 context.stroke(_path);
29685 } else {
29686 context.stroke();
29687 }
29688 context.globalCompositeOperation = gco;
29689 }
29690
29691 // reset in case we changed the border style
29692 if (context.setLineDash) {
29693 // for very outofdate browsers
29694 context.setLineDash([]);
29695 }
29696 }
29697 };
29698 var drawOverlay = function drawOverlay() {
29699 if (shouldDrawOverlay) {
29700 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29701 }
29702 };
29703 var drawUnderlay = function drawUnderlay() {
29704 if (shouldDrawOverlay) {
29705 r.drawNodeUnderlay(context, node, pos, nodeWidth, nodeHeight);
29706 }
29707 };
29708 var drawText = function drawText() {
29709 r.drawElementText(context, node, null, drawLabel);
29710 };
29711 var ghost = node.pstyle('ghost').value === 'yes';
29712 if (ghost) {
29713 var gx = node.pstyle('ghost-offset-x').pfValue;
29714 var gy = node.pstyle('ghost-offset-y').pfValue;
29715 var ghostOpacity = node.pstyle('ghost-opacity').value;
29716 var effGhostOpacity = ghostOpacity * eleOpacity;
29717 context.translate(gx, gy);
29718 setupOutlineColor();
29719 drawOutline();
29720 setupShapeColor(ghostOpacity * bgOpacity);
29721 drawShape();
29722 drawImages(effGhostOpacity, true);
29723 setupBorderColor(ghostOpacity * borderOpacity);
29724 drawBorder();
29725 drawPie(darkness !== 0 || borderWidth !== 0);
29726 drawImages(effGhostOpacity, false);
29727 darken(effGhostOpacity);
29728 context.translate(-gx, -gy);
29729 }
29730 if (usePaths) {
29731 context.translate(-pos.x, -pos.y);
29732 }
29733 drawUnderlay();
29734 if (usePaths) {
29735 context.translate(pos.x, pos.y);
29736 }
29737 setupOutlineColor();
29738 drawOutline();
29739 setupShapeColor();
29740 drawShape();
29741 drawImages(eleOpacity, true);
29742 setupBorderColor();
29743 drawBorder();
29744 drawPie(darkness !== 0 || borderWidth !== 0);
29745 drawImages(eleOpacity, false);
29746 darken();
29747 if (usePaths) {
29748 context.translate(-pos.x, -pos.y);
29749 }
29750 drawText();
29751 drawOverlay();
29752
29753 //
29754 // clean up shift
29755
29756 if (shiftToOriginWithBb) {
29757 context.translate(bb.x1, bb.y1);
29758 }
29759 };
29760 var drawNodeOverlayUnderlay = function drawNodeOverlayUnderlay(overlayOrUnderlay) {
29761 if (!['overlay', 'underlay'].includes(overlayOrUnderlay)) {
29762 throw new Error('Invalid state');
29763 }
29764 return function (context, node, pos, nodeWidth, nodeHeight) {
29765 var r = this;
29766 if (!node.visible()) {
29767 return;
29768 }
29769 var padding = node.pstyle("".concat(overlayOrUnderlay, "-padding")).pfValue;
29770 var opacity = node.pstyle("".concat(overlayOrUnderlay, "-opacity")).value;
29771 var color = node.pstyle("".concat(overlayOrUnderlay, "-color")).value;
29772 var shape = node.pstyle("".concat(overlayOrUnderlay, "-shape")).value;
29773 var radius = node.pstyle("".concat(overlayOrUnderlay, "-corner-radius")).value;
29774 if (opacity > 0) {
29775 pos = pos || node.position();
29776 if (nodeWidth == null || nodeHeight == null) {
29777 var _padding = node.padding();
29778 nodeWidth = node.width() + 2 * _padding;
29779 nodeHeight = node.height() + 2 * _padding;
29780 }
29781 r.colorFillStyle(context, color[0], color[1], color[2], opacity);
29782 r.nodeShapes[shape].draw(context, pos.x, pos.y, nodeWidth + padding * 2, nodeHeight + padding * 2, radius);
29783 context.fill();
29784 }
29785 };
29786 };
29787 CRp$5.drawNodeOverlay = drawNodeOverlayUnderlay('overlay');
29788 CRp$5.drawNodeUnderlay = drawNodeOverlayUnderlay('underlay');
29789
29790 // does the node have at least one pie piece?
29791 CRp$5.hasPie = function (node) {
29792 node = node[0]; // ensure ele ref
29793
29794 return node._private.hasPie;
29795 };
29796 CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29797 node = node[0]; // ensure ele ref
29798 pos = pos || node.position();
29799 var cyStyle = node.cy().style();
29800 var pieSize = node.pstyle('pie-size');
29801 var x = pos.x;
29802 var y = pos.y;
29803 var nodeW = node.width();
29804 var nodeH = node.height();
29805 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29806 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29807 var usePaths = this.usePaths();
29808 if (usePaths) {
29809 x = 0;
29810 y = 0;
29811 }
29812 if (pieSize.units === '%') {
29813 radius = radius * pieSize.pfValue;
29814 } else if (pieSize.pfValue !== undefined) {
29815 radius = pieSize.pfValue / 2;
29816 }
29817 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29818 // 1..N
29819 var size = node.pstyle('pie-' + i + '-background-size').value;
29820 var color = node.pstyle('pie-' + i + '-background-color').value;
29821 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29822 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29823
29824 // percent can't push beyond 1
29825 if (percent + lastPercent > 1) {
29826 percent = 1 - lastPercent;
29827 }
29828 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29829 var angleDelta = 2 * Math.PI * percent;
29830 var angleEnd = angleStart + angleDelta;
29831
29832 // ignore if
29833 // - zero size
29834 // - we're already beyond the full circle
29835 // - adding the current slice would go beyond the full circle
29836 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29837 continue;
29838 }
29839 context.beginPath();
29840 context.moveTo(x, y);
29841 context.arc(x, y, radius, angleStart, angleEnd);
29842 context.closePath();
29843 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29844 context.fill();
29845 lastPercent += percent;
29846 }
29847 };
29848
29849 var CRp$4 = {};
29850 var motionBlurDelay = 100;
29851
29852 // var isFirefox = typeof InstallTrigger !== 'undefined';
29853
29854 CRp$4.getPixelRatio = function () {
29855 var context = this.data.contexts[0];
29856 if (this.forcedPixelRatio != null) {
29857 return this.forcedPixelRatio;
29858 }
29859 var containerWindow = this.cy.window();
29860 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29861 return (containerWindow.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29862 };
29863
29864 CRp$4.paintCache = function (context) {
29865 var caches = this.paintCaches = this.paintCaches || [];
29866 var needToCreateCache = true;
29867 var cache;
29868 for (var i = 0; i < caches.length; i++) {
29869 cache = caches[i];
29870 if (cache.context === context) {
29871 needToCreateCache = false;
29872 break;
29873 }
29874 }
29875 if (needToCreateCache) {
29876 cache = {
29877 context: context
29878 };
29879 caches.push(cache);
29880 }
29881 return cache;
29882 };
29883 CRp$4.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29884 var gradientStyle;
29885 var usePaths = this.usePaths();
29886 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29887 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29888 if (fill === 'radial-gradient') {
29889 if (ele.isEdge()) {
29890 var start = ele.sourceEndpoint(),
29891 end = ele.targetEndpoint(),
29892 mid = ele.midpoint();
29893 var d1 = dist(start, mid);
29894 var d2 = dist(end, mid);
29895 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29896 } else {
29897 var pos = usePaths ? {
29898 x: 0,
29899 y: 0
29900 } : ele.position(),
29901 width = ele.paddedWidth(),
29902 height = ele.paddedHeight();
29903 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29904 }
29905 } else {
29906 if (ele.isEdge()) {
29907 var _start = ele.sourceEndpoint(),
29908 _end = ele.targetEndpoint();
29909 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29910 } else {
29911 var _pos = usePaths ? {
29912 x: 0,
29913 y: 0
29914 } : ele.position(),
29915 _width = ele.paddedWidth(),
29916 _height = ele.paddedHeight(),
29917 halfWidth = _width / 2,
29918 halfHeight = _height / 2;
29919 var direction = ele.pstyle('background-gradient-direction').value;
29920 switch (direction) {
29921 case 'to-bottom':
29922 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29923 break;
29924 case 'to-top':
29925 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29926 break;
29927 case 'to-left':
29928 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29929 break;
29930 case 'to-right':
29931 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29932 break;
29933 case 'to-bottom-right':
29934 case 'to-right-bottom':
29935 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29936 break;
29937 case 'to-top-right':
29938 case 'to-right-top':
29939 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29940 break;
29941 case 'to-bottom-left':
29942 case 'to-left-bottom':
29943 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29944 break;
29945 case 'to-top-left':
29946 case 'to-left-top':
29947 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29948 break;
29949 }
29950 }
29951 }
29952 if (!gradientStyle) return null; // invalid gradient style
29953
29954 var hasPositions = positions.length === colors.length;
29955 var length = colors.length;
29956 for (var i = 0; i < length; i++) {
29957 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29958 }
29959 return gradientStyle;
29960 };
29961 CRp$4.gradientFillStyle = function (context, ele, fill, opacity) {
29962 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29963 if (!gradientStyle) return null; // error
29964 context.fillStyle = gradientStyle;
29965 };
29966 CRp$4.colorFillStyle = function (context, r, g, b, a) {
29967 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29968 // turn off for now, seems context does its own caching
29969
29970 // var cache = this.paintCache(context);
29971
29972 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29973
29974 // if( cache.fillStyle !== fillStyle ){
29975 // context.fillStyle = cache.fillStyle = fillStyle;
29976 // }
29977 };
29978
29979 CRp$4.eleFillStyle = function (context, ele, opacity) {
29980 var backgroundFill = ele.pstyle('background-fill').value;
29981 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
29982 this.gradientFillStyle(context, ele, backgroundFill, opacity);
29983 } else {
29984 var backgroundColor = ele.pstyle('background-color').value;
29985 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
29986 }
29987 };
29988 CRp$4.gradientStrokeStyle = function (context, ele, fill, opacity) {
29989 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
29990 if (!gradientStyle) return null; // error
29991 context.strokeStyle = gradientStyle;
29992 };
29993 CRp$4.colorStrokeStyle = function (context, r, g, b, a) {
29994 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29995 // turn off for now, seems context does its own caching
29996
29997 // var cache = this.paintCache(context);
29998
29999 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30000
30001 // if( cache.strokeStyle !== strokeStyle ){
30002 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30003 // }
30004 };
30005
30006 CRp$4.eleStrokeStyle = function (context, ele, opacity) {
30007 var lineFill = ele.pstyle('line-fill').value;
30008 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30009 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30010 } else {
30011 var lineColor = ele.pstyle('line-color').value;
30012 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30013 }
30014 };
30015
30016 // Resize canvas
30017 CRp$4.matchCanvasSize = function (container) {
30018 var r = this;
30019 var data = r.data;
30020 var bb = r.findContainerClientCoords();
30021 var width = bb[2];
30022 var height = bb[3];
30023 var pixelRatio = r.getPixelRatio();
30024 var mbPxRatio = r.motionBlurPxRatio;
30025 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30026 pixelRatio = mbPxRatio;
30027 }
30028 var canvasWidth = width * pixelRatio;
30029 var canvasHeight = height * pixelRatio;
30030 var canvas;
30031 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30032 return; // save cycles if same
30033 }
30034
30035 r.fontCaches = null; // resizing resets the style
30036
30037 var canvasContainer = data.canvasContainer;
30038 canvasContainer.style.width = width + 'px';
30039 canvasContainer.style.height = height + 'px';
30040 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30041 canvas = data.canvases[i];
30042 canvas.width = canvasWidth;
30043 canvas.height = canvasHeight;
30044 canvas.style.width = width + 'px';
30045 canvas.style.height = height + 'px';
30046 }
30047 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30048 canvas = data.bufferCanvases[i];
30049 canvas.width = canvasWidth;
30050 canvas.height = canvasHeight;
30051 canvas.style.width = width + 'px';
30052 canvas.style.height = height + 'px';
30053 }
30054 r.textureMult = 1;
30055 if (pixelRatio <= 1) {
30056 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30057 r.textureMult = 2;
30058 canvas.width = canvasWidth * r.textureMult;
30059 canvas.height = canvasHeight * r.textureMult;
30060 }
30061 r.canvasWidth = canvasWidth;
30062 r.canvasHeight = canvasHeight;
30063 };
30064 CRp$4.renderTo = function (cxt, zoom, pan, pxRatio) {
30065 this.render({
30066 forcedContext: cxt,
30067 forcedZoom: zoom,
30068 forcedPan: pan,
30069 drawAllLayers: true,
30070 forcedPxRatio: pxRatio
30071 });
30072 };
30073 CRp$4.render = function (options) {
30074 options = options || staticEmptyObject();
30075 var forcedContext = options.forcedContext;
30076 var drawAllLayers = options.drawAllLayers;
30077 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30078 var forcedZoom = options.forcedZoom;
30079 var forcedPan = options.forcedPan;
30080 var r = this;
30081 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30082 var cy = r.cy;
30083 var data = r.data;
30084 var needDraw = data.canvasNeedsRedraw;
30085 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30086 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30087 var mbPxRatio = r.motionBlurPxRatio;
30088 var hasCompoundNodes = cy.hasCompoundNodes();
30089 var inNodeDragGesture = r.hoverData.draggingEles;
30090 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30091 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30092 var motionBlurFadeEffect = motionBlur;
30093 if (!forcedContext) {
30094 if (r.prevPxRatio !== pixelRatio) {
30095 r.invalidateContainerClientCoordsCache();
30096 r.matchCanvasSize(r.container);
30097 r.redrawHint('eles', true);
30098 r.redrawHint('drag', true);
30099 }
30100 r.prevPxRatio = pixelRatio;
30101 }
30102 if (!forcedContext && r.motionBlurTimeout) {
30103 clearTimeout(r.motionBlurTimeout);
30104 }
30105 if (motionBlur) {
30106 if (r.mbFrames == null) {
30107 r.mbFrames = 0;
30108 }
30109 r.mbFrames++;
30110 if (r.mbFrames < 3) {
30111 // need several frames before even high quality motionblur
30112 motionBlurFadeEffect = false;
30113 }
30114
30115 // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30116 if (r.mbFrames > r.minMbLowQualFrames) {
30117 //r.fullQualityMb = false;
30118 r.motionBlurPxRatio = r.mbPxRBlurry;
30119 }
30120 }
30121 if (r.clearingMotionBlur) {
30122 r.motionBlurPxRatio = 1;
30123 }
30124
30125 // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30126 // because a rogue async texture frame would clear needDraw
30127 if (r.textureDrawLastFrame && !textureDraw) {
30128 needDraw[r.NODE] = true;
30129 needDraw[r.SELECT_BOX] = true;
30130 }
30131 var style = cy.style();
30132 var zoom = cy.zoom();
30133 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30134 var pan = cy.pan();
30135 var effectivePan = {
30136 x: pan.x,
30137 y: pan.y
30138 };
30139 var vp = {
30140 zoom: zoom,
30141 pan: {
30142 x: pan.x,
30143 y: pan.y
30144 }
30145 };
30146 var prevVp = r.prevViewport;
30147 var viewportIsDiff = prevVp === undefined || vp.zoom !== prevVp.zoom || vp.pan.x !== prevVp.pan.x || vp.pan.y !== prevVp.pan.y;
30148
30149 // we want the low quality motionblur only when the viewport is being manipulated etc (where it's not noticed)
30150 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30151 r.motionBlurPxRatio = 1;
30152 }
30153 if (forcedPan) {
30154 effectivePan = forcedPan;
30155 }
30156
30157 // apply pixel ratio
30158
30159 effectiveZoom *= pixelRatio;
30160 effectivePan.x *= pixelRatio;
30161 effectivePan.y *= pixelRatio;
30162 var eles = r.getCachedZSortedEles();
30163 function mbclear(context, x, y, w, h) {
30164 var gco = context.globalCompositeOperation;
30165 context.globalCompositeOperation = 'destination-out';
30166 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30167 context.fillRect(x, y, w, h);
30168 context.globalCompositeOperation = gco;
30169 }
30170 function setContextTransform(context, clear) {
30171 var ePan, eZoom, w, h;
30172 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30173 ePan = {
30174 x: pan.x * mbPxRatio,
30175 y: pan.y * mbPxRatio
30176 };
30177 eZoom = zoom * mbPxRatio;
30178 w = r.canvasWidth * mbPxRatio;
30179 h = r.canvasHeight * mbPxRatio;
30180 } else {
30181 ePan = effectivePan;
30182 eZoom = effectiveZoom;
30183 w = r.canvasWidth;
30184 h = r.canvasHeight;
30185 }
30186 context.setTransform(1, 0, 0, 1, 0, 0);
30187 if (clear === 'motionBlur') {
30188 mbclear(context, 0, 0, w, h);
30189 } else if (!forcedContext && (clear === undefined || clear)) {
30190 context.clearRect(0, 0, w, h);
30191 }
30192 if (!drawAllLayers) {
30193 context.translate(ePan.x, ePan.y);
30194 context.scale(eZoom, eZoom);
30195 }
30196 if (forcedPan) {
30197 context.translate(forcedPan.x, forcedPan.y);
30198 }
30199 if (forcedZoom) {
30200 context.scale(forcedZoom, forcedZoom);
30201 }
30202 }
30203 if (!textureDraw) {
30204 r.textureDrawLastFrame = false;
30205 }
30206 if (textureDraw) {
30207 r.textureDrawLastFrame = true;
30208 if (!r.textureCache) {
30209 r.textureCache = {};
30210 r.textureCache.bb = cy.mutableElements().boundingBox();
30211 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30212 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30213 cxt.setTransform(1, 0, 0, 1, 0, 0);
30214 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30215 r.render({
30216 forcedContext: cxt,
30217 drawOnlyNodeLayer: true,
30218 forcedPxRatio: pixelRatio * r.textureMult
30219 });
30220 var vp = r.textureCache.viewport = {
30221 zoom: cy.zoom(),
30222 pan: cy.pan(),
30223 width: r.canvasWidth,
30224 height: r.canvasHeight
30225 };
30226 vp.mpan = {
30227 x: (0 - vp.pan.x) / vp.zoom,
30228 y: (0 - vp.pan.y) / vp.zoom
30229 };
30230 }
30231 needDraw[r.DRAG] = false;
30232 needDraw[r.NODE] = false;
30233 var context = data.contexts[r.NODE];
30234 var texture = r.textureCache.texture;
30235 var vp = r.textureCache.viewport;
30236 context.setTransform(1, 0, 0, 1, 0, 0);
30237 if (motionBlur) {
30238 mbclear(context, 0, 0, vp.width, vp.height);
30239 } else {
30240 context.clearRect(0, 0, vp.width, vp.height);
30241 }
30242 var outsideBgColor = style.core('outside-texture-bg-color').value;
30243 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30244 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30245 context.fillRect(0, 0, vp.width, vp.height);
30246 var zoom = cy.zoom();
30247 setContextTransform(context, false);
30248 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30249 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30250 } else if (r.textureOnViewport && !forcedContext) {
30251 // clear the cache since we don't need it
30252 r.textureCache = null;
30253 }
30254 var extent = cy.extent();
30255 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30256 var hideEdges = r.hideEdgesOnViewport && vpManip;
30257 var needMbClear = [];
30258 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30259 if (needMbClear[r.NODE]) {
30260 r.clearedForMotionBlur[r.NODE] = true;
30261 }
30262 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30263 if (needMbClear[r.DRAG]) {
30264 r.clearedForMotionBlur[r.DRAG] = true;
30265 }
30266 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30267 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30268 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30269 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30270 setContextTransform(context, clear);
30271 if (hideEdges) {
30272 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30273 } else {
30274 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30275 }
30276 if (r.debug) {
30277 r.drawDebugPoints(context, eles.nondrag);
30278 }
30279 if (!drawAllLayers && !motionBlur) {
30280 needDraw[r.NODE] = false;
30281 }
30282 }
30283 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30284 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30285 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30286 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30287 if (hideEdges) {
30288 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30289 } else {
30290 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30291 }
30292 if (r.debug) {
30293 r.drawDebugPoints(context, eles.drag);
30294 }
30295 if (!drawAllLayers && !motionBlur) {
30296 needDraw[r.DRAG] = false;
30297 }
30298 }
30299 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30300 var context = forcedContext || data.contexts[r.SELECT_BOX];
30301 setContextTransform(context);
30302 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30303 var zoom = r.cy.zoom();
30304 var borderWidth = style.core('selection-box-border-width').value / zoom;
30305 context.lineWidth = borderWidth;
30306 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 + ')';
30307 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30308 if (borderWidth > 0) {
30309 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 + ')';
30310 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30311 }
30312 }
30313 if (data.bgActivePosistion && !r.hoverData.selecting) {
30314 var zoom = r.cy.zoom();
30315 var pos = data.bgActivePosistion;
30316 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 + ')';
30317 context.beginPath();
30318 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30319 context.fill();
30320 }
30321 var timeToRender = r.lastRedrawTime;
30322 if (r.showFps && timeToRender) {
30323 timeToRender = Math.round(timeToRender);
30324 var fps = Math.round(1000 / timeToRender);
30325 context.setTransform(1, 0, 0, 1, 0, 0);
30326 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30327 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30328 context.lineWidth = 1;
30329 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30330 var maxFps = 60;
30331 context.strokeRect(0, 30, 250, 20);
30332 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30333 }
30334 if (!drawAllLayers) {
30335 needDraw[r.SELECT_BOX] = false;
30336 }
30337 }
30338
30339 // motionblur: blit rendered blurry frames
30340 if (motionBlur && mbPxRatio !== 1) {
30341 var cxtNode = data.contexts[r.NODE];
30342 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30343 var cxtDrag = data.contexts[r.DRAG];
30344 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30345 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30346 cxt.setTransform(1, 0, 0, 1, 0, 0);
30347 if (needClear || !motionBlurFadeEffect) {
30348 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30349 } else {
30350 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30351 }
30352 var pxr = mbPxRatio;
30353 cxt.drawImage(txt,
30354 // img
30355 0, 0,
30356 // sx, sy
30357 r.canvasWidth * pxr, r.canvasHeight * pxr,
30358 // sw, sh
30359 0, 0,
30360 // x, y
30361 r.canvasWidth, r.canvasHeight // w, h
30362 );
30363 };
30364
30365 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30366 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30367 needDraw[r.NODE] = false;
30368 }
30369 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30370 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30371 needDraw[r.DRAG] = false;
30372 }
30373 }
30374 r.prevViewport = vp;
30375 if (r.clearingMotionBlur) {
30376 r.clearingMotionBlur = false;
30377 r.motionBlurCleared = true;
30378 r.motionBlur = true;
30379 }
30380 if (motionBlur) {
30381 r.motionBlurTimeout = setTimeout(function () {
30382 r.motionBlurTimeout = null;
30383 r.clearedForMotionBlur[r.NODE] = false;
30384 r.clearedForMotionBlur[r.DRAG] = false;
30385 r.motionBlur = false;
30386 r.clearingMotionBlur = !textureDraw;
30387 r.mbFrames = 0;
30388 needDraw[r.NODE] = true;
30389 needDraw[r.DRAG] = true;
30390 r.redraw();
30391 }, motionBlurDelay);
30392 }
30393 if (!forcedContext) {
30394 cy.emit('render');
30395 }
30396 };
30397
30398 var CRp$3 = {};
30399
30400 // @O Polygon drawing
30401 CRp$3.drawPolygonPath = function (context, x, y, width, height, points) {
30402 var halfW = width / 2;
30403 var halfH = height / 2;
30404 if (context.beginPath) {
30405 context.beginPath();
30406 }
30407 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30408 for (var i = 1; i < points.length / 2; i++) {
30409 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30410 }
30411 context.closePath();
30412 };
30413 CRp$3.drawRoundPolygonPath = function (context, x, y, width, height, points, corners) {
30414 corners.forEach(function (corner) {
30415 return drawPreparedRoundCorner(context, corner);
30416 });
30417 context.closePath();
30418 };
30419
30420 // Round rectangle drawing
30421 CRp$3.drawRoundRectanglePath = function (context, x, y, width, height, radius) {
30422 var halfWidth = width / 2;
30423 var halfHeight = height / 2;
30424 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : Math.min(radius, halfHeight, halfWidth);
30425 if (context.beginPath) {
30426 context.beginPath();
30427 }
30428
30429 // Start at top middle
30430 context.moveTo(x, y - halfHeight);
30431 // Arc from middle top to right side
30432 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius);
30433 // Arc from right side to bottom
30434 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30435 // Arc from bottom to left side
30436 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30437 // Arc from left side to topBorder
30438 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius);
30439 // Join line
30440 context.lineTo(x, y - halfHeight);
30441 context.closePath();
30442 };
30443 CRp$3.drawBottomRoundRectanglePath = function (context, x, y, width, height, radius) {
30444 var halfWidth = width / 2;
30445 var halfHeight = height / 2;
30446 var cornerRadius = radius === 'auto' ? getRoundRectangleRadius(width, height) : radius;
30447 if (context.beginPath) {
30448 context.beginPath();
30449 }
30450
30451 // Start at top middle
30452 context.moveTo(x, y - halfHeight);
30453 context.lineTo(x + halfWidth, y - halfHeight);
30454 context.lineTo(x + halfWidth, y);
30455 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30456 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30457 context.lineTo(x - halfWidth, y - halfHeight);
30458 context.lineTo(x, y - halfHeight);
30459 context.closePath();
30460 };
30461 CRp$3.drawCutRectanglePath = function (context, x, y, width, height, points, corners) {
30462 var halfWidth = width / 2;
30463 var halfHeight = height / 2;
30464 var cornerLength = corners === 'auto' ? getCutRectangleCornerLength() : corners;
30465 if (context.beginPath) {
30466 context.beginPath();
30467 }
30468 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30469 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30470 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30471 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30472 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30473 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30474 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30475 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30476 context.closePath();
30477 };
30478 CRp$3.drawBarrelPath = function (context, x, y, width, height) {
30479 var halfWidth = width / 2;
30480 var halfHeight = height / 2;
30481 var xBegin = x - halfWidth;
30482 var xEnd = x + halfWidth;
30483 var yBegin = y - halfHeight;
30484 var yEnd = y + halfHeight;
30485 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30486 var wOffset = barrelCurveConstants.widthOffset;
30487 var hOffset = barrelCurveConstants.heightOffset;
30488 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30489 if (context.beginPath) {
30490 context.beginPath();
30491 }
30492 context.moveTo(xBegin, yBegin + hOffset);
30493 context.lineTo(xBegin, yEnd - hOffset);
30494 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30495 context.lineTo(xEnd - wOffset, yEnd);
30496 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30497 context.lineTo(xEnd, yBegin + hOffset);
30498 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30499 context.lineTo(xBegin + wOffset, yBegin);
30500 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30501 context.closePath();
30502 };
30503 var sin0 = Math.sin(0);
30504 var cos0 = Math.cos(0);
30505 var sin = {};
30506 var cos = {};
30507 var ellipseStepSize = Math.PI / 40;
30508 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30509 sin[i] = Math.sin(i);
30510 cos[i] = Math.cos(i);
30511 }
30512 CRp$3.drawEllipsePath = function (context, centerX, centerY, width, height) {
30513 if (context.beginPath) {
30514 context.beginPath();
30515 }
30516 if (context.ellipse) {
30517 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30518 } else {
30519 var xPos, yPos;
30520 var rw = width / 2;
30521 var rh = height / 2;
30522 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30523 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30524 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30525 if (i === 0) {
30526 context.moveTo(xPos, yPos);
30527 } else {
30528 context.lineTo(xPos, yPos);
30529 }
30530 }
30531 }
30532 context.closePath();
30533 };
30534
30535 /* global atob, ArrayBuffer, Uint8Array, Blob */
30536 var CRp$2 = {};
30537 CRp$2.createBuffer = function (w, h) {
30538 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30539 buffer.width = w;
30540 buffer.height = h;
30541 return [buffer, buffer.getContext('2d')];
30542 };
30543 CRp$2.bufferCanvasImage = function (options) {
30544 var cy = this.cy;
30545 var eles = cy.mutableElements();
30546 var bb = eles.boundingBox();
30547 var ctrRect = this.findContainerClientCoords();
30548 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30549 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30550 var specdMaxDims = number$1(options.maxWidth) || number$1(options.maxHeight);
30551 var pxRatio = this.getPixelRatio();
30552 var scale = 1;
30553 if (options.scale !== undefined) {
30554 width *= options.scale;
30555 height *= options.scale;
30556 scale = options.scale;
30557 } else if (specdMaxDims) {
30558 var maxScaleW = Infinity;
30559 var maxScaleH = Infinity;
30560 if (number$1(options.maxWidth)) {
30561 maxScaleW = scale * options.maxWidth / width;
30562 }
30563 if (number$1(options.maxHeight)) {
30564 maxScaleH = scale * options.maxHeight / height;
30565 }
30566 scale = Math.min(maxScaleW, maxScaleH);
30567 width *= scale;
30568 height *= scale;
30569 }
30570 if (!specdMaxDims) {
30571 width *= pxRatio;
30572 height *= pxRatio;
30573 scale *= pxRatio;
30574 }
30575 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30576
30577 buffCanvas.width = width;
30578 buffCanvas.height = height;
30579 buffCanvas.style.width = width + 'px';
30580 buffCanvas.style.height = height + 'px';
30581 var buffCxt = buffCanvas.getContext('2d');
30582
30583 // Rasterize the layers, but only if container has nonzero size
30584 if (width > 0 && height > 0) {
30585 buffCxt.clearRect(0, 0, width, height);
30586 buffCxt.globalCompositeOperation = 'source-over';
30587 var zsortedEles = this.getCachedZSortedEles();
30588 if (options.full) {
30589 // draw the full bounds of the graph
30590 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30591 buffCxt.scale(scale, scale);
30592 this.drawElements(buffCxt, zsortedEles);
30593 buffCxt.scale(1 / scale, 1 / scale);
30594 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30595 } else {
30596 // draw the current view
30597 var pan = cy.pan();
30598 var translation = {
30599 x: pan.x * scale,
30600 y: pan.y * scale
30601 };
30602 scale *= cy.zoom();
30603 buffCxt.translate(translation.x, translation.y);
30604 buffCxt.scale(scale, scale);
30605 this.drawElements(buffCxt, zsortedEles);
30606 buffCxt.scale(1 / scale, 1 / scale);
30607 buffCxt.translate(-translation.x, -translation.y);
30608 }
30609
30610 // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30611 if (options.bg) {
30612 buffCxt.globalCompositeOperation = 'destination-over';
30613 buffCxt.fillStyle = options.bg;
30614 buffCxt.rect(0, 0, width, height);
30615 buffCxt.fill();
30616 }
30617 }
30618 return buffCanvas;
30619 };
30620 function b64ToBlob(b64, mimeType) {
30621 var bytes = atob(b64);
30622 var buff = new ArrayBuffer(bytes.length);
30623 var buffUint8 = new Uint8Array(buff);
30624 for (var i = 0; i < bytes.length; i++) {
30625 buffUint8[i] = bytes.charCodeAt(i);
30626 }
30627 return new Blob([buff], {
30628 type: mimeType
30629 });
30630 }
30631 function b64UriToB64(b64uri) {
30632 var i = b64uri.indexOf(',');
30633 return b64uri.substr(i + 1);
30634 }
30635 function output(options, canvas, mimeType) {
30636 var getB64Uri = function getB64Uri() {
30637 return canvas.toDataURL(mimeType, options.quality);
30638 };
30639 switch (options.output) {
30640 case 'blob-promise':
30641 return new Promise$1(function (resolve, reject) {
30642 try {
30643 canvas.toBlob(function (blob) {
30644 if (blob != null) {
30645 resolve(blob);
30646 } else {
30647 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30648 }
30649 }, mimeType, options.quality);
30650 } catch (err) {
30651 reject(err);
30652 }
30653 });
30654 case 'blob':
30655 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30656 case 'base64':
30657 return b64UriToB64(getB64Uri());
30658 case 'base64uri':
30659 default:
30660 return getB64Uri();
30661 }
30662 }
30663 CRp$2.png = function (options) {
30664 return output(options, this.bufferCanvasImage(options), 'image/png');
30665 };
30666 CRp$2.jpg = function (options) {
30667 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30668 };
30669
30670 var CRp$1 = {};
30671 CRp$1.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points, corners) {
30672 switch (name) {
30673 case 'ellipse':
30674 return this.drawEllipsePath(context, centerX, centerY, width, height);
30675 case 'polygon':
30676 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30677 case 'round-polygon':
30678 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points, corners);
30679 case 'roundrectangle':
30680 case 'round-rectangle':
30681 return this.drawRoundRectanglePath(context, centerX, centerY, width, height, corners);
30682 case 'cutrectangle':
30683 case 'cut-rectangle':
30684 return this.drawCutRectanglePath(context, centerX, centerY, width, height, points, corners);
30685 case 'bottomroundrectangle':
30686 case 'bottom-round-rectangle':
30687 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height, corners);
30688 case 'barrel':
30689 return this.drawBarrelPath(context, centerX, centerY, width, height);
30690 }
30691 };
30692
30693 var CR = CanvasRenderer;
30694 var CRp = CanvasRenderer.prototype;
30695 CRp.CANVAS_LAYERS = 3;
30696 //
30697 CRp.SELECT_BOX = 0;
30698 CRp.DRAG = 1;
30699 CRp.NODE = 2;
30700 CRp.BUFFER_COUNT = 3;
30701 //
30702 CRp.TEXTURE_BUFFER = 0;
30703 CRp.MOTIONBLUR_BUFFER_NODE = 1;
30704 CRp.MOTIONBLUR_BUFFER_DRAG = 2;
30705 function CanvasRenderer(options) {
30706 var r = this;
30707 var containerWindow = r.cy.window();
30708 var document = containerWindow.document;
30709 r.data = {
30710 canvases: new Array(CRp.CANVAS_LAYERS),
30711 contexts: new Array(CRp.CANVAS_LAYERS),
30712 canvasNeedsRedraw: new Array(CRp.CANVAS_LAYERS),
30713 bufferCanvases: new Array(CRp.BUFFER_COUNT),
30714 bufferContexts: new Array(CRp.CANVAS_LAYERS)
30715 };
30716 var tapHlOffAttr = '-webkit-tap-highlight-color';
30717 var tapHlOffStyle = 'rgba(0,0,0,0)';
30718 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30719 var containerStyle = r.data.canvasContainer.style;
30720 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30721 containerStyle.position = 'relative';
30722 containerStyle.zIndex = '0';
30723 containerStyle.overflow = 'hidden';
30724 var container = options.cy.container();
30725 container.appendChild(r.data.canvasContainer);
30726 container.style[tapHlOffAttr] = tapHlOffStyle;
30727 var styleMap = {
30728 '-webkit-user-select': 'none',
30729 '-moz-user-select': '-moz-none',
30730 'user-select': 'none',
30731 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30732 'outline-style': 'none'
30733 };
30734 if (ms()) {
30735 styleMap['-ms-touch-action'] = 'none';
30736 styleMap['touch-action'] = 'none';
30737 }
30738 for (var i = 0; i < CRp.CANVAS_LAYERS; i++) {
30739 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30740 r.data.contexts[i] = canvas.getContext('2d');
30741 Object.keys(styleMap).forEach(function (k) {
30742 canvas.style[k] = styleMap[k];
30743 });
30744 canvas.style.position = 'absolute';
30745 canvas.setAttribute('data-id', 'layer' + i);
30746 canvas.style.zIndex = String(CRp.CANVAS_LAYERS - i);
30747 r.data.canvasContainer.appendChild(canvas);
30748 r.data.canvasNeedsRedraw[i] = false;
30749 }
30750 r.data.topCanvas = r.data.canvases[0];
30751 r.data.canvases[CRp.NODE].setAttribute('data-id', 'layer' + CRp.NODE + '-node');
30752 r.data.canvases[CRp.SELECT_BOX].setAttribute('data-id', 'layer' + CRp.SELECT_BOX + '-selectbox');
30753 r.data.canvases[CRp.DRAG].setAttribute('data-id', 'layer' + CRp.DRAG + '-drag');
30754 for (var i = 0; i < CRp.BUFFER_COUNT; i++) {
30755 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30756 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30757 r.data.bufferCanvases[i].style.position = 'absolute';
30758 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30759 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30760 r.data.bufferCanvases[i].style.visibility = 'hidden';
30761 //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30762 }
30763
30764 r.pathsEnabled = true;
30765 var emptyBb = makeBoundingBox();
30766 var getBoxCenter = function getBoxCenter(bb) {
30767 return {
30768 x: (bb.x1 + bb.x2) / 2,
30769 y: (bb.y1 + bb.y2) / 2
30770 };
30771 };
30772 var getCenterOffset = function getCenterOffset(bb) {
30773 return {
30774 x: -bb.w / 2,
30775 y: -bb.h / 2
30776 };
30777 };
30778 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30779 var _p = ele[0]._private;
30780 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30781 return !same;
30782 };
30783 var getStyleKey = function getStyleKey(ele) {
30784 return ele[0]._private.nodeKey;
30785 };
30786 var getLabelKey = function getLabelKey(ele) {
30787 return ele[0]._private.labelStyleKey;
30788 };
30789 var getSourceLabelKey = function getSourceLabelKey(ele) {
30790 return ele[0]._private.sourceLabelStyleKey;
30791 };
30792 var getTargetLabelKey = function getTargetLabelKey(ele) {
30793 return ele[0]._private.targetLabelStyleKey;
30794 };
30795 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30796 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30797 };
30798 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30799 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30800 };
30801 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30802 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30803 };
30804 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30805 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30806 };
30807 var getElementBox = function getElementBox(ele) {
30808 ele.boundingBox();
30809 return ele[0]._private.bodyBounds;
30810 };
30811 var getLabelBox = function getLabelBox(ele) {
30812 ele.boundingBox();
30813 return ele[0]._private.labelBounds.main || emptyBb;
30814 };
30815 var getSourceLabelBox = function getSourceLabelBox(ele) {
30816 ele.boundingBox();
30817 return ele[0]._private.labelBounds.source || emptyBb;
30818 };
30819 var getTargetLabelBox = function getTargetLabelBox(ele) {
30820 ele.boundingBox();
30821 return ele[0]._private.labelBounds.target || emptyBb;
30822 };
30823 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
30824 return scaledLabelShown;
30825 };
30826 var getElementRotationPoint = function getElementRotationPoint(ele) {
30827 return getBoxCenter(getElementBox(ele));
30828 };
30829 var addTextMargin = function addTextMargin(prefix, pt, ele) {
30830 var pre = prefix ? prefix + '-' : '';
30831 return {
30832 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
30833 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
30834 };
30835 };
30836 var getRsPt = function getRsPt(ele, x, y) {
30837 var rs = ele[0]._private.rscratch;
30838 return {
30839 x: rs[x],
30840 y: rs[y]
30841 };
30842 };
30843 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
30844 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
30845 };
30846 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
30847 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
30848 };
30849 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
30850 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
30851 };
30852 var getElementRotationOffset = function getElementRotationOffset(ele) {
30853 return getCenterOffset(getElementBox(ele));
30854 };
30855 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
30856 return getCenterOffset(getSourceLabelBox(ele));
30857 };
30858 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
30859 return getCenterOffset(getTargetLabelBox(ele));
30860 };
30861 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
30862 var bb = getLabelBox(ele);
30863 var p = getCenterOffset(getLabelBox(ele));
30864 if (ele.isNode()) {
30865 switch (ele.pstyle('text-halign').value) {
30866 case 'left':
30867 p.x = -bb.w;
30868 break;
30869 case 'right':
30870 p.x = 0;
30871 break;
30872 }
30873 switch (ele.pstyle('text-valign').value) {
30874 case 'top':
30875 p.y = -bb.h;
30876 break;
30877 case 'bottom':
30878 p.y = 0;
30879 break;
30880 }
30881 }
30882 return p;
30883 };
30884 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
30885 getKey: getStyleKey,
30886 doesEleInvalidateKey: backgroundTimestampHasChanged,
30887 drawElement: drawElement,
30888 getBoundingBox: getElementBox,
30889 getRotationPoint: getElementRotationPoint,
30890 getRotationOffset: getElementRotationOffset,
30891 allowEdgeTxrCaching: false,
30892 allowParentTxrCaching: false
30893 });
30894 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
30895 getKey: getLabelKey,
30896 drawElement: drawLabel,
30897 getBoundingBox: getLabelBox,
30898 getRotationPoint: getLabelRotationPoint,
30899 getRotationOffset: getLabelRotationOffset,
30900 isVisible: isLabelVisibleAtScale
30901 });
30902 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
30903 getKey: getSourceLabelKey,
30904 drawElement: drawSourceLabel,
30905 getBoundingBox: getSourceLabelBox,
30906 getRotationPoint: getSourceLabelRotationPoint,
30907 getRotationOffset: getSourceLabelRotationOffset,
30908 isVisible: isLabelVisibleAtScale
30909 });
30910 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
30911 getKey: getTargetLabelKey,
30912 drawElement: drawTargetLabel,
30913 getBoundingBox: getTargetLabelBox,
30914 getRotationPoint: getTargetLabelRotationPoint,
30915 getRotationOffset: getTargetLabelRotationOffset,
30916 isVisible: isLabelVisibleAtScale
30917 });
30918 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
30919 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
30920 // each cache should check for sub-key diff to see that the update affects that cache particularly
30921 eleTxrCache.invalidateElements(eles);
30922 lblTxrCache.invalidateElements(eles);
30923 slbTxrCache.invalidateElements(eles);
30924 tlbTxrCache.invalidateElements(eles);
30925
30926 // any change invalidates the layers
30927 lyrTxrCache.invalidateElements(eles);
30928
30929 // update the old bg timestamp so diffs can be done in the ele txr caches
30930 for (var _i = 0; _i < eles.length; _i++) {
30931 var _p = eles[_i]._private;
30932 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
30933 }
30934 });
30935 var refineInLayers = function refineInLayers(reqs) {
30936 for (var i = 0; i < reqs.length; i++) {
30937 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
30938 }
30939 };
30940 eleTxrCache.onDequeue(refineInLayers);
30941 lblTxrCache.onDequeue(refineInLayers);
30942 slbTxrCache.onDequeue(refineInLayers);
30943 tlbTxrCache.onDequeue(refineInLayers);
30944 }
30945 CRp.redrawHint = function (group, bool) {
30946 var r = this;
30947 switch (group) {
30948 case 'eles':
30949 r.data.canvasNeedsRedraw[CRp.NODE] = bool;
30950 break;
30951 case 'drag':
30952 r.data.canvasNeedsRedraw[CRp.DRAG] = bool;
30953 break;
30954 case 'select':
30955 r.data.canvasNeedsRedraw[CRp.SELECT_BOX] = bool;
30956 break;
30957 }
30958 };
30959
30960 // whether to use Path2D caching for drawing
30961 var pathsImpld = typeof Path2D !== 'undefined';
30962 CRp.path2dEnabled = function (on) {
30963 if (on === undefined) {
30964 return this.pathsEnabled;
30965 }
30966 this.pathsEnabled = on ? true : false;
30967 };
30968 CRp.usePaths = function () {
30969 return pathsImpld && this.pathsEnabled;
30970 };
30971 CRp.setImgSmoothing = function (context, bool) {
30972 if (context.imageSmoothingEnabled != null) {
30973 context.imageSmoothingEnabled = bool;
30974 } else {
30975 context.webkitImageSmoothingEnabled = bool;
30976 context.mozImageSmoothingEnabled = bool;
30977 context.msImageSmoothingEnabled = bool;
30978 }
30979 };
30980 CRp.getImgSmoothing = function (context) {
30981 if (context.imageSmoothingEnabled != null) {
30982 return context.imageSmoothingEnabled;
30983 } else {
30984 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
30985 }
30986 };
30987 CRp.makeOffscreenCanvas = function (width, height) {
30988 var canvas;
30989 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ("undefined" )) {
30990 canvas = new OffscreenCanvas(width, height);
30991 } else {
30992 var containerWindow = this.cy.window();
30993 var document = containerWindow.document;
30994 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
30995 canvas.width = width;
30996 canvas.height = height;
30997 }
30998 return canvas;
30999 };
31000 [CRp$a, CRp$9, CRp$8, CRp$7, CRp$6, CRp$5, CRp$4, CRp$3, CRp$2, CRp$1].forEach(function (props) {
31001 extend(CRp, props);
31002 });
31003
31004 var renderer = [{
31005 name: 'null',
31006 impl: NullRenderer
31007 }, {
31008 name: 'base',
31009 impl: BR
31010 }, {
31011 name: 'canvas',
31012 impl: CR
31013 }];
31014
31015 var incExts = [{
31016 type: 'layout',
31017 extensions: layout
31018 }, {
31019 type: 'renderer',
31020 extensions: renderer
31021 }];
31022
31023 // registered extensions to cytoscape, indexed by name
31024 var extensions = {};
31025
31026 // registered modules for extensions, indexed by name
31027 var modules = {};
31028 function setExtension(type, name, registrant) {
31029 var ext = registrant;
31030 var overrideErr = function overrideErr(field) {
31031 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31032 };
31033 if (type === 'core') {
31034 if (Core.prototype[name]) {
31035 return overrideErr(name);
31036 } else {
31037 Core.prototype[name] = registrant;
31038 }
31039 } else if (type === 'collection') {
31040 if (Collection.prototype[name]) {
31041 return overrideErr(name);
31042 } else {
31043 Collection.prototype[name] = registrant;
31044 }
31045 } else if (type === 'layout') {
31046 // fill in missing layout functions in the prototype
31047
31048 var Layout = function Layout(options) {
31049 this.options = options;
31050 registrant.call(this, options);
31051
31052 // make sure layout has _private for use w/ std apis like .on()
31053 if (!plainObject(this._private)) {
31054 this._private = {};
31055 }
31056 this._private.cy = options.cy;
31057 this._private.listeners = [];
31058 this.createEmitter();
31059 };
31060 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31061 var optLayoutFns = [];
31062 for (var i = 0; i < optLayoutFns.length; i++) {
31063 var fnName = optLayoutFns[i];
31064 layoutProto[fnName] = layoutProto[fnName] || function () {
31065 return this;
31066 };
31067 }
31068
31069 // either .start() or .run() is defined, so autogen the other
31070 if (layoutProto.start && !layoutProto.run) {
31071 layoutProto.run = function () {
31072 this.start();
31073 return this;
31074 };
31075 } else if (!layoutProto.start && layoutProto.run) {
31076 layoutProto.start = function () {
31077 this.run();
31078 return this;
31079 };
31080 }
31081 var regStop = registrant.prototype.stop;
31082 layoutProto.stop = function () {
31083 var opts = this.options;
31084 if (opts && opts.animate) {
31085 var anis = this.animations;
31086 if (anis) {
31087 for (var _i = 0; _i < anis.length; _i++) {
31088 anis[_i].stop();
31089 }
31090 }
31091 }
31092 if (regStop) {
31093 regStop.call(this);
31094 } else {
31095 this.emit('layoutstop');
31096 }
31097 return this;
31098 };
31099 if (!layoutProto.destroy) {
31100 layoutProto.destroy = function () {
31101 return this;
31102 };
31103 }
31104 layoutProto.cy = function () {
31105 return this._private.cy;
31106 };
31107 var getCy = function getCy(layout) {
31108 return layout._private.cy;
31109 };
31110 var emitterOpts = {
31111 addEventFields: function addEventFields(layout, evt) {
31112 evt.layout = layout;
31113 evt.cy = getCy(layout);
31114 evt.target = layout;
31115 },
31116 bubble: function bubble() {
31117 return true;
31118 },
31119 parent: function parent(layout) {
31120 return getCy(layout);
31121 }
31122 };
31123 extend(layoutProto, {
31124 createEmitter: function createEmitter() {
31125 this._private.emitter = new Emitter(emitterOpts, this);
31126 return this;
31127 },
31128 emitter: function emitter() {
31129 return this._private.emitter;
31130 },
31131 on: function on(evt, cb) {
31132 this.emitter().on(evt, cb);
31133 return this;
31134 },
31135 one: function one(evt, cb) {
31136 this.emitter().one(evt, cb);
31137 return this;
31138 },
31139 once: function once(evt, cb) {
31140 this.emitter().one(evt, cb);
31141 return this;
31142 },
31143 removeListener: function removeListener(evt, cb) {
31144 this.emitter().removeListener(evt, cb);
31145 return this;
31146 },
31147 removeAllListeners: function removeAllListeners() {
31148 this.emitter().removeAllListeners();
31149 return this;
31150 },
31151 emit: function emit(evt, params) {
31152 this.emitter().emit(evt, params);
31153 return this;
31154 }
31155 });
31156 define.eventAliasesOn(layoutProto);
31157 ext = Layout; // replace with our wrapped layout
31158 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31159 // user registered renderers inherit from base
31160
31161 var BaseRenderer = getExtension('renderer', 'base');
31162 var bProto = BaseRenderer.prototype;
31163 var RegistrantRenderer = registrant;
31164 var rProto = registrant.prototype;
31165 var Renderer = function Renderer() {
31166 BaseRenderer.apply(this, arguments);
31167 RegistrantRenderer.apply(this, arguments);
31168 };
31169 var proto = Renderer.prototype;
31170 for (var pName in bProto) {
31171 var pVal = bProto[pName];
31172 var existsInR = rProto[pName] != null;
31173 if (existsInR) {
31174 return overrideErr(pName);
31175 }
31176 proto[pName] = pVal; // take impl from base
31177 }
31178
31179 for (var _pName in rProto) {
31180 proto[_pName] = rProto[_pName]; // take impl from registrant
31181 }
31182
31183 bProto.clientFunctions.forEach(function (name) {
31184 proto[name] = proto[name] || function () {
31185 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31186 };
31187 });
31188 ext = Renderer;
31189 } else if (type === '__proto__' || type === 'constructor' || type === 'prototype') {
31190 // to avoid potential prototype pollution
31191 return error(type + ' is an illegal type to be registered, possibly lead to prototype pollutions');
31192 }
31193 return setMap({
31194 map: extensions,
31195 keys: [type, name],
31196 value: ext
31197 });
31198 }
31199 function getExtension(type, name) {
31200 return getMap({
31201 map: extensions,
31202 keys: [type, name]
31203 });
31204 }
31205 function setModule(type, name, moduleType, moduleName, registrant) {
31206 return setMap({
31207 map: modules,
31208 keys: [type, name, moduleType, moduleName],
31209 value: registrant
31210 });
31211 }
31212 function getModule(type, name, moduleType, moduleName) {
31213 return getMap({
31214 map: modules,
31215 keys: [type, name, moduleType, moduleName]
31216 });
31217 }
31218 var extension = function extension() {
31219 // e.g. extension('renderer', 'svg')
31220 if (arguments.length === 2) {
31221 return getExtension.apply(null, arguments);
31222 }
31223
31224 // e.g. extension('renderer', 'svg', { ... })
31225 else if (arguments.length === 3) {
31226 return setExtension.apply(null, arguments);
31227 }
31228
31229 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31230 else if (arguments.length === 4) {
31231 return getModule.apply(null, arguments);
31232 }
31233
31234 // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31235 else if (arguments.length === 5) {
31236 return setModule.apply(null, arguments);
31237 } else {
31238 error('Invalid extension access syntax');
31239 }
31240 };
31241
31242 // allows a core instance to access extensions internally
31243 Core.prototype.extension = extension;
31244
31245 // included extensions
31246 incExts.forEach(function (group) {
31247 group.extensions.forEach(function (ext) {
31248 setExtension(group.type, ext.name, ext.impl);
31249 });
31250 });
31251
31252 // a dummy stylesheet object that doesn't need a reference to the core
31253 // (useful for init)
31254 var Stylesheet = function Stylesheet() {
31255 if (!(this instanceof Stylesheet)) {
31256 return new Stylesheet();
31257 }
31258 this.length = 0;
31259 };
31260 var sheetfn = Stylesheet.prototype;
31261 sheetfn.instanceString = function () {
31262 return 'stylesheet';
31263 };
31264
31265 // just store the selector to be parsed later
31266 sheetfn.selector = function (selector) {
31267 var i = this.length++;
31268 this[i] = {
31269 selector: selector,
31270 properties: []
31271 };
31272 return this; // chaining
31273 };
31274
31275 // just store the property to be parsed later
31276 sheetfn.css = function (name, value) {
31277 var i = this.length - 1;
31278 if (string(name)) {
31279 this[i].properties.push({
31280 name: name,
31281 value: value
31282 });
31283 } else if (plainObject(name)) {
31284 var map = name;
31285 var propNames = Object.keys(map);
31286 for (var j = 0; j < propNames.length; j++) {
31287 var key = propNames[j];
31288 var mapVal = map[key];
31289 if (mapVal == null) {
31290 continue;
31291 }
31292 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31293 if (prop == null) {
31294 continue;
31295 }
31296 var _name = prop.name;
31297 var _value = mapVal;
31298 this[i].properties.push({
31299 name: _name,
31300 value: _value
31301 });
31302 }
31303 }
31304 return this; // chaining
31305 };
31306
31307 sheetfn.style = sheetfn.css;
31308
31309 // generate a real style object from the dummy stylesheet
31310 sheetfn.generateStyle = function (cy) {
31311 var style = new Style(cy);
31312 return this.appendToStyle(style);
31313 };
31314
31315 // append a dummy stylesheet object on a real style object
31316 sheetfn.appendToStyle = function (style) {
31317 for (var i = 0; i < this.length; i++) {
31318 var context = this[i];
31319 var selector = context.selector;
31320 var props = context.properties;
31321 style.selector(selector); // apply selector
31322
31323 for (var j = 0; j < props.length; j++) {
31324 var prop = props[j];
31325 style.css(prop.name, prop.value); // apply property
31326 }
31327 }
31328
31329 return style;
31330 };
31331
31332 var version = "3.30.2";
31333
31334 var cytoscape = function cytoscape(options) {
31335 // if no options specified, use default
31336 if (options === undefined) {
31337 options = {};
31338 }
31339
31340 // create instance
31341 if (plainObject(options)) {
31342 return new Core(options);
31343 }
31344
31345 // allow for registration of extensions
31346 else if (string(options)) {
31347 return extension.apply(extension, arguments);
31348 }
31349 };
31350
31351 // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31352 cytoscape.use = function (ext) {
31353 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31354
31355 args.unshift(cytoscape); // cytoscape is first arg to ext
31356
31357 ext.apply(null, args);
31358 return this;
31359 };
31360 cytoscape.warnings = function (bool) {
31361 return warnings(bool);
31362 };
31363
31364 // replaced by build system
31365 cytoscape.version = version;
31366
31367 // expose public apis (mostly for extensions)
31368 cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31369
31370 return cytoscape;
31371
31372}));