UNPKG

cytoscape

Version:

Graph theory (a.k.a. network) library for analysis and visualisation

1,984 lines (1,659 loc) 872 kB
/** * Copyright (c) 2016-2022, The Cytoscape Consortium. * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the “Software”), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ import debounce from 'lodash/debounce'; import Heap from 'heap'; import get from 'lodash/get'; import set from 'lodash/set'; import toPath from 'lodash/toPath'; function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef var navigator = window$1 ? window$1.navigator : null; window$1 ? window$1.document : null; var typeofstr = _typeof(''); var typeofobj = _typeof({}); var typeoffn = _typeof(function () {}); var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement); var instanceStr = function instanceStr(obj) { return obj && obj.instanceString && fn$6(obj.instanceString) ? obj.instanceString() : null; }; var string = function string(obj) { return obj != null && _typeof(obj) == typeofstr; }; var fn$6 = function fn(obj) { return obj != null && _typeof(obj) === typeoffn; }; var array = function array(obj) { return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array); }; var plainObject = function plainObject(obj) { return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object; }; var object = function object(obj) { return obj != null && _typeof(obj) === typeofobj; }; var number$1 = function number(obj) { return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj); }; var integer = function integer(obj) { return number$1(obj) && Math.floor(obj) === obj; }; var htmlElement = function htmlElement(obj) { if ('undefined' === typeofhtmlele) { return undefined; } else { return null != obj && obj instanceof HTMLElement; } }; var elementOrCollection = function elementOrCollection(obj) { return element(obj) || collection(obj); }; var element = function element(obj) { return instanceStr(obj) === 'collection' && obj._private.single; }; var collection = function collection(obj) { return instanceStr(obj) === 'collection' && !obj._private.single; }; var core = function core(obj) { return instanceStr(obj) === 'core'; }; var stylesheet = function stylesheet(obj) { return instanceStr(obj) === 'stylesheet'; }; var event = function event(obj) { return instanceStr(obj) === 'event'; }; var emptyString = function emptyString(obj) { if (obj === undefined || obj === null) { // null is empty return true; } else if (obj === '' || obj.match(/^\s+$/)) { return true; // empty string is empty } return false; // otherwise, we don't know what we've got }; var domElement = function domElement(obj) { if (typeof HTMLElement === 'undefined') { return false; // we're not in a browser so it doesn't matter } else { return obj instanceof HTMLElement; } }; var boundingBox = function boundingBox(obj) { return plainObject(obj) && number$1(obj.x1) && number$1(obj.x2) && number$1(obj.y1) && number$1(obj.y2); }; var promise = function promise(obj) { return object(obj) && fn$6(obj.then); }; var ms = function ms() { return navigator && navigator.userAgent.match(/msie|trident|edge/i); }; // probably a better way to detect this... var memoize = function memoize(fn, keyFn) { if (!keyFn) { keyFn = function keyFn() { if (arguments.length === 1) { return arguments[0]; } else if (arguments.length === 0) { return 'undefined'; } var args = []; for (var i = 0; i < arguments.length; i++) { args.push(arguments[i]); } return args.join('$'); }; } var memoizedFn = function memoizedFn() { var self = this; var args = arguments; var ret; var k = keyFn.apply(self, args); var cache = memoizedFn.cache; if (!(ret = cache[k])) { ret = cache[k] = fn.apply(self, args); } return ret; }; memoizedFn.cache = {}; return memoizedFn; }; var camel2dash = memoize(function (str) { return str.replace(/([A-Z])/g, function (v) { return '-' + v.toLowerCase(); }); }); var dash2camel = memoize(function (str) { return str.replace(/(-\w)/g, function (v) { return v[1].toUpperCase(); }); }); var prependCamel = memoize(function (prefix, str) { return prefix + str[0].toUpperCase() + str.substring(1); }, function (prefix, str) { return prefix + '$' + str; }); var capitalize = function capitalize(str) { if (emptyString(str)) { return str; } return str.charAt(0).toUpperCase() + str.substring(1); }; var number = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))'; var rgba = 'rgb[a]?\\((' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)\\s*,\\s*(' + number + '[%]?)(?:\\s*,\\s*(' + number + '))?\\)'; var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)\\s*,\\s*(?:' + number + '[%]?)(?:\\s*,\\s*(?:' + number + '))?\\)'; var hsla = 'hsl[a]?\\((' + number + ')\\s*,\\s*(' + number + '[%])\\s*,\\s*(' + number + '[%])(?:\\s*,\\s*(' + number + '))?\\)'; var hslaNoBackRefs = 'hsl[a]?\\((?:' + number + ')\\s*,\\s*(?:' + number + '[%])\\s*,\\s*(?:' + number + '[%])(?:\\s*,\\s*(?:' + number + '))?\\)'; var hex3 = '\\#[0-9a-fA-F]{3}'; var hex6 = '\\#[0-9a-fA-F]{6}'; var ascending = function ascending(a, b) { if (a < b) { return -1; } else if (a > b) { return 1; } else { return 0; } }; var descending = function descending(a, b) { return -1 * ascending(a, b); }; var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) { var args = arguments; for (var i = 1; i < args.length; i++) { var obj = args[i]; if (obj == null) { continue; } var keys = Object.keys(obj); for (var j = 0; j < keys.length; j++) { var k = keys[j]; tgt[k] = obj[k]; } } return tgt; }; var hex2tuple = function hex2tuple(hex) { if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') { return; } var shortHex = hex.length === 4; var r, g, b; var base = 16; if (shortHex) { r = parseInt(hex[1] + hex[1], base); g = parseInt(hex[2] + hex[2], base); b = parseInt(hex[3] + hex[3], base); } else { r = parseInt(hex[1] + hex[2], base); g = parseInt(hex[3] + hex[4], base); b = parseInt(hex[5] + hex[6], base); } return [r, g, b]; }; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0) var hsl2tuple = function hsl2tuple(hsl) { var ret; var h, s, l, a, r, g, b; function hue2rgb(p, q, t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } var m = new RegExp('^' + hsla + '$').exec(hsl); if (m) { // get hue h = parseInt(m[1]); if (h < 0) { h = (360 - -1 * h % 360) % 360; } else if (h > 360) { h = h % 360; } h /= 360; // normalise on [0, 1] s = parseFloat(m[2]); if (s < 0 || s > 100) { return; } // saturation is [0, 100] s = s / 100; // normalise on [0, 1] l = parseFloat(m[3]); if (l < 0 || l > 100) { return; } // lightness is [0, 100] l = l / 100; // normalise on [0, 1] a = m[4]; if (a !== undefined) { a = parseFloat(a); if (a < 0 || a > 1) { return; } // alpha is [0, 1] } // now, convert to rgb // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript if (s === 0) { r = g = b = Math.round(l * 255); // achromatic } else { var q = l < 0.5 ? l * (1 + s) : l + s - l * s; var p = 2 * l - q; r = Math.round(255 * hue2rgb(p, q, h + 1 / 3)); g = Math.round(255 * hue2rgb(p, q, h)); b = Math.round(255 * hue2rgb(p, q, h - 1 / 3)); } ret = [r, g, b, a]; } return ret; }; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0) var rgb2tuple = function rgb2tuple(rgb) { var ret; var m = new RegExp('^' + rgba + '$').exec(rgb); if (m) { ret = []; var isPct = []; for (var i = 1; i <= 3; i++) { var channel = m[i]; if (channel[channel.length - 1] === '%') { isPct[i] = true; } channel = parseFloat(channel); if (isPct[i]) { channel = channel / 100 * 255; // normalise to [0, 255] } if (channel < 0 || channel > 255) { return; } // invalid channel value ret.push(Math.floor(channel)); } var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3]; var allArePct = isPct[1] && isPct[2] && isPct[3]; if (atLeastOneIsPct && !allArePct) { return; } // must all be percent values if one is var alpha = m[4]; if (alpha !== undefined) { alpha = parseFloat(alpha); if (alpha < 0 || alpha > 1) { return; } // invalid alpha value ret.push(alpha); } } return ret; }; var colorname2tuple = function colorname2tuple(color) { return colors[color.toLowerCase()]; }; var color2tuple = function color2tuple(color) { return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color); }; var colors = { // special colour names transparent: [0, 0, 0, 0], // NB alpha === 0 // regular colours aliceblue: [240, 248, 255], antiquewhite: [250, 235, 215], aqua: [0, 255, 255], aquamarine: [127, 255, 212], azure: [240, 255, 255], beige: [245, 245, 220], bisque: [255, 228, 196], black: [0, 0, 0], blanchedalmond: [255, 235, 205], blue: [0, 0, 255], blueviolet: [138, 43, 226], brown: [165, 42, 42], burlywood: [222, 184, 135], cadetblue: [95, 158, 160], chartreuse: [127, 255, 0], chocolate: [210, 105, 30], coral: [255, 127, 80], cornflowerblue: [100, 149, 237], cornsilk: [255, 248, 220], crimson: [220, 20, 60], cyan: [0, 255, 255], darkblue: [0, 0, 139], darkcyan: [0, 139, 139], darkgoldenrod: [184, 134, 11], darkgray: [169, 169, 169], darkgreen: [0, 100, 0], darkgrey: [169, 169, 169], darkkhaki: [189, 183, 107], darkmagenta: [139, 0, 139], darkolivegreen: [85, 107, 47], darkorange: [255, 140, 0], darkorchid: [153, 50, 204], darkred: [139, 0, 0], darksalmon: [233, 150, 122], darkseagreen: [143, 188, 143], darkslateblue: [72, 61, 139], darkslategray: [47, 79, 79], darkslategrey: [47, 79, 79], darkturquoise: [0, 206, 209], darkviolet: [148, 0, 211], deeppink: [255, 20, 147], deepskyblue: [0, 191, 255], dimgray: [105, 105, 105], dimgrey: [105, 105, 105], dodgerblue: [30, 144, 255], firebrick: [178, 34, 34], floralwhite: [255, 250, 240], forestgreen: [34, 139, 34], fuchsia: [255, 0, 255], gainsboro: [220, 220, 220], ghostwhite: [248, 248, 255], gold: [255, 215, 0], goldenrod: [218, 165, 32], gray: [128, 128, 128], grey: [128, 128, 128], green: [0, 128, 0], greenyellow: [173, 255, 47], honeydew: [240, 255, 240], hotpink: [255, 105, 180], indianred: [205, 92, 92], indigo: [75, 0, 130], ivory: [255, 255, 240], khaki: [240, 230, 140], lavender: [230, 230, 250], lavenderblush: [255, 240, 245], lawngreen: [124, 252, 0], lemonchiffon: [255, 250, 205], lightblue: [173, 216, 230], lightcoral: [240, 128, 128], lightcyan: [224, 255, 255], lightgoldenrodyellow: [250, 250, 210], lightgray: [211, 211, 211], lightgreen: [144, 238, 144], lightgrey: [211, 211, 211], lightpink: [255, 182, 193], lightsalmon: [255, 160, 122], lightseagreen: [32, 178, 170], lightskyblue: [135, 206, 250], lightslategray: [119, 136, 153], lightslategrey: [119, 136, 153], lightsteelblue: [176, 196, 222], lightyellow: [255, 255, 224], lime: [0, 255, 0], limegreen: [50, 205, 50], linen: [250, 240, 230], magenta: [255, 0, 255], maroon: [128, 0, 0], mediumaquamarine: [102, 205, 170], mediumblue: [0, 0, 205], mediumorchid: [186, 85, 211], mediumpurple: [147, 112, 219], mediumseagreen: [60, 179, 113], mediumslateblue: [123, 104, 238], mediumspringgreen: [0, 250, 154], mediumturquoise: [72, 209, 204], mediumvioletred: [199, 21, 133], midnightblue: [25, 25, 112], mintcream: [245, 255, 250], mistyrose: [255, 228, 225], moccasin: [255, 228, 181], navajowhite: [255, 222, 173], navy: [0, 0, 128], oldlace: [253, 245, 230], olive: [128, 128, 0], olivedrab: [107, 142, 35], orange: [255, 165, 0], orangered: [255, 69, 0], orchid: [218, 112, 214], palegoldenrod: [238, 232, 170], palegreen: [152, 251, 152], paleturquoise: [175, 238, 238], palevioletred: [219, 112, 147], papayawhip: [255, 239, 213], peachpuff: [255, 218, 185], peru: [205, 133, 63], pink: [255, 192, 203], plum: [221, 160, 221], powderblue: [176, 224, 230], purple: [128, 0, 128], red: [255, 0, 0], rosybrown: [188, 143, 143], royalblue: [65, 105, 225], saddlebrown: [139, 69, 19], salmon: [250, 128, 114], sandybrown: [244, 164, 96], seagreen: [46, 139, 87], seashell: [255, 245, 238], sienna: [160, 82, 45], silver: [192, 192, 192], skyblue: [135, 206, 235], slateblue: [106, 90, 205], slategray: [112, 128, 144], slategrey: [112, 128, 144], snow: [255, 250, 250], springgreen: [0, 255, 127], steelblue: [70, 130, 180], tan: [210, 180, 140], teal: [0, 128, 128], thistle: [216, 191, 216], tomato: [255, 99, 71], turquoise: [64, 224, 208], violet: [238, 130, 238], wheat: [245, 222, 179], white: [255, 255, 255], whitesmoke: [245, 245, 245], yellow: [255, 255, 0], yellowgreen: [154, 205, 50] }; var setMap = function setMap(options) { var obj = options.map; var keys = options.keys; var l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; if (plainObject(key)) { throw Error('Tried to set map with object key'); } if (i < keys.length - 1) { // extend the map if necessary if (obj[key] == null) { obj[key] = {}; } obj = obj[key]; } else { // set the value obj[key] = options.value; } } }; // gets the value in a map even if it's not built in places var getMap = function getMap(options) { var obj = options.map; var keys = options.keys; var l = keys.length; for (var i = 0; i < l; i++) { var key = keys[i]; if (plainObject(key)) { throw Error('Tried to get map with object key'); } obj = obj[key]; if (obj == null) { return obj; } } return obj; }; // deletes the entry in the map var performance = window$1 ? window$1.performance : null; var pnow = performance && performance.now ? function () { return performance.now(); } : function () { return Date.now(); }; var raf = function () { if (window$1) { if (window$1.requestAnimationFrame) { return function (fn) { window$1.requestAnimationFrame(fn); }; } else if (window$1.mozRequestAnimationFrame) { return function (fn) { window$1.mozRequestAnimationFrame(fn); }; } else if (window$1.webkitRequestAnimationFrame) { return function (fn) { window$1.webkitRequestAnimationFrame(fn); }; } else if (window$1.msRequestAnimationFrame) { return function (fn) { window$1.msRequestAnimationFrame(fn); }; } } return function (fn) { if (fn) { setTimeout(function () { fn(pnow()); }, 1000 / 60); } }; }(); var requestAnimationFrame = function requestAnimationFrame(fn) { return raf(fn); }; var performanceNow = pnow; var DEFAULT_HASH_SEED = 9261; var K = 65599; // 37 also works pretty well var DEFAULT_HASH_SEED_ALT = 5381; var hashIterableInts = function hashIterableInts(iterator) { var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED; // sdbm/string-hash var hash = seed; var entry; for (;;) { entry = iterator.next(); if (entry.done) { break; } hash = hash * K + entry.value | 0; } return hash; }; var hashInt = function hashInt(num) { var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED; // sdbm/string-hash return seed * K + num | 0; }; var hashIntAlt = function hashIntAlt(num) { var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT; // djb2/string-hash return (seed << 5) + seed + num | 0; }; var combineHashes = function combineHashes(hash1, hash2) { return hash1 * 0x200000 + hash2; }; var combineHashesArray = function combineHashesArray(hashes) { return hashes[0] * 0x200000 + hashes[1]; }; var hashArrays = function hashArrays(hashes1, hashes2) { return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])]; }; var hashIntsArray = function hashIntsArray(ints, seed) { var entry = { value: 0, done: false }; var i = 0; var length = ints.length; var iterator = { next: function next() { if (i < length) { entry.value = ints[i++]; } else { entry.done = true; } return entry; } }; return hashIterableInts(iterator, seed); }; var hashString = function hashString(str, seed) { var entry = { value: 0, done: false }; var i = 0; var length = str.length; var iterator = { next: function next() { if (i < length) { entry.value = str.charCodeAt(i++); } else { entry.done = true; } return entry; } }; return hashIterableInts(iterator, seed); }; var hashStrings = function hashStrings() { return hashStringsArray(arguments); }; var hashStringsArray = function hashStringsArray(strs) { var hash; for (var i = 0; i < strs.length; i++) { var str = strs[i]; if (i === 0) { hash = hashString(str); } else { hash = hashString(str, hash); } } return hash; }; /*global console */ var warningsEnabled = true; var warnSupported = console.warn != null; // eslint-disable-line no-console var traceSupported = console.trace != null; // eslint-disable-line no-console var MAX_INT$1 = Number.MAX_SAFE_INTEGER || 9007199254740991; var trueify = function trueify() { return true; }; var falsify = function falsify() { return false; }; var zeroify = function zeroify() { return 0; }; var noop$1 = function noop() {}; var error = function error(msg) { throw new Error(msg); }; var warnings = function warnings(enabled) { if (enabled !== undefined) { warningsEnabled = !!enabled; } else { return warningsEnabled; } }; var warn = function warn(msg) { /* eslint-disable no-console */ if (!warnings()) { return; } if (warnSupported) { console.warn(msg); } else { console.log(msg); if (traceSupported) { console.trace(); } } }; /* eslint-enable */ var clone = function clone(obj) { return extend({}, obj); }; // gets a shallow copy of the argument var copy = function copy(obj) { if (obj == null) { return obj; } if (array(obj)) { return obj.slice(); } else if (plainObject(obj)) { return clone(obj); } else { return obj; } }; var copyArray = function copyArray(arr) { return arr.slice(); }; var uuid = function uuid(a, b /* placeholders */ ) { for ( // loop :) b = a = ''; // b - result , a - numeric letiable a++ < 36; // b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24 ? // return a random number or 4 (a ^ 15 // if "a" is not 15 ? // generate a random number from 0 to 15 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11 : 4 // otherwise 4 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-" ) { } return b; }; var _staticEmptyObject = {}; var staticEmptyObject = function staticEmptyObject() { return _staticEmptyObject; }; var defaults$g = function defaults(_defaults) { var keys = Object.keys(_defaults); return function (opts) { var filledOpts = {}; for (var i = 0; i < keys.length; i++) { var key = keys[i]; var optVal = opts == null ? undefined : opts[key]; filledOpts[key] = optVal === undefined ? _defaults[key] : optVal; } return filledOpts; }; }; var removeFromArray = function removeFromArray(arr, ele, oneCopy) { for (var i = arr.length - 1; i >= 0; i--) { if (arr[i] === ele) { arr.splice(i, 1); if (oneCopy) { break; } } } }; var clearArray = function clearArray(arr) { arr.splice(0, arr.length); }; var push = function push(arr, otherArr) { for (var i = 0; i < otherArr.length; i++) { var el = otherArr[i]; arr.push(el); } }; var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) { if (prefix) { propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth } return obj[propName]; }; var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) { if (prefix) { propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth } obj[propName] = value; }; /* global Map */ var ObjectMap = /*#__PURE__*/function () { function ObjectMap() { _classCallCheck(this, ObjectMap); this._obj = {}; } _createClass(ObjectMap, [{ key: "set", value: function set(key, val) { this._obj[key] = val; return this; } }, { key: "delete", value: function _delete(key) { this._obj[key] = undefined; return this; } }, { key: "clear", value: function clear() { this._obj = {}; } }, { key: "has", value: function has(key) { return this._obj[key] !== undefined; } }, { key: "get", value: function get(key) { return this._obj[key]; } }]); return ObjectMap; }(); var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap; /* global Set */ var undef = "undefined" ; var ObjectSet = /*#__PURE__*/function () { function ObjectSet(arrayOrObjectSet) { _classCallCheck(this, ObjectSet); this._obj = Object.create(null); this.size = 0; if (arrayOrObjectSet != null) { var arr; if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) { arr = arrayOrObjectSet.toArray(); } else { arr = arrayOrObjectSet; } for (var i = 0; i < arr.length; i++) { this.add(arr[i]); } } } _createClass(ObjectSet, [{ key: "instanceString", value: function instanceString() { return 'set'; } }, { key: "add", value: function add(val) { var o = this._obj; if (o[val] !== 1) { o[val] = 1; this.size++; } } }, { key: "delete", value: function _delete(val) { var o = this._obj; if (o[val] === 1) { o[val] = 0; this.size--; } } }, { key: "clear", value: function clear() { this._obj = Object.create(null); } }, { key: "has", value: function has(val) { return this._obj[val] === 1; } }, { key: "toArray", value: function toArray() { var _this = this; return Object.keys(this._obj).filter(function (key) { return _this.has(key); }); } }, { key: "forEach", value: function forEach(callback, thisArg) { return this.toArray().forEach(callback, thisArg); } }]); return ObjectSet; }(); var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet; var Element = function Element(cy, params) { var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; if (cy === undefined || params === undefined || !core(cy)) { error('An element must have a core reference and parameters set'); return; } var group = params.group; // try to automatically infer the group if unspecified if (group == null) { if (params.data && params.data.source != null && params.data.target != null) { group = 'edges'; } else { group = 'nodes'; } } // validate group if (group !== 'nodes' && group !== 'edges') { error('An element must be of type `nodes` or `edges`; you specified `' + group + '`'); return; } // make the element array-like, just like a collection this.length = 1; this[0] = this; // NOTE: when something is added here, add also to ele.json() var _p = this._private = { cy: cy, single: true, // indicates this is an element data: params.data || {}, // data object position: params.position || { x: 0, y: 0 }, // (x, y) position pair autoWidth: undefined, // width and height of nodes calculated by the renderer when set to special 'auto' value autoHeight: undefined, autoPadding: undefined, compoundBoundsClean: false, // whether the compound dimensions need to be recalculated the next time dimensions are read listeners: [], // array of bound listeners group: group, // string; 'nodes' or 'edges' style: {}, // properties as set by the style rstyle: {}, // properties for style sent from the renderer to the core styleCxts: [], // applied style contexts from the styler styleKeys: {}, // per-group keys of style property values removed: true, // whether it's inside the vis; true if removed (set true here since we call restore) selected: params.selected ? true : false, // whether it's selected selectable: params.selectable === undefined ? true : params.selectable ? true : false, // whether it's selectable locked: params.locked ? true : false, // whether the element is locked (cannot be moved) grabbed: false, // whether the element is grabbed by the mouse; renderer sets this privately grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false, // whether the element can be grabbed pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false, // whether the element has passthrough panning enabled active: false, // whether the element is active from user interaction classes: new Set$1(), // map ( className => true ) animation: { // object for currently-running animations current: [], queue: [] }, rscratch: {}, // object in which the renderer can store information scratch: params.scratch || {}, // scratch objects edges: [], // array of connected edges children: [], // array of children parent: params.parent && params.parent.isNode() ? params.parent : null, // parent ref traversalCache: {}, // cache of output of traversal functions backgrounding: false, // whether background images are loading bbCache: null, // cache of the current bounding box bbCacheShift: { x: 0, y: 0 }, // shift applied to cached bb to be applied on next get bodyBounds: null, // bounds cache of element body, w/o overlay overlayBounds: null, // bounds cache of element body, including overlay labelBounds: { // bounds cache of labels all: null, source: null, target: null, main: null }, arrowBounds: { // bounds cache of edge arrows source: null, target: null, 'mid-source': null, 'mid-target': null } }; if (_p.position.x == null) { _p.position.x = 0; } if (_p.position.y == null) { _p.position.y = 0; } // renderedPosition overrides if specified if (params.renderedPosition) { var rpos = params.renderedPosition; var pan = cy.pan(); var zoom = cy.zoom(); _p.position = { x: (rpos.x - pan.x) / zoom, y: (rpos.y - pan.y) / zoom }; } var classes = []; if (array(params.classes)) { classes = params.classes; } else if (string(params.classes)) { classes = params.classes.split(/\s+/); } for (var i = 0, l = classes.length; i < l; i++) { var cls = classes[i]; if (!cls || cls === '') { continue; } _p.classes.add(cls); } this.createEmitter(); var bypass = params.style || params.css; if (bypass) { warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.'); this.style(bypass); } if (restore === undefined || restore) { this.restore(); } }; var defineSearch = function defineSearch(params) { params = { bfs: params.bfs || !params.dfs, dfs: params.dfs || !params.bfs }; // from pseudocode on wikipedia return function searchFn(roots, fn, directed) { var options; if (plainObject(roots) && !elementOrCollection(roots)) { options = roots; roots = options.roots || options.root; fn = options.visit; directed = options.directed; } directed = arguments.length === 2 && !fn$6(fn) ? fn : directed; fn = fn$6(fn) ? fn : function () {}; var cy = this._private.cy; var v = roots = string(roots) ? this.filter(roots) : roots; var Q = []; var connectedNodes = []; var connectedBy = {}; var id2depth = {}; var V = {}; var j = 0; var found; var _this$byGroup = this.byGroup(), nodes = _this$byGroup.nodes, edges = _this$byGroup.edges; // enqueue v for (var i = 0; i < v.length; i++) { var vi = v[i]; var viId = vi.id(); if (vi.isNode()) { Q.unshift(vi); if (params.bfs) { V[viId] = true; connectedNodes.push(vi); } id2depth[viId] = 0; } } var _loop = function _loop() { var v = params.bfs ? Q.shift() : Q.pop(); var vId = v.id(); if (params.dfs) { if (V[vId]) { return "continue"; } V[vId] = true; connectedNodes.push(v); } var depth = id2depth[vId]; var prevEdge = connectedBy[vId]; var src = prevEdge != null ? prevEdge.source() : null; var tgt = prevEdge != null ? prevEdge.target() : null; var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0]; var ret = void 0; ret = fn(v, prevEdge, prevNode, j++, depth); if (ret === true) { found = v; return "break"; } if (ret === false) { return "break"; } var vwEdges = v.connectedEdges().filter(function (e) { return (!directed || e.source().same(v)) && edges.has(e); }); for (var _i2 = 0; _i2 < vwEdges.length; _i2++) { var e = vwEdges[_i2]; var w = e.connectedNodes().filter(function (n) { return !n.same(v) && nodes.has(n); }); var wId = w.id(); if (w.length !== 0 && !V[wId]) { w = w[0]; Q.push(w); if (params.bfs) { V[wId] = true; connectedNodes.push(w); } connectedBy[wId] = e; id2depth[wId] = id2depth[vId] + 1; } } }; while (Q.length !== 0) { var _ret = _loop(); if (_ret === "continue") continue; if (_ret === "break") break; } var connectedEles = cy.collection(); for (var _i = 0; _i < connectedNodes.length; _i++) { var node = connectedNodes[_i]; var edge = connectedBy[node.id()]; if (edge != null) { connectedEles.push(edge); } connectedEles.push(node); } return { path: cy.collection(connectedEles), found: cy.collection(found) }; }; }; // search, spanning trees, etc var elesfn$v = { breadthFirstSearch: defineSearch({ bfs: true }), depthFirstSearch: defineSearch({ dfs: true }) }; // nice, short mathematical alias elesfn$v.bfs = elesfn$v.breadthFirstSearch; elesfn$v.dfs = elesfn$v.depthFirstSearch; var dijkstraDefaults = defaults$g({ root: null, weight: function weight(edge) { return 1; }, directed: false }); var elesfn$u = { dijkstra: function dijkstra(options) { if (!plainObject(options)) { var args = arguments; options = { root: args[0], weight: args[1], directed: args[2] }; } var _dijkstraDefaults = dijkstraDefaults(options), root = _dijkstraDefaults.root, weight = _dijkstraDefaults.weight, directed = _dijkstraDefaults.directed; var eles = this; var weightFn = weight; var source = string(root) ? this.filter(root)[0] : root[0]; var dist = {}; var prev = {}; var knownDist = {}; var _this$byGroup = this.byGroup(), nodes = _this$byGroup.nodes, edges = _this$byGroup.edges; edges.unmergeBy(function (ele) { return ele.isLoop(); }); var getDist = function getDist(node) { return dist[node.id()]; }; var setDist = function setDist(node, d) { dist[node.id()] = d; Q.updateItem(node); }; var Q = new Heap(function (a, b) { return getDist(a) - getDist(b); }); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; dist[node.id()] = node.same(source) ? 0 : Infinity; Q.push(node); } var distBetween = function distBetween(u, v) { var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges); var smallestDistance = Infinity; var smallestEdge; for (var _i = 0; _i < uvs.length; _i++) { var edge = uvs[_i]; var _weight = weightFn(edge); if (_weight < smallestDistance || !smallestEdge) { smallestDistance = _weight; smallestEdge = edge; } } return { edge: smallestEdge, dist: smallestDistance }; }; while (Q.size() > 0) { var u = Q.pop(); var smalletsDist = getDist(u); var uid = u.id(); knownDist[uid] = smalletsDist; if (smalletsDist === Infinity) { continue; } var neighbors = u.neighborhood().intersect(nodes); for (var _i2 = 0; _i2 < neighbors.length; _i2++) { var v = neighbors[_i2]; var vid = v.id(); var vDist = distBetween(u, v); var alt = smalletsDist + vDist.dist; if (alt < getDist(v)) { setDist(v, alt); prev[vid] = { node: u, edge: vDist.edge }; } } // for } // while return { distanceTo: function distanceTo(node) { var target = string(node) ? nodes.filter(node)[0] : node[0]; return knownDist[target.id()]; }, pathTo: function pathTo(node) { var target = string(node) ? nodes.filter(node)[0] : node[0]; var S = []; var u = target; var uid = u.id(); if (target.length > 0) { S.unshift(target); while (prev[uid]) { var p = prev[uid]; S.unshift(p.edge); S.unshift(p.node); u = p.node; uid = u.id(); } } return eles.spawn(S); } }; } }; var elesfn$t = { // kruskal's algorithm (finds min spanning tree, assuming undirected graph) // implemented from pseudocode from wikipedia kruskal: function kruskal(weightFn) { weightFn = weightFn || function (edge) { return 1; }; var _this$byGroup = this.byGroup(), nodes = _this$byGroup.nodes, edges = _this$byGroup.edges; var numNodes = nodes.length; var forest = new Array(numNodes); var A = nodes; // assumes byGroup() creates new collections that can be safely mutated var findSetIndex = function findSetIndex(ele) { for (var i = 0; i < forest.length; i++) { var eles = forest[i]; if (eles.has(ele)) { return i; } } }; // start with one forest per node for (var i = 0; i < numNodes; i++) { forest[i] = this.spawn(nodes[i]); } var S = edges.sort(function (a, b) { return weightFn(a) - weightFn(b); }); for (var _i = 0; _i < S.length; _i++) { var edge = S[_i]; var u = edge.source()[0]; var v = edge.target()[0]; var setUIndex = findSetIndex(u); var setVIndex = findSetIndex(v); var setU = forest[setUIndex]; var setV = forest[setVIndex]; if (setUIndex !== setVIndex) { A.merge(edge); // combine forests for u and v setU.merge(setV); forest.splice(setVIndex, 1); } } return A; } }; var aStarDefaults = defaults$g({ root: null, goal: null, weight: function weight(edge) { return 1; }, heuristic: function heuristic(edge) { return 0; }, directed: false }); var elesfn$s = { // Implemented from pseudocode from wikipedia aStar: function aStar(options) { var cy = this.cy(); var _aStarDefaults = aStarDefaults(options), root = _aStarDefaults.root, goal = _aStarDefaults.goal, heuristic = _aStarDefaults.heuristic, directed = _aStarDefaults.directed, weight = _aStarDefaults.weight; root = cy.collection(root)[0]; goal = cy.collection(goal)[0]; var sid = root.id(); var tid = goal.id(); var gScore = {}; var fScore = {}; var closedSetIds = {}; var openSet = new Heap(function (a, b) { return fScore[a.id()] - fScore[b.id()]; }); var openSetIds = new Set$1(); var cameFrom = {}; var cameFromEdge = {}; var addToOpenSet = function addToOpenSet(ele, id) { openSet.push(ele); openSetIds.add(id); }; var cMin, cMinId; var popFromOpenSet = function popFromOpenSet() { cMin = openSet.pop(); cMinId = cMin.id(); openSetIds["delete"](cMinId); }; var isInOpenSet = function isInOpenSet(id) { return openSetIds.has(id); }; addToOpenSet(root, sid); gScore[sid] = 0; fScore[sid] = heuristic(root); // Counter var steps = 0; // Main loop while (openSet.size() > 0) { popFromOpenSet(); steps++; // If we've found our goal, then we are done if (cMinId === tid) { var path = []; var pathNode = goal; var pathNodeId = tid; var pathEdge = cameFromEdge[pathNodeId]; for (;;) { path.unshift(pathNode); if (pathEdge != null) { path.unshift(pathEdge); } pathNode = cameFrom[pathNodeId]; if (pathNode == null) { break; } pathNodeId = pathNode.id(); pathEdge = cameFromEdge[pathNodeId]; } return { found: true, distance: gScore[cMinId], path: this.spawn(path), steps: steps }; } // Add cMin to processed nodes closedSetIds[cMinId] = true; // Update scores for neighbors of cMin // Take into account if graph is directed or not var vwEdges = cMin._private.edges; for (var i = 0; i < vwEdges.length; i++) { var e = vwEdges[i]; // edge must be in set of calling eles if (!this.hasElementWithId(e.id())) { continue; } // cMin must be the source of edge if directed if (directed && e.data('source') !== cMinId) { continue; } var wSrc = e.source(); var wTgt = e.target(); var w = wSrc.id() !== cMinId ? wSrc : wTgt; var wid = w.id(); // node must be in set of calling eles if (!this.hasElementWithId(wid)) { continue; } // if node is in closedSet, ignore it if (closedSetIds[wid]) { continue; } // New tentative score for node w var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if: // w not present in openSet // OR // tentative gScore is less than previous value // w not in openSet if (!isInOpenSet(wid)) { gScore[wid] = tempScore; fScore[wid] = tempScore + heuristic(w); addToOpenSet(w, wid); cameFrom[wid] = cMin; cameFromEdge[wid] = e; continue; } // w already in openSet, but with greater gScore if (tempScore < gScore[wid]) { gScore[wid] = tempScore; fScore[wid] = tempScore + heuristic(w); cameFrom[wid] = cMin; cameFromEdge[wid] = e; } } // End of neighbors update } // End of main loop // If we've reached here, then we've not reached our goal return { found: false, distance: undefined, path: undefined, steps: steps }; } }; // elesfn var floydWarshallDefaults = defaults$g({ weight: function weight(edge) { return 1; }, directed: false }); var elesfn$r = { // Implemented from pseudocode from wikipedia floydWarshall: function floydWarshall(options) { var cy = this.cy(); var _floydWarshallDefault = floydWarshallDefaults(options), weight = _floydWarshallDefault.weight, directed = _floydWarshallDefault.directed; var weightFn = weight; var _this$byGroup = this.byGroup(), nodes = _this$byGroup.nodes, edges = _this$byGroup.edges; var N = nodes.length; var Nsq = N * N; var indexOf = function indexOf(node) { return nodes.indexOf(node); }; var atIndex = function atIndex(i) { return nodes[i]; }; // Initialize distance matrix var dist = new Array(Nsq); for (var n = 0; n < Nsq; n++) { var j = n % N; var i = (n - j) / N; if (i === j) { dist[n] = 0; } else { dist[n] = Infinity; } } // Initialize matrix used for path reconstruction // Initialize distance matrix var next = new Array(Nsq); var edgeNext = new Array(Nsq); // Process edges for (var _i = 0; _i < edges.length; _i++) { var edge = edges[_i]; var src = edge.source()[0]; var tgt = edge.target()[0]; if (src === tgt) { continue; } // exclude loops var s = indexOf(src); var t = indexOf(tgt); var st = s * N + t; // source to target index var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes if (dist[st] > _weight) { dist[st] = _weight; next[st] = t; edgeNext[st] = edge; } // If undirected graph, process 'reversed' edge if (!directed) { var ts = t * N + s; // target to source index if (!directed && dist[ts] > _weight) { dist[ts] = _weight; next[ts] = s; edgeNext[ts] = edge; } } } // Main loop for (var k = 0; k < N; k++) { for (var _i2 = 0; _i2 < N; _i2++) { var ik = _i2 * N + k; for (var _j = 0; _j < N; _j++) { var ij = _i2 * N + _j; var kj = k * N + _j; if (dist[ik] + dist[kj] < dist[ij]) { dist[ij] = dist[ik] + dist[kj]; next[ij] = next[ik]; } } } } var getArgEle = function getArgEle(ele) { return (string(ele) ? cy.filter(ele) : ele)[0]; }; var indexOfArgEle = function indexOfArgEle(ele) { return indexOf(getArgEle(ele)); }; var res = { distance: function distance(from, to) { var i = indexOfArgEle(from); var j = indexOfArgEle(to); return dist[i * N + j]; }, path: function path(from, to) { var i = indexOfArgEle(from); var j = indexOfArgEle(to); var fromNode = atIndex(i); if (i === j) { return fromNode.collection(); } if (next[i * N + j] == null) { return cy.collection(); } var path = cy.collection(); var prev = i; var edge; path.merge(fromNode); while (i !== j) { prev = i; i = next[i * N + j]; edge = edgeNext[prev * N + i]; path.merge(edge); path.merge(atIndex(i)); } return path; } }; return res; } // floydWarshall }; // elesfn var bellmanFordDefaults = defaults$g({ weight: function weight(edge) { return 1; }, directed: false, root: null }); var elesfn$q = { // Implemented from pseudocode from wikipedia bellmanFord: function bellmanFord(options) { var _this = this; var _bellmanFordDefaults = bellmanFordDefaults(options), weight = _bellmanFordDefaults.weight, directed = _bellmanFordDefaults.directed, root = _bellmanFordDefaults.root; var weightFn = weight; var eles = this; var cy = this.cy(); var _this$byGroup = this.byGroup(), edges = _this$byGroup.edges, nodes = _this$byGroup.nodes; var numNodes = nodes.length; var infoMap = new Map$1(); var hasNegativeWeightCycle = false; var negativeWeightCycles = []; root = cy.collection(root)[0]; // in case selector passed edges.unmergeBy(function (edge) { return edge.isLoop(); }); var numEdges = edges.length; var getInfo = function getInfo(node) { var obj = infoMap.get(node.id()); if (!obj) { obj = {}; infoMap.set(node.id(), obj); } return obj; }; var getNodeFromTo = function getNodeFromTo(to) { return (string(to) ? cy.$(to) : to)[0]; }; var distanceTo = function distanceTo(to) { return getInfo(getNodeFromTo(to)).dist; }; var pathTo = function pathTo(to) { var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root; var end = getNodeFromTo(to); var path = []; var node = end; for (;;) { if (node == null) { return _this.spawn(); } var _getInfo = getInfo(node), edge = _getInfo.edge, pred = _getInfo.pred; path.unshift(n